xref: /aosp_15_r20/system/apex/apexd/apex_file_repository.cpp (revision 33f3758387333dbd2962d7edbd98681940d895da)
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