1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "apex_file_repository.h"
18
19 #include <android-base/file.h>
20 #include <android-base/properties.h>
21 #include <android-base/result.h>
22 #include <android-base/stringprintf.h>
23 #include <android-base/strings.h>
24 #include <microdroid/metadata.h>
25
26 #include <cstdint>
27 #include <filesystem>
28 #include <unordered_map>
29
30 #include "apex_blocklist.h"
31 #include "apex_constants.h"
32 #include "apex_file.h"
33 #include "apexd_brand_new_verifier.h"
34 #include "apexd_utils.h"
35 #include "apexd_vendor_apex.h"
36 #include "apexd_verity.h"
37
38 using android::base::EndsWith;
39 using android::base::Error;
40 using android::base::GetProperty;
41 using android::base::Result;
42 using ::apex::proto::ApexBlocklist;
43
44 namespace android {
45 namespace apex {
46
ConsumeApexPackageSuffix(const std::string & path)47 std::string ConsumeApexPackageSuffix(const std::string& path) {
48 std::string_view path_view(path);
49 android::base::ConsumeSuffix(&path_view, kApexPackageSuffix);
50 android::base::ConsumeSuffix(&path_view, kCompressedApexPackageSuffix);
51 return std::string(path_view);
52 }
53
GetApexSelectFilenameFromProp(const std::vector<std::string> & prefixes,const std::string & apex_name)54 std::string GetApexSelectFilenameFromProp(
55 const std::vector<std::string>& prefixes, const std::string& apex_name) {
56 for (const std::string& prefix : prefixes) {
57 const std::string& filename = GetProperty(prefix + apex_name, "");
58 if (filename != "") {
59 return ConsumeApexPackageSuffix(filename);
60 }
61 }
62 return "";
63 }
64
ScanBuiltInDir(const std::string & dir,ApexPartition partition)65 Result<void> ApexFileRepository::ScanBuiltInDir(const std::string& dir,
66 ApexPartition partition) {
67 LOG(INFO) << "Scanning " << dir << " for pre-installed ApexFiles";
68 if (access(dir.c_str(), F_OK) != 0 && errno == ENOENT) {
69 LOG(WARNING) << dir << " does not exist. Skipping";
70 return {};
71 }
72
73 Result<std::vector<std::string>> all_apex_files = FindFilesBySuffix(
74 dir, {kApexPackageSuffix, kCompressedApexPackageSuffix});
75 if (!all_apex_files.ok()) {
76 return all_apex_files.error();
77 }
78
79 // TODO(b/179248390): scan parallelly if possible
80 for (const auto& file : *all_apex_files) {
81 LOG(INFO) << "Found pre-installed APEX " << file;
82 Result<ApexFile> apex_file = ApexFile::Open(file);
83 if (!apex_file.ok()) {
84 return Error() << "Failed to open " << file << " : " << apex_file.error();
85 }
86
87 const std::string& name = apex_file->GetManifest().name();
88
89 // Check if this APEX name is treated as a multi-install APEX.
90 //
91 // Note: apexd is a oneshot service which runs at boot, but can be restarted
92 // when needed (such as staging an APEX update). If a multi-install select
93 // property changes between boot and when apexd restarts, the LOG messages
94 // below will report the version that will be activated on next reboot,
95 // which may differ from the currently-active version.
96 std::string select_filename = GetApexSelectFilenameFromProp(
97 multi_install_select_prop_prefixes_, name);
98 if (!select_filename.empty()) {
99 std::string path;
100 if (!android::base::Realpath(apex_file->GetPath(), &path)) {
101 LOG(ERROR) << "Unable to resolve realpath of APEX with path "
102 << apex_file->GetPath();
103 continue;
104 }
105 if (enforce_multi_install_partition_ &&
106 partition != ApexPartition::Vendor &&
107 partition != ApexPartition::Odm) {
108 LOG(ERROR) << "Multi-install APEX " << path
109 << " can only be preinstalled on /{odm,vendor}/apex/.";
110 continue;
111 }
112
113 auto& keys = multi_install_public_keys_[name];
114 keys.insert(apex_file->GetBundledPublicKey());
115 if (keys.size() > 1) {
116 LOG(ERROR) << "Multi-install APEXes for " << name
117 << " have different public keys.";
118 // If any versions of a multi-installed APEX differ in public key,
119 // then no version should be installed.
120 if (auto it = pre_installed_store_.find(name);
121 it != pre_installed_store_.end()) {
122 pre_installed_store_.erase(it);
123 partition_store_.erase(name);
124 }
125 continue;
126 }
127
128 if (ConsumeApexPackageSuffix(android::base::Basename(path)) ==
129 select_filename) {
130 LOG(INFO) << "Found APEX at path " << path << " for multi-install APEX "
131 << name;
132 // Add the APEX file to the store if its filename matches the property.
133 pre_installed_store_.emplace(name, std::move(*apex_file));
134 partition_store_.emplace(name, partition);
135 } else {
136 LOG(INFO) << "Skipping APEX at path " << path
137 << " because it does not match expected multi-install"
138 << " APEX property for " << name;
139 }
140
141 continue;
142 }
143
144 auto it = pre_installed_store_.find(name);
145 if (it == pre_installed_store_.end()) {
146 pre_installed_store_.emplace(name, std::move(*apex_file));
147 partition_store_.emplace(name, partition);
148 } else if (it->second.GetPath() != apex_file->GetPath()) {
149 LOG(FATAL) << "Found two apex packages " << it->second.GetPath()
150 << " and " << apex_file->GetPath()
151 << " with the same module name " << name;
152 } else if (it->second.GetBundledPublicKey() !=
153 apex_file->GetBundledPublicKey()) {
154 LOG(FATAL) << "Public key of apex package " << it->second.GetPath()
155 << " (" << name << ") has unexpectedly changed";
156 }
157 }
158 multi_install_public_keys_.clear();
159 return {};
160 }
161
GetInstance()162 ApexFileRepository& ApexFileRepository::GetInstance() {
163 static ApexFileRepository instance;
164 return instance;
165 }
166
AddPreInstalledApex(const std::unordered_map<ApexPartition,std::string> & partition_to_prebuilt_dirs)167 android::base::Result<void> ApexFileRepository::AddPreInstalledApex(
168 const std::unordered_map<ApexPartition, std::string>&
169 partition_to_prebuilt_dirs) {
170 for (const auto& [partition, dir] : partition_to_prebuilt_dirs) {
171 if (auto result = ScanBuiltInDir(dir, partition); !result.ok()) {
172 return result.error();
173 }
174 }
175 return {};
176 }
177
AddBlockApex(const std::string & metadata_partition)178 Result<int> ApexFileRepository::AddBlockApex(
179 const std::string& metadata_partition) {
180 CHECK(!block_disk_path_.has_value())
181 << "AddBlockApex() can't be called twice.";
182
183 auto metadata_ready = WaitForFile(metadata_partition, kBlockApexWaitTime);
184 if (!metadata_ready.ok()) {
185 LOG(ERROR) << "Error waiting for metadata_partition : "
186 << metadata_ready.error();
187 return {};
188 }
189
190 // TODO(b/185069443) consider moving the logic to find disk_path from
191 // metadata_partition to its own library
192 LOG(INFO) << "Scanning " << metadata_partition << " for host apexes";
193 if (access(metadata_partition.c_str(), F_OK) != 0 && errno == ENOENT) {
194 LOG(WARNING) << metadata_partition << " does not exist. Skipping";
195 return {};
196 }
197
198 std::string metadata_realpath;
199 if (!android::base::Realpath(metadata_partition, &metadata_realpath)) {
200 LOG(WARNING) << "Can't get realpath of " << metadata_partition
201 << ". Skipping";
202 return {};
203 }
204
205 std::string_view metadata_path_view(metadata_realpath);
206 if (!android::base::ConsumeSuffix(&metadata_path_view, "1")) {
207 LOG(WARNING) << metadata_realpath << " is not a first partition. Skipping";
208 return {};
209 }
210
211 block_disk_path_ = std::string(metadata_path_view);
212
213 // Read the payload metadata.
214 // "metadata" can be overridden by microdroid_manager. To ensure that
215 // "microdroid" is started with the same/unmodified set of host APEXes,
216 // microdroid stores APEXes' pubkeys in its encrypted instance disk. Next
217 // time, microdroid checks if there's pubkeys in the instance disk and use
218 // them to activate APEXes. Microdroid_manager passes pubkeys in instance.img
219 // via the following file.
220 if (auto exists = PathExists("/apex/vm-payload-metadata");
221 exists.ok() && *exists) {
222 metadata_realpath = "/apex/vm-payload-metadata";
223 LOG(INFO) << "Overriding metadata to " << metadata_realpath;
224 }
225 auto metadata = android::microdroid::ReadMetadata(metadata_realpath);
226 if (!metadata.ok()) {
227 LOG(WARNING) << "Failed to load metadata from " << metadata_realpath
228 << ". Skipping: " << metadata.error();
229 return {};
230 }
231
232 int ret = 0;
233
234 // subsequent partitions are APEX archives.
235 static constexpr const int kFirstApexPartition = 2;
236 for (int i = 0; i < metadata->apexes_size(); i++) {
237 const auto& apex_config = metadata->apexes(i);
238
239 const std::string apex_path =
240 *block_disk_path_ + std::to_string(i + kFirstApexPartition);
241
242 auto apex_ready = WaitForFile(apex_path, kBlockApexWaitTime);
243 if (!apex_ready.ok()) {
244 return Error() << "Error waiting for apex file : " << apex_ready.error();
245 }
246
247 auto apex_file = ApexFile::Open(apex_path);
248 if (!apex_file.ok()) {
249 return Error() << "Failed to open " << apex_path << " : "
250 << apex_file.error();
251 }
252
253 // When metadata specifies the public key of the apex, it should match the
254 // bundled key. Otherwise we accept it.
255 if (apex_config.public_key() != "" &&
256 apex_config.public_key() != apex_file->GetBundledPublicKey()) {
257 return Error() << "public key doesn't match: " << apex_path;
258 }
259
260 const std::string& name = apex_file->GetManifest().name();
261
262 // When metadata specifies the manifest name and version of the apex, it
263 // should match what we see in the manifest.
264 if (apex_config.manifest_name() != "" &&
265 apex_config.manifest_name() != name) {
266 return Error() << "manifest name doesn't match: " << apex_path;
267 }
268
269 if (apex_config.manifest_version() != 0 &&
270 apex_config.manifest_version() != apex_file->GetManifest().version()) {
271 return Error() << "manifest version doesn't match: " << apex_path;
272 }
273
274 BlockApexOverride overrides;
275
276 // A block device doesn't have an inherent timestamp, so it is carried in
277 // the metadata.
278 if (int64_t last_update_seconds = apex_config.last_update_seconds();
279 last_update_seconds != 0) {
280 overrides.last_update_seconds = last_update_seconds;
281 }
282
283 // When metadata specifies the root digest of the apex, it should be used
284 // when activating the apex. So we need to keep it.
285 if (auto root_digest = apex_config.root_digest(); root_digest != "") {
286 overrides.block_apex_root_digest =
287 BytesToHex(reinterpret_cast<const uint8_t*>(root_digest.data()),
288 root_digest.size());
289 }
290
291 if (overrides.last_update_seconds.has_value() ||
292 overrides.block_apex_root_digest.has_value()) {
293 block_apex_overrides_.emplace(apex_path, std::move(overrides));
294 }
295
296 // Depending on whether the APEX was a factory version in the host or not,
297 // put it to different stores.
298 auto& store = apex_config.is_factory() ? pre_installed_store_ : data_store_;
299 // We want "uniqueness" in each store.
300 if (auto it = store.find(name); it != store.end()) {
301 return Error() << "duplicate of " << name << " found in "
302 << it->second.GetPath();
303 }
304 store.emplace(name, std::move(*apex_file));
305 // NOTE: We consider block APEXes are SYSTEM. APEX Config should be extended
306 // to support non-system block APEXes.
307 partition_store_.emplace(name, ApexPartition::System);
308
309 ret++;
310 }
311 return {ret};
312 }
313
314 // TODO(b/179497746): AddDataApex should not concern with filtering out invalid
315 // apex.
AddDataApex(const std::string & data_dir)316 Result<void> ApexFileRepository::AddDataApex(const std::string& data_dir) {
317 LOG(INFO) << "Scanning " << data_dir << " for data ApexFiles";
318 if (access(data_dir.c_str(), F_OK) != 0 && errno == ENOENT) {
319 LOG(WARNING) << data_dir << " does not exist. Skipping";
320 return {};
321 }
322
323 Result<std::vector<std::string>> active_apex =
324 FindFilesBySuffix(data_dir, {kApexPackageSuffix});
325 if (!active_apex.ok()) {
326 return active_apex.error();
327 }
328
329 // TODO(b/179248390): scan parallelly if possible
330 for (const auto& file : *active_apex) {
331 LOG(INFO) << "Found updated apex " << file;
332 Result<ApexFile> apex_file = ApexFile::Open(file);
333 if (!apex_file.ok()) {
334 LOG(ERROR) << "Failed to open " << file << " : " << apex_file.error();
335 continue;
336 }
337
338 const std::string& name = apex_file->GetManifest().name();
339 auto preinstalled = pre_installed_store_.find(name);
340 if (preinstalled != pre_installed_store_.end()) {
341 if (preinstalled->second.GetBundledPublicKey() !=
342 apex_file->GetBundledPublicKey()) {
343 // Ignore data apex if public key doesn't match with pre-installed apex
344 LOG(ERROR) << "Skipping " << file
345 << " : public key doesn't match pre-installed one";
346 continue;
347 }
348 } else if (ApexFileRepository::IsBrandNewApexEnabled()) {
349 auto verified_partition =
350 VerifyBrandNewPackageAgainstPreinstalled(*apex_file);
351 if (!verified_partition.ok()) {
352 LOG(ERROR) << "Skipping " << file << " : "
353 << verified_partition.error();
354 continue;
355 }
356 // Stores partition for already-verified brand-new APEX.
357 partition_store_.emplace(name, *verified_partition);
358 } else {
359 LOG(ERROR) << "Skipping " << file << " : no preinstalled apex";
360 // Ignore data apex without corresponding pre-installed apex
361 continue;
362 }
363
364 std::string select_filename = GetApexSelectFilenameFromProp(
365 multi_install_select_prop_prefixes_, name);
366 if (!select_filename.empty()) {
367 LOG(WARNING) << "APEX " << name << " is a multi-installed APEX."
368 << " Any updated version in /data will always overwrite"
369 << " the multi-installed preinstalled version, if possible.";
370 }
371
372 if (EndsWith(apex_file->GetPath(), kDecompressedApexPackageSuffix)) {
373 LOG(WARNING) << "Skipping " << file
374 << " : Non-decompressed APEX should not have "
375 << kDecompressedApexPackageSuffix << " suffix";
376 continue;
377 }
378
379 auto it = data_store_.find(name);
380 if (it == data_store_.end()) {
381 data_store_.emplace(name, std::move(*apex_file));
382 continue;
383 }
384
385 const auto& existing_version = it->second.GetManifest().version();
386 const auto new_version = apex_file->GetManifest().version();
387 // If multiple data apexs are preset, select the one with highest version
388 bool prioritize_higher_version = new_version > existing_version;
389 // For same version, non-decompressed apex gets priority
390 if (prioritize_higher_version) {
391 it->second = std::move(*apex_file);
392 }
393 }
394 return {};
395 }
396
AddBrandNewApexCredentialAndBlocklist(const std::unordered_map<ApexPartition,std::string> & partition_to_dir_map)397 Result<void> ApexFileRepository::AddBrandNewApexCredentialAndBlocklist(
398 const std::unordered_map<ApexPartition, std::string>&
399 partition_to_dir_map) {
400 for (const auto& [partition, dir] : partition_to_dir_map) {
401 LOG(INFO)
402 << "Scanning " << dir
403 << " for pre-installed public keys and blocklists of brand-new APEX";
404 if (access(dir.c_str(), F_OK) != 0 && errno == ENOENT) {
405 continue;
406 }
407
408 std::vector<std::string> all_credential_files =
409 OR_RETURN(FindFilesBySuffix(dir, {kBrandNewApexPublicKeySuffix}));
410 for (const std::string& credential_path : all_credential_files) {
411 std::string content;
412 CHECK(android::base::ReadFileToString(credential_path, &content));
413 const auto& [it, inserted] =
414 brand_new_apex_pubkeys_.emplace(content, partition);
415 CHECK(inserted || it->second == partition)
416 << "Duplicate public keys are found in different partitions.";
417 }
418
419 const std::string& blocklist_path =
420 std::filesystem::path(dir) / kBrandNewApexBlocklistFileName;
421 const auto blocklist_exists = OR_RETURN(PathExists(blocklist_path));
422 if (!blocklist_exists) {
423 continue;
424 }
425
426 std::unordered_map<std::string, int64_t> apex_name_to_version;
427 ApexBlocklist blocklist = OR_RETURN(ReadBlocklist(blocklist_path));
428 for (const auto& block_item : blocklist.blocked_apex()) {
429 const auto& [it, inserted] =
430 apex_name_to_version.emplace(block_item.name(), block_item.version());
431 CHECK(inserted) << "Duplicate APEX names are found in blocklist.";
432 }
433 brand_new_apex_blocked_version_.emplace(partition, apex_name_to_version);
434 }
435 return {};
436 }
437
GetPartition(const ApexFile & apex) const438 Result<ApexPartition> ApexFileRepository::GetPartition(
439 const ApexFile& apex) const {
440 const std::string& name = apex.GetManifest().name();
441 auto it = partition_store_.find(name);
442 if (it != partition_store_.end()) {
443 return it->second;
444 }
445
446 // Supports staged but not-yet-activated brand-new APEX.
447 if (!ApexFileRepository::IsBrandNewApexEnabled()) {
448 return Error() << "No preinstalled data found for package " << name;
449 }
450 return VerifyBrandNewPackageAgainstPreinstalled(apex);
451 }
452
453 // TODO(b/179497746): remove this method when we add api for fetching ApexFile
454 // by name
GetPublicKey(const std::string & name) const455 Result<const std::string> ApexFileRepository::GetPublicKey(
456 const std::string& name) const {
457 auto it = pre_installed_store_.find(name);
458 if (it == pre_installed_store_.end()) {
459 // Special casing for APEXes backed by block devices, i.e. APEXes in VM.
460 // Inside a VM, we fall back to find the key from data_store_. This is
461 // because an APEX is put to either pre_installed_store_ or data_store,
462 // depending on whether it was a factory APEX or not in the host.
463 it = data_store_.find(name);
464 if (it != data_store_.end() && IsBlockApex(it->second)) {
465 return it->second.GetBundledPublicKey();
466 }
467 return Error() << "No preinstalled apex found for package " << name;
468 }
469 return it->second.GetBundledPublicKey();
470 }
471
472 // TODO(b/179497746): remove this method when we add api for fetching ApexFile
473 // by name
GetPreinstalledPath(const std::string & name) const474 Result<const std::string> ApexFileRepository::GetPreinstalledPath(
475 const std::string& name) const {
476 auto it = pre_installed_store_.find(name);
477 if (it == pre_installed_store_.end()) {
478 return Error() << "No preinstalled data found for package " << name;
479 }
480 return it->second.GetPath();
481 }
482
483 // TODO(b/179497746): remove this method when we add api for fetching ApexFile
484 // by name
GetDataPath(const std::string & name) const485 Result<const std::string> ApexFileRepository::GetDataPath(
486 const std::string& name) const {
487 auto it = data_store_.find(name);
488 if (it == data_store_.end()) {
489 return Error() << "No data apex found for package " << name;
490 }
491 return it->second.GetPath();
492 }
493
GetBlockApexRootDigest(const std::string & path) const494 std::optional<std::string> ApexFileRepository::GetBlockApexRootDigest(
495 const std::string& path) const {
496 auto it = block_apex_overrides_.find(path);
497 if (it == block_apex_overrides_.end()) {
498 return std::nullopt;
499 }
500 return it->second.block_apex_root_digest;
501 }
502
GetBlockApexLastUpdateSeconds(const std::string & path) const503 std::optional<int64_t> ApexFileRepository::GetBlockApexLastUpdateSeconds(
504 const std::string& path) const {
505 auto it = block_apex_overrides_.find(path);
506 if (it == block_apex_overrides_.end()) {
507 return std::nullopt;
508 }
509 return it->second.last_update_seconds;
510 }
511
HasPreInstalledVersion(const std::string & name) const512 bool ApexFileRepository::HasPreInstalledVersion(const std::string& name) const {
513 return pre_installed_store_.find(name) != pre_installed_store_.end();
514 }
515
HasDataVersion(const std::string & name) const516 bool ApexFileRepository::HasDataVersion(const std::string& name) const {
517 return data_store_.find(name) != data_store_.end();
518 }
519
520 // ApexFile is considered a decompressed APEX if it is located in decompression
521 // dir
IsDecompressedApex(const ApexFile & apex) const522 bool ApexFileRepository::IsDecompressedApex(const ApexFile& apex) const {
523 return apex.GetPath().starts_with(decompression_dir_);
524 }
525
IsPreInstalledApex(const ApexFile & apex) const526 bool ApexFileRepository::IsPreInstalledApex(const ApexFile& apex) const {
527 auto it = pre_installed_store_.find(apex.GetManifest().name());
528 if (it == pre_installed_store_.end()) {
529 return false;
530 }
531 return it->second.GetPath() == apex.GetPath() || IsDecompressedApex(apex);
532 }
533
IsBlockApex(const ApexFile & apex) const534 bool ApexFileRepository::IsBlockApex(const ApexFile& apex) const {
535 return block_disk_path_.has_value() &&
536 apex.GetPath().starts_with(*block_disk_path_);
537 }
538
GetPreInstalledApexFiles() const539 std::vector<ApexFileRef> ApexFileRepository::GetPreInstalledApexFiles() const {
540 std::vector<ApexFileRef> result;
541 result.reserve(pre_installed_store_.size());
542 for (const auto& it : pre_installed_store_) {
543 result.emplace_back(std::cref(it.second));
544 }
545 return result;
546 }
547
GetDataApexFiles() const548 std::vector<ApexFileRef> ApexFileRepository::GetDataApexFiles() const {
549 std::vector<ApexFileRef> result;
550 result.reserve(data_store_.size());
551 for (const auto& it : data_store_) {
552 result.emplace_back(std::cref(it.second));
553 }
554 return result;
555 }
556
557 std::optional<ApexPartition>
GetBrandNewApexPublicKeyPartition(const std::string & public_key) const558 ApexFileRepository::GetBrandNewApexPublicKeyPartition(
559 const std::string& public_key) const {
560 auto it = brand_new_apex_pubkeys_.find(public_key);
561 if (it == brand_new_apex_pubkeys_.end()) {
562 return std::nullopt;
563 }
564 return it->second;
565 }
566
GetBrandNewApexBlockedVersion(ApexPartition partition,const std::string & apex_name) const567 std::optional<int64_t> ApexFileRepository::GetBrandNewApexBlockedVersion(
568 ApexPartition partition, const std::string& apex_name) const {
569 auto it = brand_new_apex_blocked_version_.find(partition);
570 if (it == brand_new_apex_blocked_version_.end()) {
571 return std::nullopt;
572 }
573 const auto& apex_name_to_version = it->second;
574 auto itt = apex_name_to_version.find(apex_name);
575 if (itt == apex_name_to_version.end()) {
576 return std::nullopt;
577 }
578 return itt->second;
579 }
580
581 // Group pre-installed APEX and data APEX by name
582 std::unordered_map<std::string, std::vector<ApexFileRef>>
AllApexFilesByName() const583 ApexFileRepository::AllApexFilesByName() const {
584 // Collect all apex files
585 std::vector<ApexFileRef> all_apex_files;
586 auto pre_installed_apexs = GetPreInstalledApexFiles();
587 auto data_apexs = GetDataApexFiles();
588 std::move(pre_installed_apexs.begin(), pre_installed_apexs.end(),
589 std::back_inserter(all_apex_files));
590 std::move(data_apexs.begin(), data_apexs.end(),
591 std::back_inserter(all_apex_files));
592
593 // Group them by name
594 std::unordered_map<std::string, std::vector<ApexFileRef>> result;
595 for (const auto& apex_file_ref : all_apex_files) {
596 const ApexFile& apex_file = apex_file_ref.get();
597 const std::string& package_name = apex_file.GetManifest().name();
598 if (result.find(package_name) == result.end()) {
599 result[package_name] = std::vector<ApexFileRef>{};
600 }
601 result[package_name].emplace_back(apex_file_ref);
602 }
603
604 return result;
605 }
606
GetDataApex(const std::string & name) const607 ApexFileRef ApexFileRepository::GetDataApex(const std::string& name) const {
608 auto it = data_store_.find(name);
609 CHECK(it != data_store_.end());
610 return std::cref(it->second);
611 }
612
GetPreInstalledApex(const std::string & name) const613 ApexFileRef ApexFileRepository::GetPreInstalledApex(
614 const std::string& name) const {
615 auto it = pre_installed_store_.find(name);
616 CHECK(it != pre_installed_store_.end());
617 return std::cref(it->second);
618 }
619
620 } // namespace apex
621 } // namespace android
622