xref: /aosp_15_r20/frameworks/base/libs/androidfw/AssetManager2.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
2  * Copyright (C) 2016 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 #define ATRACE_TAG ATRACE_TAG_RESOURCES
18 
19 #include "androidfw/AssetManager2.h"
20 
21 #include <algorithm>
22 #include <iterator>
23 #include <map>
24 #include <set>
25 #include <span>
26 #include <utility>
27 
28 #include "android-base/logging.h"
29 #include "android-base/stringprintf.h"
30 #include "androidfw/CombinedIterator.h"
31 #include "androidfw/ResourceTypes.h"
32 #include "androidfw/ResourceUtils.h"
33 #include "androidfw/Util.h"
34 #include "utils/ByteOrder.h"
35 #include "utils/Trace.h"
36 
37 #ifdef _WIN32
38 #ifdef ERROR
39 #undef ERROR
40 #endif
41 #endif
42 
43 namespace android {
44 
45 namespace {
46 
47 using EntryValue = std::variant<Res_value, incfs::verified_map_ptr<ResTable_map_entry>>;
48 
49 /* NOTE: table_entry has been verified in LoadedPackage::GetEntryFromOffset(),
50  * and so access to ->value() and ->map_entry() are safe here
51  */
GetEntryValue(incfs::verified_map_ptr<ResTable_entry> table_entry)52 base::expected<EntryValue, IOError> GetEntryValue(
53     incfs::verified_map_ptr<ResTable_entry> table_entry) {
54   const uint16_t entry_size = table_entry->size();
55 
56   // Check if the entry represents a bag value.
57   if (entry_size >= sizeof(ResTable_map_entry) && table_entry->is_complex()) {
58     return table_entry.convert<ResTable_map_entry>().verified();
59   }
60 
61   return table_entry->value();
62 }
63 
64 } // namespace
65 
66 struct FindEntryResult {
67   // The cookie representing the ApkAssets in which the value resides.
68   ApkAssetsCookie cookie;
69 
70   // The value of the resource table entry. Either an android::Res_value for non-bag types or an
71   // incfs::verified_map_ptr<ResTable_map_entry> for bag types.
72   EntryValue entry;
73 
74   // The configuration for which the resulting entry was defined. This is already swapped to host
75   // endianness.
76   ResTable_config config;
77 
78   // The bitmask of configuration axis with which the resource value varies.
79   uint32_t type_flags;
80 
81   // The dynamic package ID map for the package from which this resource came from.
82   const DynamicRefTable* dynamic_ref_table;
83 
84   // The package name of the resource.
85   const std::string* package_name;
86 
87   // The string pool reference to the type's name. This uses a different string pool than
88   // the global string pool, but this is hidden from the caller.
89   StringPoolRef type_string_ref;
90 
91   // The string pool reference to the entry's name. This uses a different string pool than
92   // the global string pool, but this is hidden from the caller.
93   StringPoolRef entry_string_ref;
94 };
95 
96 struct Theme::Entry {
97   ApkAssetsCookie cookie;
98   uint32_t type_spec_flags;
99   Res_value value;
100 };
101 
AssetManager2(ApkAssetsList apk_assets,const ResTable_config & configuration)102 AssetManager2::AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration) {
103   configurations_.push_back(configuration);
104 
105   // Don't invalidate caches here as there's nothing cached yet.
106   SetApkAssets(apk_assets, false);
107 }
108 
AssetManager2()109 AssetManager2::AssetManager2() {
110   configurations_.emplace_back();
111 }
112 
SetApkAssets(ApkAssetsList apk_assets,bool invalidate_caches)113 bool AssetManager2::SetApkAssets(ApkAssetsList apk_assets, bool invalidate_caches) {
114   BuildDynamicRefTable(apk_assets);
115   RebuildFilterList();
116   if (invalidate_caches) {
117     InvalidateCaches(static_cast<uint32_t>(-1));
118   }
119   return true;
120 }
121 
PresetApkAssets(ApkAssetsList apk_assets)122 void AssetManager2::PresetApkAssets(ApkAssetsList apk_assets) {
123   BuildDynamicRefTable(apk_assets);
124 }
125 
SetApkAssets(std::initializer_list<ApkAssetsPtr> apk_assets,bool invalidate_caches)126 bool AssetManager2::SetApkAssets(std::initializer_list<ApkAssetsPtr> apk_assets,
127                                  bool invalidate_caches) {
128   return SetApkAssets(ApkAssetsList(apk_assets.begin(), apk_assets.size()), invalidate_caches);
129 }
130 
BuildDynamicRefTable(ApkAssetsList apk_assets)131 void AssetManager2::BuildDynamicRefTable(ApkAssetsList apk_assets) {
132   auto op = StartOperation();
133 
134   apk_assets_.resize(apk_assets.size());
135   for (size_t i = 0; i != apk_assets.size(); ++i) {
136     apk_assets_[i].first = apk_assets[i];
137     // Let's populate the locked assets right away as we're going to need them here later.
138     apk_assets_[i].second = apk_assets[i];
139   }
140 
141   package_groups_.clear();
142   package_ids_.fill(0xff);
143 
144   // A mapping from path of apk assets that could be target packages of overlays to the runtime
145   // package id of its first loaded package. Overlays currently can only override resources in the
146   // first package in the target resource table.
147   std::unordered_map<std::string_view, uint8_t> target_assets_package_ids;
148 
149   // Overlay resources are not directly referenced by an application so their resource ids
150   // can change throughout the application's lifetime. Assign overlay package ids last.
151   std::vector<const ApkAssets*> sorted_apk_assets;
152   sorted_apk_assets.reserve(apk_assets.size());
153   for (auto& asset : apk_assets) {
154     sorted_apk_assets.push_back(asset.get());
155   }
156   std::stable_partition(sorted_apk_assets.begin(), sorted_apk_assets.end(),
157                         [](auto a) { return !a->IsOverlay(); });
158 
159   // The assets cookie must map to the position of the apk assets in the unsorted apk assets list.
160   std::unordered_map<const ApkAssets*, ApkAssetsCookie> apk_assets_cookies;
161   apk_assets_cookies.reserve(apk_assets.size());
162   for (size_t i = 0, n = apk_assets.size(); i < n; i++) {
163     apk_assets_cookies[apk_assets[i].get()] = static_cast<ApkAssetsCookie>(i);
164   }
165 
166   // 0x01 is reserved for the android package.
167   int next_package_id = 0x02;
168   for (const ApkAssets* apk_assets : sorted_apk_assets) {
169     std::shared_ptr<OverlayDynamicRefTable> overlay_ref_table;
170     if (auto loaded_idmap = apk_assets->GetLoadedIdmap(); loaded_idmap != nullptr) {
171       // The target package must precede the overlay package in the apk assets paths in order
172       // to take effect.
173       auto iter = target_assets_package_ids.find(loaded_idmap->TargetApkPath());
174       if (iter == target_assets_package_ids.end()) {
175          LOG(INFO) << "failed to find target package for overlay "
176                    << loaded_idmap->OverlayApkPath();
177       } else {
178         uint8_t target_package_id = iter->second;
179 
180         // Create a special dynamic reference table for the overlay to rewrite references to
181         // overlay resources as references to the target resources they overlay.
182         overlay_ref_table = std::make_shared<OverlayDynamicRefTable>(
183             loaded_idmap->GetOverlayDynamicRefTable(target_package_id));
184 
185         // Add the overlay resource map to the target package's set of overlays.
186         const uint8_t target_idx = package_ids_[target_package_id];
187         CHECK(target_idx != 0xff) << "overlay target '" << loaded_idmap->TargetApkPath()
188                                   << "'added to apk_assets_package_ids but does not have an"
189                                   << " assigned package group";
190 
191         PackageGroup& target_package_group = package_groups_[target_idx];
192         target_package_group.overlays_.push_back(
193             ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id,
194                                                                   overlay_ref_table.get()),
195                               apk_assets_cookies[apk_assets]});
196       }
197     }
198 
199     const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc();
200     for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) {
201       // Get the package ID or assign one if a shared library.
202       int package_id;
203       if (package->IsDynamic()) {
204         package_id = next_package_id++;
205       } else {
206         package_id = package->GetPackageId();
207       }
208 
209       uint8_t idx = package_ids_[package_id];
210       if (idx == 0xff) {
211         // Add the mapping for package ID to index if not present.
212         package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size());
213         PackageGroup& new_group = package_groups_.emplace_back();
214 
215         if (overlay_ref_table != nullptr) {
216           // If this package is from an overlay, use a dynamic reference table that can rewrite
217           // overlay resource ids to their corresponding target resource ids.
218           new_group.dynamic_ref_table = std::move(overlay_ref_table);
219         }
220 
221         DynamicRefTable* ref_table = new_group.dynamic_ref_table.get();
222         ref_table->mAssignedPackageId = package_id;
223         ref_table->mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
224       }
225 
226       // Add the package to the set of packages with the same ID.
227       PackageGroup* package_group = &package_groups_[idx];
228       package_group->packages_.emplace_back().loaded_package_ = package.get();
229       package_group->cookies_.push_back(apk_assets_cookies[apk_assets]);
230 
231       // Add the package name -> build time ID mappings.
232       for (const DynamicPackageEntry& entry : package->GetDynamicPackageMap()) {
233         String16 package_name(entry.package_name.c_str(), entry.package_name.size());
234         package_group->dynamic_ref_table->mEntries.replaceValueFor(
235             package_name, static_cast<uint8_t>(entry.package_id));
236       }
237 
238       if (auto apk_assets_path = apk_assets->GetPath()) {
239         // Overlay target ApkAssets must have been created using path based load apis.
240         target_assets_package_ids.emplace(*apk_assets_path, package_id);
241       }
242     }
243   }
244 
245   // Now assign the runtime IDs so that we have a build-time to runtime ID map.
246   DynamicRefTable::AliasMap aliases;
247   for (const auto& group : package_groups_) {
248     const std::string& package_name = group.packages_[0].loaded_package_->GetPackageName();
249     const auto name_16 = String16(package_name.c_str(), package_name.size());
250     for (auto&& inner_group : package_groups_) {
251       inner_group.dynamic_ref_table->addMapping(name_16,
252                                                 group.dynamic_ref_table->mAssignedPackageId);
253     }
254 
255     for (const auto& package : group.packages_) {
256       const auto& package_aliases = package.loaded_package_->GetAliasResourceIdMap();
257       aliases.insert(aliases.end(), package_aliases.begin(), package_aliases.end());
258     }
259   }
260 
261   if (!aliases.empty()) {
262     std::sort(aliases.begin(), aliases.end(), [](auto&& l, auto&& r) { return l.first < r.first; });
263 
264     // Add the alias resources to the dynamic reference table of every package group. Since
265     // staging aliases can only be defined by the framework package (which is not a shared
266     // library), the compile-time package id of the framework is the same across all packages
267     // that compile against the framework.
268     for (auto& group : std::span(package_groups_.data(), package_groups_.size() - 1)) {
269       group.dynamic_ref_table->setAliases(aliases);
270     }
271     package_groups_.back().dynamic_ref_table->setAliases(std::move(aliases));
272   }
273 }
274 
DumpToLog() const275 void AssetManager2::DumpToLog() const {
276   LOG(INFO) << base::StringPrintf("AssetManager2(this=%p)", this);
277 
278   auto op = StartOperation();
279   std::string list;
280   for (size_t i = 0, s = apk_assets_.size(); i < s; ++i) {
281     const auto& assets = GetApkAssets(i);
282     base::StringAppendF(&list, "%s,", assets ? assets->GetDebugName().c_str() : "nullptr");
283   }
284   LOG(INFO) << "ApkAssets: " << list;
285 
286   list = "";
287   for (size_t i = 0; i < package_ids_.size(); i++) {
288     if (package_ids_[i] != 0xff) {
289       base::StringAppendF(&list, "%02x -> %d, ", (int)i, package_ids_[i]);
290     }
291   }
292   LOG(INFO) << "Package ID map: " << list;
293 
294   for (const auto& package_group: package_groups_) {
295     list = "";
296     for (const auto& package : package_group.packages_) {
297       const LoadedPackage* loaded_package = package.loaded_package_;
298       base::StringAppendF(&list, "%s(%02x%s), ", loaded_package->GetPackageName().c_str(),
299                           loaded_package->GetPackageId(),
300                           (loaded_package->IsDynamic() ? " dynamic" : ""));
301     }
302     LOG(INFO) << base::StringPrintf("PG (%02x): ",
303                                     package_group.dynamic_ref_table->mAssignedPackageId)
304               << list;
305 
306     for (size_t i = 0; i < 256; i++) {
307       if (package_group.dynamic_ref_table->mLookupTable[i] != 0) {
308         LOG(INFO) << base::StringPrintf("    e[0x%02x] -> 0x%02x", (uint8_t) i,
309                                         package_group.dynamic_ref_table->mLookupTable[i]);
310       }
311     }
312   }
313 }
314 
GetStringPoolForCookie(ApkAssetsCookie cookie) const315 const ResStringPool* AssetManager2::GetStringPoolForCookie(ApkAssetsCookie cookie) const {
316   if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
317     return nullptr;
318   }
319   auto op = StartOperation();
320   const auto& assets = GetApkAssets(cookie);
321   return assets ? assets->GetLoadedArsc()->GetStringPool() : nullptr;
322 }
323 
GetDynamicRefTableForPackage(uint32_t package_id) const324 const DynamicRefTable* AssetManager2::GetDynamicRefTableForPackage(uint32_t package_id) const {
325   if (package_id >= package_ids_.size()) {
326     return nullptr;
327   }
328 
329   const size_t idx = package_ids_[package_id];
330   if (idx == 0xff) {
331     return nullptr;
332   }
333   return package_groups_[idx].dynamic_ref_table.get();
334 }
335 
GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const336 std::shared_ptr<const DynamicRefTable> AssetManager2::GetDynamicRefTableForCookie(
337     ApkAssetsCookie cookie) const {
338   for (const PackageGroup& package_group : package_groups_) {
339     for (const ApkAssetsCookie& package_cookie : package_group.cookies_) {
340       if (package_cookie == cookie) {
341         return package_group.dynamic_ref_table;
342       }
343     }
344   }
345   return nullptr;
346 }
347 
348 const std::unordered_map<std::string, std::string>*
GetOverlayableMapForPackage(uint32_t package_id) const349   AssetManager2::GetOverlayableMapForPackage(uint32_t package_id) const {
350 
351   if (package_id >= package_ids_.size()) {
352     return nullptr;
353   }
354 
355   const size_t idx = package_ids_[package_id];
356   if (idx == 0xff) {
357     return nullptr;
358   }
359 
360   const PackageGroup& package_group = package_groups_[idx];
361   if (package_group.packages_.empty()) {
362     return nullptr;
363   }
364 
365   const auto loaded_package = package_group.packages_[0].loaded_package_;
366   return &loaded_package->GetOverlayableMap();
367 }
368 
GetOverlayablesToString(android::StringPiece package_name,std::string * out) const369 bool AssetManager2::GetOverlayablesToString(android::StringPiece package_name,
370                                             std::string* out) const {
371   auto op = StartOperation();
372   uint8_t package_id = 0U;
373   for (size_t i = 0, s = apk_assets_.size(); i != s; ++i) {
374     const auto& assets = GetApkAssets(i);
375     if (!assets) {
376       continue;
377     }
378     const LoadedArsc* loaded_arsc = assets->GetLoadedArsc();
379     if (loaded_arsc == nullptr) {
380       continue;
381     }
382 
383     const auto& loaded_packages = loaded_arsc->GetPackages();
384     if (loaded_packages.empty()) {
385       continue;
386     }
387 
388     const auto& loaded_package = loaded_packages[0];
389     if (loaded_package->GetPackageName() == package_name) {
390       package_id = GetAssignedPackageId(loaded_package.get());
391       break;
392     }
393   }
394 
395   if (package_id == 0U) {
396     ANDROID_LOG(ERROR) << base::StringPrintf("No package with name '%s", package_name.data());
397     return false;
398   }
399 
400   const size_t idx = package_ids_[package_id];
401   if (idx == 0xff) {
402     return false;
403   }
404 
405   std::string output;
406   for (const ConfiguredPackage& package : package_groups_[idx].packages_) {
407     const LoadedPackage* loaded_package = package.loaded_package_;
408     for (auto it = loaded_package->begin(); it != loaded_package->end(); it++) {
409       const OverlayableInfo* info = loaded_package->GetOverlayableInfo(*it);
410       if (info != nullptr) {
411         auto res_name = GetResourceName(*it);
412         if (!res_name.has_value()) {
413           ANDROID_LOG(ERROR) << base::StringPrintf(
414               "Unable to retrieve name of overlayable resource 0x%08x", *it);
415           return false;
416         }
417 
418         const std::string name = ToFormattedResourceString(*res_name);
419         output.append(base::StringPrintf(
420             "resource='%s' overlayable='%s' actor='%s' policy='0x%08x'\n",
421             name.c_str(), info->name.data(), info->actor.data(), info->policy_flags));
422       }
423     }
424   }
425 
426   *out = std::move(output);
427   return true;
428 }
429 
ContainsAllocatedTable() const430 bool AssetManager2::ContainsAllocatedTable() const {
431   auto op = StartOperation();
432   for (size_t i = 0, s = apk_assets_.size(); i != s; ++i) {
433     const auto& assets = GetApkAssets(i);
434     if (assets && assets->IsTableAllocated()) {
435       return true;
436     }
437   }
438   return false;
439 }
440 
SetConfigurations(std::span<const ResTable_config> configurations,bool force_refresh)441 void AssetManager2::SetConfigurations(std::span<const ResTable_config> configurations,
442                                       bool force_refresh) {
443   int diff = 0;
444   if (force_refresh) {
445     diff = -1;
446   } else {
447     if (configurations_.size() != configurations.size()) {
448       diff = -1;
449     } else {
450       for (int i = 0; i < configurations_.size(); i++) {
451         diff |= configurations_[i].diff(configurations[i]);
452       }
453     }
454   }
455   configurations_.clear();
456   for (auto&& config : configurations) {
457     configurations_.emplace_back(config);
458   }
459   if (diff) {
460     RebuildFilterList();
461     InvalidateCaches(static_cast<uint32_t>(diff));
462   }
463 }
464 
GetNonSystemOverlays() const465 std::set<AssetManager2::ApkAssetsPtr> AssetManager2::GetNonSystemOverlays() const {
466   std::set<ApkAssetsPtr> non_system_overlays;
467   for (const PackageGroup& package_group : package_groups_) {
468     bool found_system_package = false;
469     for (const ConfiguredPackage& package : package_group.packages_) {
470       if (package.loaded_package_->IsSystem()) {
471         found_system_package = true;
472         break;
473       }
474     }
475 
476     if (!found_system_package) {
477       auto op = StartOperation();
478       for (const ConfiguredOverlay& overlay : package_group.overlays_) {
479         if (const auto& asset = GetApkAssets(overlay.cookie)) {
480           non_system_overlays.insert(std::move(asset));
481         }
482       }
483     }
484   }
485 
486   return non_system_overlays;
487 }
488 
GetResourceConfigurations(bool exclude_system,bool exclude_mipmap) const489 base::expected<std::set<ResTable_config>, IOError> AssetManager2::GetResourceConfigurations(
490     bool exclude_system, bool exclude_mipmap) const {
491   ATRACE_NAME("AssetManager::GetResourceConfigurations");
492   auto op = StartOperation();
493 
494   const auto non_system_overlays =
495       exclude_system ? GetNonSystemOverlays() : std::set<ApkAssetsPtr>();
496 
497   std::set<ResTable_config> configurations;
498   for (const PackageGroup& package_group : package_groups_) {
499     for (size_t i = 0; i < package_group.packages_.size(); i++) {
500       const ConfiguredPackage& package = package_group.packages_[i];
501       if (exclude_system) {
502         if (package.loaded_package_->IsSystem()) {
503           continue;
504         }
505         if (!non_system_overlays.empty()) {
506           // Exclude overlays that target only system resources.
507           const auto& apk_assets = GetApkAssets(package_group.cookies_[i]);
508           if (apk_assets && apk_assets->IsOverlay() &&
509               non_system_overlays.find(apk_assets) == non_system_overlays.end()) {
510             continue;
511           }
512         }
513       }
514 
515       auto result = package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations);
516       if (UNLIKELY(!result.has_value())) {
517         return base::unexpected(result.error());
518       }
519     }
520   }
521   return configurations;
522 }
523 
GetResourceLocales(bool exclude_system,bool merge_equivalent_languages) const524 std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system,
525                                                         bool merge_equivalent_languages) const {
526   ATRACE_NAME("AssetManager::GetResourceLocales");
527   auto op = StartOperation();
528 
529   std::set<std::string> locales;
530   const auto non_system_overlays =
531       exclude_system ? GetNonSystemOverlays() : std::set<ApkAssetsPtr>();
532 
533   for (const PackageGroup& package_group : package_groups_) {
534     for (size_t i = 0; i < package_group.packages_.size(); i++) {
535       const ConfiguredPackage& package = package_group.packages_[i];
536       if (exclude_system) {
537         if (package.loaded_package_->IsSystem()) {
538           continue;
539         }
540         if (!non_system_overlays.empty()) {
541           // Exclude overlays that target only system resources.
542           const auto& apk_assets = GetApkAssets(package_group.cookies_[i]);
543           if (apk_assets && apk_assets->IsOverlay() &&
544               non_system_overlays.find(apk_assets) == non_system_overlays.end()) {
545             continue;
546           }
547         }
548       }
549 
550       package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales);
551     }
552   }
553   return locales;
554 }
555 
Open(const std::string & filename,Asset::AccessMode mode) const556 std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename,
557                                            Asset::AccessMode mode) const {
558   const std::string new_path = "assets/" + filename;
559   return OpenNonAsset(new_path, mode);
560 }
561 
Open(const std::string & filename,ApkAssetsCookie cookie,Asset::AccessMode mode) const562 std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie,
563                                            Asset::AccessMode mode) const {
564   const std::string new_path = "assets/" + filename;
565   return OpenNonAsset(new_path, cookie, mode);
566 }
567 
OpenDir(const std::string & dirname) const568 std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) const {
569   ATRACE_NAME("AssetManager::OpenDir");
570   auto op = StartOperation();
571 
572   std::string full_path = "assets/" + dirname;
573   auto files = util::make_unique<SortedVector<AssetDir::FileInfo>>();
574 
575   // Start from the back.
576   for (size_t i = apk_assets_.size(); i > 0; --i) {
577     const auto& apk_assets = GetApkAssets(i - 1);
578     if (!apk_assets || apk_assets->IsOverlay()) {
579       continue;
580     }
581 
582     auto func = [&](StringPiece name, FileType type) {
583       AssetDir::FileInfo info;
584       info.setFileName(String8(name.data(), name.size()));
585       info.setFileType(type);
586       info.setSourceName(String8(apk_assets->GetDebugName().c_str()));
587       files->add(info);
588     };
589 
590     if (!apk_assets->GetAssetsProvider()->ForEachFile(full_path, func)) {
591       return {};
592     }
593   }
594 
595   std::unique_ptr<AssetDir> asset_dir = util::make_unique<AssetDir>();
596   asset_dir->setFileList(files.release());
597   return asset_dir;
598 }
599 
600 // Search in reverse because that's how we used to do it and we need to preserve behaviour.
601 // This is unfortunate, because ClassLoaders delegate to the parent first, so the order
602 // is inconsistent for split APKs.
OpenNonAsset(const std::string & filename,Asset::AccessMode mode,ApkAssetsCookie * out_cookie) const603 std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
604                                                    Asset::AccessMode mode,
605                                                    ApkAssetsCookie* out_cookie) const {
606   auto op = StartOperation();
607   for (size_t i = apk_assets_.size(); i > 0; i--) {
608     const auto& assets = GetApkAssets(i - 1);
609     // Prevent RRO from modifying assets and other entries accessed by file
610     // path. Explicitly asking for a path in a given package (denoted by a
611     // cookie) is still OK.
612     if (!assets || assets->IsOverlay()) {
613       continue;
614     }
615 
616     std::unique_ptr<Asset> asset = assets->GetAssetsProvider()->Open(filename, mode);
617     if (asset) {
618       if (out_cookie != nullptr) {
619         *out_cookie = i - 1;
620       }
621       return asset;
622     }
623   }
624 
625   if (out_cookie != nullptr) {
626     *out_cookie = kInvalidCookie;
627   }
628   return {};
629 }
630 
OpenNonAsset(const std::string & filename,ApkAssetsCookie cookie,Asset::AccessMode mode) const631 std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
632                                                    ApkAssetsCookie cookie,
633                                                    Asset::AccessMode mode) const {
634   if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
635     return {};
636   }
637   auto op = StartOperation();
638   const auto& assets = GetApkAssets(cookie);
639   return assets ? assets->GetAssetsProvider()->Open(filename, mode) : nullptr;
640 }
641 
FindEntry(uint32_t resid,uint16_t density_override,bool stop_at_first_match,bool ignore_configuration) const642 base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
643     uint32_t resid, uint16_t density_override, bool stop_at_first_match,
644     bool ignore_configuration) const {
645   const bool logging_enabled = resource_resolution_logging_enabled_;
646   if (UNLIKELY(logging_enabled)) {
647     // Clear the last logged resource resolution.
648     ResetResourceResolution();
649     last_resolution_.resid = resid;
650   }
651 
652   auto op = StartOperation();
653 
654 
655   // Retrieve the package group from the package id of the resource id.
656   if (UNLIKELY(!is_valid_resid(resid))) {
657     LOG(ERROR) << base::StringPrintf("Invalid resource ID 0x%08x.", resid);
658     return base::unexpected(std::nullopt);
659   }
660 
661   const uint32_t package_id = get_package_id(resid);
662   const uint8_t type_idx = get_type_id(resid) - 1;
663   const uint16_t entry_idx = get_entry_id(resid);
664   uint8_t package_idx = package_ids_[package_id];
665   if (UNLIKELY(package_idx == 0xff)) {
666     ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for resource ID 0x%08x.",
667                                              package_id, resid);
668     return base::unexpected(std::nullopt);
669   }
670 
671   const PackageGroup& package_group = package_groups_[package_idx];
672   std::optional<FindEntryResult> final_result;
673   bool final_has_locale = false;
674   bool final_overlaid = false;
675   for (auto & config : configurations_) {
676     // Might use this if density_override != 0.
677     ResTable_config density_override_config;
678 
679     // Select our configuration or generate a density override configuration.
680     const ResTable_config* desired_config = &config;
681     if (density_override != 0 && density_override != config.density) {
682       density_override_config = config;
683       density_override_config.density = density_override;
684       desired_config = &density_override_config;
685     }
686 
687     auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
688                                     stop_at_first_match, ignore_configuration);
689     if (UNLIKELY(!result.has_value())) {
690       return base::unexpected(result.error());
691     }
692     bool overlaid = false;
693     if (!stop_at_first_match && !ignore_configuration) {
694       const auto& assets = GetApkAssets(result->cookie);
695       if (!assets) {
696         ALOGE("Found expired ApkAssets #%d for resource ID 0x%08x.", result->cookie, resid);
697         return base::unexpected(std::nullopt);
698       }
699       if (!assets->IsLoader()) {
700         for (const auto& id_map : package_group.overlays_) {
701           auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
702           if (!overlay_entry) {
703             // No id map entry exists for this target resource.
704             continue;
705           }
706           if (overlay_entry.IsInlineValue()) {
707             // The target resource is overlaid by an inline value not represented by a resource.
708             ConfigDescription best_frro_config;
709             Res_value best_frro_value;
710             bool frro_found = false;
711             for( const auto& [config, value] : overlay_entry.GetInlineValue()) {
712               if ((!frro_found || config.isBetterThan(best_frro_config, desired_config))
713                   && config.match(*desired_config)) {
714                 frro_found = true;
715                 best_frro_config = config;
716                 best_frro_value = value;
717               }
718             }
719             if (!frro_found) {
720               continue;
721             }
722             result->entry = best_frro_value;
723             result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
724             result->cookie = id_map.cookie;
725 
726             if (UNLIKELY(logging_enabled)) {
727               last_resolution_.steps.push_back(Resolution::Step{
728                   Resolution::Step::Type::OVERLAID_INLINE, result->cookie, String8()});
729               if (auto path = assets->GetPath()) {
730                 const std::string overlay_path = path->data();
731                 if (IsFabricatedOverlay(overlay_path)) {
732                   // FRRO don't have package name so we use the creating package here.
733                   String8 frro_name = String8("FRRO");
734                   // Get the first part of it since the expected one should be like
735                   // {overlayPackageName}-{overlayName}-{4 alphanumeric chars}.frro
736                   // under /data/resource-cache/.
737                   const std::string name = overlay_path.substr(overlay_path.rfind('/') + 1);
738                   const size_t end = name.find('-');
739                   if (frro_name.size() != overlay_path.size() && end != std::string::npos) {
740                     frro_name.append(base::StringPrintf(" created by %s",
741                                                         name.substr(0 /* pos */,
742                                                                     end).c_str()).c_str());
743                   }
744                   last_resolution_.best_package_name = frro_name;
745                 } else {
746                   last_resolution_.best_package_name = result->package_name->c_str();
747                 }
748               }
749               overlaid = true;
750             }
751             continue;
752           }
753 
754           auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override,
755                                           false /* stop_at_first_match */,
756                                           false /* ignore_configuration */);
757           if (UNLIKELY(IsIOError(overlay_result))) {
758             return base::unexpected(overlay_result.error());
759           }
760           if (!overlay_result.has_value()) {
761             continue;
762           }
763 
764           if (!overlay_result->config.isBetterThan(result->config, desired_config)
765               && overlay_result->config.compare(result->config) != 0) {
766             // The configuration of the entry for the overlay must be equal to or better than the
767             // target configuration to be chosen as the better value.
768             continue;
769           }
770 
771           result->cookie = overlay_result->cookie;
772           result->entry = overlay_result->entry;
773           result->config = overlay_result->config;
774           result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
775 
776           if (UNLIKELY(logging_enabled)) {
777             last_resolution_.steps.push_back(
778                 Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->cookie,
779                                  overlay_result->config.toString()});
780             last_resolution_.best_package_name =
781                 overlay_result->package_name->c_str();
782             overlaid = true;
783           }
784         }
785       }
786     }
787 
788     bool has_locale = false;
789     if (result->config.locale == 0) {
790       if (default_locale_ != 0) {
791         ResTable_config conf = {.locale = default_locale_};
792         // Since we know conf has a locale and only a locale, match will tell us if that locale
793         // matches
794         has_locale = conf.match(config);
795       }
796     } else {
797       has_locale = true;
798     }
799 
800       // if we don't have a result yet
801     if (!final_result ||
802         // or this config is better before the locale than the existing result
803         result->config.isBetterThanBeforeLocale(final_result->config, desired_config) ||
804         // or the existing config isn't better before locale and this one specifies a locale
805         // whereas the existing one doesn't
806         (!final_result->config.isBetterThanBeforeLocale(result->config, desired_config)
807             && has_locale && !final_has_locale)) {
808       final_result = result.value();
809       final_overlaid = overlaid;
810       final_has_locale = has_locale;
811     }
812   }
813 
814   if (UNLIKELY(logging_enabled)) {
815     last_resolution_.cookie = final_result->cookie;
816     last_resolution_.type_string_ref = final_result->type_string_ref;
817     last_resolution_.entry_string_ref = final_result->entry_string_ref;
818     last_resolution_.best_config_name = final_result->config.toString();
819     if (!final_overlaid) {
820       last_resolution_.best_package_name = final_result->package_name->c_str();
821     }
822   }
823 
824   return *final_result;
825 }
826 
FindEntryInternal(const PackageGroup & package_group,uint8_t type_idx,uint16_t entry_idx,const ResTable_config & desired_config,bool stop_at_first_match,bool ignore_configuration) const827 base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
828     const PackageGroup& package_group, uint8_t type_idx, uint16_t entry_idx,
829     const ResTable_config& desired_config, bool stop_at_first_match,
830     bool ignore_configuration) const {
831   const bool logging_enabled = resource_resolution_logging_enabled_;
832   ApkAssetsCookie best_cookie = kInvalidCookie;
833   const LoadedPackage* best_package = nullptr;
834   incfs::verified_map_ptr<ResTable_type> best_type;
835   const ResTable_config* best_config = nullptr;
836   uint32_t best_offset = 0U;
837   uint32_t type_flags = 0U;
838 
839   // If `desired_config` is not the same as the set configuration or the caller will accept a value
840   // from any configuration, then we cannot use our filtered list of types since it only it contains
841   // types matched to the set configuration.
842   const bool use_filtered = !ignore_configuration && std::find_if(
843       configurations_.begin(), configurations_.end(),
844       [&desired_config](auto& value) { return &desired_config == &value; })
845       != configurations_.end();
846   const size_t package_count = package_group.packages_.size();
847   for (size_t pi = 0; pi < package_count; pi++) {
848     const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi];
849     const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_;
850     const ApkAssetsCookie cookie = package_group.cookies_[pi];
851 
852     // If the type IDs are offset in this package, we need to take that into account when searching
853     // for a type.
854     const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx);
855     if (UNLIKELY(type_spec == nullptr)) {
856       continue;
857     }
858 
859     // Allow custom loader packages to overlay resource values with configurations equivalent to the
860     // current best configuration.
861     const bool package_is_loader = loaded_package->IsCustomLoader();
862 
863     auto entry_flags = type_spec->GetFlagsForEntryIndex(entry_idx);
864     if (UNLIKELY(!entry_flags.has_value())) {
865       return base::unexpected(entry_flags.error());
866     }
867     type_flags |= entry_flags.value();
868 
869     const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
870     const size_t type_entry_count = (use_filtered) ? filtered_group.type_entries.size()
871                                                    : type_spec->type_entries.size();
872     for (size_t i = 0; i < type_entry_count; i++) {
873       const TypeSpec::TypeEntry* type_entry = (use_filtered) ? filtered_group.type_entries[i]
874                                                              : &type_spec->type_entries[i];
875 
876       // We can skip calling ResTable_config::match() if the caller does not care for the
877       // configuration to match or if we're using the list of types that have already had their
878       // configuration matched. The exception to this is when the user has multiple locales set
879       // because the filtered list will then have values from multiple locales and we will need to
880       // call match() to make sure the current entry matches the config we are currently checking.
881       const ResTable_config& this_config = type_entry->config;
882       if (!((use_filtered && (configurations_.size() == 1))
883           || ignore_configuration || this_config.match(desired_config))) {
884         continue;
885       }
886 
887       Resolution::Step::Type resolution_type;
888       if (best_config == nullptr) {
889         resolution_type = Resolution::Step::Type::INITIAL;
890       } else if (this_config.isBetterThan(*best_config, &desired_config)) {
891         resolution_type = Resolution::Step::Type::BETTER_MATCH;
892       } else if (package_is_loader && this_config.compare(*best_config) == 0) {
893         resolution_type = Resolution::Step::Type::OVERLAID;
894       } else {
895         if (UNLIKELY(logging_enabled)) {
896           last_resolution_.steps.push_back(Resolution::Step{Resolution::Step::Type::SKIPPED,
897                                                             cookie, this_config.toString()});
898         }
899         continue;
900       }
901 
902       // The configuration matches and is better than the previous selection.
903       // Find the entry value if it exists for this configuration.
904       const auto& type = type_entry->type;
905       const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx);
906       if (UNLIKELY(IsIOError(offset))) {
907         return base::unexpected(offset.error());
908       }
909 
910       if (!offset.has_value()) {
911         if (UNLIKELY(logging_enabled)) {
912           last_resolution_.steps.push_back(Resolution::Step{Resolution::Step::Type::NO_ENTRY,
913                                                             cookie, this_config.toString()});
914         }
915         continue;
916       }
917 
918       best_cookie = cookie;
919       best_package = loaded_package;
920       best_type = type;
921       best_config = &this_config;
922       best_offset = offset.value();
923 
924       if (UNLIKELY(logging_enabled)) {
925         last_resolution_.steps.push_back(Resolution::Step{resolution_type,
926                                                           cookie, this_config.toString()});
927       }
928 
929       // Any configuration will suffice, so break.
930       if (stop_at_first_match) {
931         break;
932       }
933     }
934   }
935 
936   if (UNLIKELY(best_cookie == kInvalidCookie)) {
937     return base::unexpected(std::nullopt);
938   }
939 
940   auto best_entry_verified = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
941   if (!best_entry_verified.has_value()) {
942     return base::unexpected(best_entry_verified.error());
943   }
944 
945   const auto entry = GetEntryValue(*best_entry_verified);
946   if (!entry.has_value()) {
947     return base::unexpected(entry.error());
948   }
949 
950   return FindEntryResult{
951     .cookie = best_cookie,
952     .entry = *entry,
953     .config = *best_config,
954     .type_flags = type_flags,
955     .dynamic_ref_table = package_group.dynamic_ref_table.get(),
956     .package_name = &best_package->GetPackageName(),
957     .type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1),
958     .entry_string_ref = StringPoolRef(best_package->GetKeyStringPool(),
959                                       (*best_entry_verified)->key()),
960   };
961 }
962 
ResetResourceResolution() const963 void AssetManager2::ResetResourceResolution() const {
964   last_resolution_ = Resolution{};
965 }
966 
SetResourceResolutionLoggingEnabled(bool enabled)967 void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) {
968   resource_resolution_logging_enabled_ = enabled;
969   if (!enabled) {
970     ResetResourceResolution();
971   }
972 }
973 
GetLastResourceResolution() const974 std::string AssetManager2::GetLastResourceResolution() const {
975   if (!resource_resolution_logging_enabled_) {
976     LOG(ERROR) << "Must enable resource resolution logging before getting path.";
977     return {};
978   }
979 
980   const ApkAssetsCookie cookie = last_resolution_.cookie;
981   if (cookie == kInvalidCookie) {
982     LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path.";
983     return {};
984   }
985 
986   auto op = StartOperation();
987 
988   const uint32_t resid = last_resolution_.resid;
989   const auto& assets = GetApkAssets(cookie);
990   const auto package =
991       assets ? assets->GetLoadedArsc()->GetPackageById(get_package_id(resid)) : nullptr;
992 
993   std::string resource_name_string;
994   if (package != nullptr) {
995     auto resource_name = ToResourceName(last_resolution_.type_string_ref,
996                                         last_resolution_.entry_string_ref,
997                                         package->GetPackageName());
998     resource_name_string = resource_name.has_value() ?
999         ToFormattedResourceString(resource_name.value()) : "<unknown>";
1000   }
1001 
1002   std::stringstream log_stream;
1003   if (configurations_.size() == 1) {
1004     log_stream << base::StringPrintf("Resolution for 0x%08x %s\n"
1005                                      "\tFor config - %s", resid, resource_name_string.c_str(),
1006                                      configurations_[0].toString().c_str());
1007   } else {
1008     ResTable_config conf = configurations_[0];
1009     conf.clearLocale();
1010     log_stream << base::StringPrintf("Resolution for 0x%08x %s\n\tFor config - %s and locales",
1011                                      resid, resource_name_string.c_str(), conf.toString().c_str());
1012     char str[40];
1013     str[0] = '\0';
1014     for(auto iter = configurations_.begin(); iter < configurations_.end(); iter++) {
1015       iter->getBcp47Locale(str);
1016       log_stream << base::StringPrintf(" %s%s", str, iter < configurations_.end() ? "," : "");
1017     }
1018   }
1019   for (const Resolution::Step& step : last_resolution_.steps) {
1020     constexpr static std::array kStepStrings = {
1021         "Found initial",
1022         "Found better",
1023         "Overlaid",
1024         "Overlaid inline",
1025         "Skipped",
1026         "No entry"
1027     };
1028 
1029     if (step.type < Resolution::Step::Type::INITIAL
1030         || step.type > Resolution::Step::Type::NO_ENTRY) {
1031       continue;
1032     }
1033     const auto prefix = kStepStrings[int(step.type) - int(Resolution::Step::Type::INITIAL)];
1034     const auto& assets = GetApkAssets(step.cookie);
1035     log_stream << "\n\t" << prefix << ": " << (assets ? assets->GetDebugName() : "<null>")
1036                << " #" << step.cookie;
1037     if (!step.config_name.empty()) {
1038       log_stream << " - " << step.config_name;
1039     }
1040   }
1041 
1042   log_stream << "\nBest matching is from "
1043              << (last_resolution_.best_config_name.empty() ? "default"
1044                     : last_resolution_.best_config_name.c_str())
1045              << " configuration of " << last_resolution_.best_package_name;
1046   return log_stream.str();
1047 }
1048 
GetParentThemeResourceId(uint32_t resid) const1049 base::expected<uint32_t, NullOrIOError> AssetManager2::GetParentThemeResourceId(uint32_t resid)
1050 const {
1051   auto entry = FindEntry(resid, 0u /* density_override */,
1052                          false /* stop_at_first_match */,
1053                          false /* ignore_configuration */);
1054   if (!entry.has_value()) {
1055     return base::unexpected(entry.error());
1056   }
1057 
1058   auto entry_map = std::get_if<incfs::verified_map_ptr<ResTable_map_entry>>(&entry->entry);
1059   if (entry_map == nullptr) {
1060     // Not a bag, nothing to do.
1061     return base::unexpected(std::nullopt);
1062   }
1063 
1064   auto map = *entry_map;
1065   const uint32_t parent_resid = dtohl(map->parent.ident);
1066 
1067   return parent_resid;
1068 }
1069 
GetResourceName(uint32_t resid) const1070 base::expected<AssetManager2::ResourceName, NullOrIOError> AssetManager2::GetResourceName(
1071     uint32_t resid) const {
1072   auto result = FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */,
1073                           true /* ignore_configuration */);
1074   if (!result.has_value()) {
1075     return base::unexpected(result.error());
1076   }
1077 
1078   return ToResourceName(result->type_string_ref,
1079                         result->entry_string_ref,
1080                         *result->package_name);
1081 }
1082 
GetResourceTypeSpecFlags(uint32_t resid) const1083 base::expected<uint32_t, NullOrIOError> AssetManager2::GetResourceTypeSpecFlags(
1084     uint32_t resid) const {
1085   auto result = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */,
1086                           true /* ignore_configuration */);
1087   if (!result.has_value()) {
1088     return base::unexpected(result.error());
1089   }
1090   return result->type_flags;
1091 }
1092 
GetResource(uint32_t resid,bool may_be_bag,uint16_t density_override) const1093 base::expected<AssetManager2::SelectedValue, NullOrIOError> AssetManager2::GetResource(
1094     uint32_t resid, bool may_be_bag, uint16_t density_override) const {
1095   auto result = FindEntry(resid, density_override, false /* stop_at_first_match */,
1096                           false /* ignore_configuration */);
1097   if (!result.has_value()) {
1098     return base::unexpected(result.error());
1099   }
1100 
1101   auto result_map_entry = std::get_if<incfs::verified_map_ptr<ResTable_map_entry>>(&result->entry);
1102   if (result_map_entry != nullptr) {
1103     if (!may_be_bag) {
1104       LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
1105       return base::unexpected(std::nullopt);
1106     }
1107 
1108     // Create a reference since we can't represent this complex type as a Res_value.
1109     return SelectedValue(Res_value::TYPE_REFERENCE, resid, result->cookie, result->type_flags,
1110                          resid, result->config);
1111   }
1112 
1113   // Convert the package ID to the runtime assigned package ID.
1114   Res_value value = std::get<Res_value>(result->entry);
1115   result->dynamic_ref_table->lookupResourceValue(&value);
1116 
1117   return SelectedValue(value.dataType, value.data, result->cookie, result->type_flags,
1118                        resid, result->config);
1119 }
1120 
ResolveReference(AssetManager2::SelectedValue & value,bool cache_value) const1121 base::expected<std::monostate, NullOrIOError> AssetManager2::ResolveReference(
1122     AssetManager2::SelectedValue& value, bool cache_value) const {
1123   if (value.type != Res_value::TYPE_REFERENCE || value.data == 0U) {
1124     // Not a reference. Nothing to do.
1125     return {};
1126   }
1127 
1128   const uint32_t original_flags = value.flags;
1129   const uint32_t original_resid = value.data;
1130   if (cache_value) {
1131     auto cached_value = cached_resolved_values_.find(value.data);
1132     if (cached_value != cached_resolved_values_.end()) {
1133       value = cached_value->second;
1134       value.flags |= original_flags;
1135       return {};
1136     }
1137   }
1138 
1139   uint32_t combined_flags = 0U;
1140   uint32_t resolve_resid = original_resid;
1141   constexpr const uint32_t kMaxIterations = 20;
1142   for (uint32_t i = 0U;; i++) {
1143     auto result = GetResource(resolve_resid, true /*may_be_bag*/);
1144     if (!result.has_value()) {
1145       value.resid = resolve_resid;
1146       return base::unexpected(result.error());
1147     }
1148 
1149     // If resource resolution fails, the value should be set to the last reference that was able to
1150     // be resolved successfully.
1151     value = *result;
1152     value.flags |= combined_flags;
1153 
1154     if (result->type != Res_value::TYPE_REFERENCE ||
1155         result->data == Res_value::DATA_NULL_UNDEFINED ||
1156         result->data == resolve_resid || i == kMaxIterations) {
1157       // This reference can't be resolved, so exit now and let the caller deal with it.
1158       if (cache_value) {
1159         cached_resolved_values_[original_resid] = value;
1160       }
1161 
1162       // Above value is cached without original_flags to ensure they don't get included in future
1163       // queries that hit the cache
1164       value.flags |= original_flags;
1165       return {};
1166     }
1167 
1168     combined_flags = result->flags;
1169     resolve_resid = result->data;
1170   }
1171 }
1172 
GetBagResIdStack(uint32_t resid) const1173 base::expected<const std::vector<uint32_t>*, NullOrIOError> AssetManager2::GetBagResIdStack(
1174     uint32_t resid) const {
1175   auto it = cached_bag_resid_stacks_.find(resid);
1176   if (it != cached_bag_resid_stacks_.end()) {
1177     return &it->second;
1178   }
1179   std::vector<uint32_t> stacks;
1180   if (auto maybe_bag = GetBag(resid, stacks); UNLIKELY(IsIOError(maybe_bag))) {
1181     return base::unexpected(maybe_bag.error());
1182   }
1183 
1184   it = cached_bag_resid_stacks_.emplace(resid, std::move(stacks)).first;
1185   return &it->second;
1186 }
1187 
ResolveBag(AssetManager2::SelectedValue & value) const1188 base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::ResolveBag(
1189     AssetManager2::SelectedValue& value) const {
1190   if (UNLIKELY(value.type != Res_value::TYPE_REFERENCE)) {
1191     return base::unexpected(std::nullopt);
1192   }
1193 
1194   auto bag = GetBag(value.data);
1195   if (bag.has_value()) {
1196     value.flags |= (*bag)->type_spec_flags;
1197   }
1198   return bag;
1199 }
1200 
GetBag(uint32_t resid) const1201 base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(uint32_t resid) const {
1202   auto resid_stacks_it = cached_bag_resid_stacks_.find(resid);
1203   if (resid_stacks_it == cached_bag_resid_stacks_.end()) {
1204     resid_stacks_it = cached_bag_resid_stacks_.emplace(resid, std::vector<uint32_t>{}).first;
1205   }
1206   const auto bag = GetBag(resid, resid_stacks_it->second);
1207   if (UNLIKELY(IsIOError(bag))) {
1208     cached_bag_resid_stacks_.erase(resid_stacks_it);
1209     return base::unexpected(bag.error());
1210   }
1211   return bag;
1212 }
1213 
GetBag(uint32_t resid,std::vector<uint32_t> & child_resids) const1214 base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(
1215     uint32_t resid, std::vector<uint32_t>& child_resids) const {
1216   if (auto cached_iter = cached_bags_.find(resid); cached_iter != cached_bags_.end()) {
1217     return cached_iter->second.get();
1218   }
1219 
1220   auto entry = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */,
1221                          false /* ignore_configuration */);
1222   if (!entry.has_value()) {
1223     return base::unexpected(entry.error());
1224   }
1225 
1226   auto entry_map = std::get_if<incfs::verified_map_ptr<ResTable_map_entry>>(&entry->entry);
1227   if (entry_map == nullptr) {
1228     // Not a bag, nothing to do.
1229     return base::unexpected(std::nullopt);
1230   }
1231 
1232   auto map = *entry_map;
1233   auto map_entry = map.offset(dtohs(map->size)).convert<ResTable_map>();
1234   const auto map_entry_end = map_entry + dtohl(map->count);
1235 
1236   // Keep track of ids that have already been seen to prevent infinite loops caused by circular
1237   // dependencies between bags.
1238   child_resids.push_back(resid);
1239 
1240   uint32_t parent_resid = dtohl(map->parent.ident);
1241   if (parent_resid == 0U ||
1242       std::find(child_resids.begin(), child_resids.end(), parent_resid) != child_resids.end()) {
1243     // There is no parent or a circular parental dependency exist, meaning there is nothing to
1244     // inherit and we can do a simple copy of the entries in the map.
1245     const size_t entry_count = map_entry_end - map_entry;
1246     util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
1247         malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))};
1248 
1249     bool sort_entries = false;
1250     for (auto new_entry = new_bag->entries; map_entry != map_entry_end; ++map_entry) {
1251       if (UNLIKELY(!map_entry)) {
1252         return base::unexpected(IOError::PAGES_MISSING);
1253       }
1254 
1255       uint32_t new_key = dtohl(map_entry->name.ident);
1256       if (!is_internal_resid(new_key)) {
1257         // Attributes, arrays, etc don't have a resource id as the name. They specify
1258         // other data, which would be wrong to change via a lookup.
1259         if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR)) {
1260           LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key,
1261                                            resid);
1262           return base::unexpected(std::nullopt);
1263         }
1264       }
1265 
1266       new_entry->cookie = entry->cookie;
1267       new_entry->key = new_key;
1268       new_entry->key_pool = nullptr;
1269       new_entry->type_pool = nullptr;
1270       new_entry->style = resid;
1271       new_entry->value.copyFrom_dtoh(map_entry->value);
1272       status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value);
1273       if (UNLIKELY(err != NO_ERROR)) {
1274         LOG(ERROR) << base::StringPrintf(
1275             "Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType,
1276             new_entry->value.data, new_key);
1277         return base::unexpected(std::nullopt);
1278       }
1279 
1280       sort_entries = sort_entries ||
1281           (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key));
1282       ++new_entry;
1283     }
1284 
1285     if (sort_entries) {
1286       std::sort(new_bag->entries, new_bag->entries + entry_count,
1287                 [](auto&& lhs, auto&& rhs) { return lhs.key < rhs.key; });
1288     }
1289 
1290     new_bag->type_spec_flags = entry->type_flags;
1291     new_bag->entry_count = static_cast<uint32_t>(entry_count);
1292     ResolvedBag* result = new_bag.get();
1293     cached_bags_[resid] = std::move(new_bag);
1294     return result;
1295   }
1296 
1297   // In case the parent is a dynamic reference, resolve it.
1298   entry->dynamic_ref_table->lookupResourceId(&parent_resid);
1299 
1300   // Get the parent and do a merge of the keys.
1301   const auto parent_bag = GetBag(parent_resid, child_resids);
1302   if (UNLIKELY(!parent_bag.has_value())) {
1303     // Failed to get the parent that should exist.
1304     LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid,
1305                                      resid);
1306     return base::unexpected(parent_bag.error());
1307   }
1308 
1309   // Create the max possible entries we can make. Once we construct the bag,
1310   // we will realloc to fit to size.
1311   const size_t max_count = (*parent_bag)->entry_count + dtohl(map->count);
1312   util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
1313       malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry))))};
1314   ResolvedBag::Entry* new_entry = new_bag->entries;
1315 
1316   const ResolvedBag::Entry* parent_entry = (*parent_bag)->entries;
1317   const ResolvedBag::Entry* const parent_entry_end = parent_entry + (*parent_bag)->entry_count;
1318 
1319   // The keys are expected to be in sorted order. Merge the two bags.
1320   bool sort_entries = false;
1321   while (map_entry != map_entry_end && parent_entry != parent_entry_end) {
1322     if (UNLIKELY(!map_entry)) {
1323       return base::unexpected(IOError::PAGES_MISSING);
1324     }
1325 
1326     uint32_t child_key = dtohl(map_entry->name.ident);
1327     if (!is_internal_resid(child_key)) {
1328       if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR)) {
1329         LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key,
1330                                          resid);
1331         return base::unexpected(std::nullopt);
1332       }
1333     }
1334 
1335     if (child_key <= parent_entry->key) {
1336       // Use the child key if it comes before the parent
1337       // or is equal to the parent (overrides).
1338       new_entry->cookie = entry->cookie;
1339       new_entry->key = child_key;
1340       new_entry->key_pool = nullptr;
1341       new_entry->type_pool = nullptr;
1342       new_entry->value.copyFrom_dtoh(map_entry->value);
1343       new_entry->style = resid;
1344       status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value);
1345       if (UNLIKELY(err != NO_ERROR)) {
1346         LOG(ERROR) << base::StringPrintf(
1347             "Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType,
1348             new_entry->value.data, child_key);
1349         return base::unexpected(std::nullopt);
1350       }
1351       ++map_entry;
1352     } else {
1353       // Take the parent entry as-is.
1354       memcpy(new_entry, parent_entry, sizeof(*new_entry));
1355     }
1356 
1357     sort_entries = sort_entries ||
1358         (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key));
1359     if (child_key >= parent_entry->key) {
1360       // Move to the next parent entry if we used it or it was overridden.
1361       ++parent_entry;
1362     }
1363     // Increment to the next entry to fill.
1364     ++new_entry;
1365   }
1366 
1367   // Finish the child entries if they exist.
1368   while (map_entry != map_entry_end) {
1369     if (UNLIKELY(!map_entry)) {
1370       return base::unexpected(IOError::PAGES_MISSING);
1371     }
1372 
1373     uint32_t new_key = dtohl(map_entry->name.ident);
1374     if (!is_internal_resid(new_key)) {
1375       if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR)) {
1376         LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key,
1377                                          resid);
1378         return base::unexpected(std::nullopt);
1379       }
1380     }
1381     new_entry->cookie = entry->cookie;
1382     new_entry->key = new_key;
1383     new_entry->key_pool = nullptr;
1384     new_entry->type_pool = nullptr;
1385     new_entry->value.copyFrom_dtoh(map_entry->value);
1386     new_entry->style = resid;
1387     status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value);
1388     if (UNLIKELY(err != NO_ERROR)) {
1389       LOG(ERROR) << base::StringPrintf("Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.",
1390                                        new_entry->value.dataType, new_entry->value.data, new_key);
1391       return base::unexpected(std::nullopt);
1392     }
1393     sort_entries = sort_entries ||
1394         (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key));
1395     ++map_entry;
1396     ++new_entry;
1397   }
1398 
1399   // Finish the parent entries if they exist.
1400   if (parent_entry != parent_entry_end) {
1401     // Take the rest of the parent entries as-is.
1402     const size_t num_entries_to_copy = parent_entry_end - parent_entry;
1403     memcpy(new_entry, parent_entry, num_entries_to_copy * sizeof(*new_entry));
1404     new_entry += num_entries_to_copy;
1405   }
1406 
1407   // Resize the resulting array to fit.
1408   const size_t actual_count = new_entry - new_bag->entries;
1409   if (actual_count != max_count) {
1410     new_bag.reset(reinterpret_cast<ResolvedBag*>(realloc(
1411         new_bag.release(), sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry)))));
1412   }
1413 
1414   if (sort_entries) {
1415     std::sort(new_bag->entries, new_bag->entries + actual_count,
1416               [](auto&& lhs, auto&& rhs) { return lhs.key < rhs.key; });
1417   }
1418 
1419   // Combine flags from the parent and our own bag.
1420   new_bag->type_spec_flags = entry->type_flags | (*parent_bag)->type_spec_flags;
1421   new_bag->entry_count = static_cast<uint32_t>(actual_count);
1422   ResolvedBag* result = new_bag.get();
1423   cached_bags_[resid] = std::move(new_bag);
1424   return result;
1425 }
1426 
Utf8ToUtf16(StringPiece str,std::u16string * out)1427 static bool Utf8ToUtf16(StringPiece str, std::u16string* out) {
1428   ssize_t len =
1429       utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str.data()), str.size(), false);
1430   if (len < 0) {
1431     return false;
1432   }
1433   out->resize(static_cast<size_t>(len));
1434   utf8_to_utf16(reinterpret_cast<const uint8_t*>(str.data()), str.size(), &*out->begin(),
1435                 static_cast<size_t>(len + 1));
1436   return true;
1437 }
1438 
GetResourceId(const std::string & resource_name,const std::string & fallback_type,const std::string & fallback_package) const1439 base::expected<uint32_t, NullOrIOError> AssetManager2::GetResourceId(
1440     const std::string& resource_name, const std::string& fallback_type,
1441     const std::string& fallback_package) const {
1442   StringPiece package_name, type, entry;
1443   if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) {
1444     return base::unexpected(std::nullopt);
1445   }
1446 
1447   if (entry.empty()) {
1448     return base::unexpected(std::nullopt);
1449   }
1450 
1451   if (package_name.empty()) {
1452     package_name = fallback_package;
1453   }
1454 
1455   if (type.empty()) {
1456     type = fallback_type;
1457   }
1458 
1459   std::u16string type16;
1460   if (!Utf8ToUtf16(type, &type16)) {
1461     return base::unexpected(std::nullopt);
1462   }
1463 
1464   std::u16string entry16;
1465   if (!Utf8ToUtf16(entry, &entry16)) {
1466     return base::unexpected(std::nullopt);
1467   }
1468 
1469   const StringPiece16 kAttr16 = u"attr";
1470   const static std::u16string kAttrPrivate16 = u"^attr-private";
1471 
1472   for (const PackageGroup& package_group : package_groups_) {
1473     for (const ConfiguredPackage& package_impl : package_group.packages_) {
1474       const LoadedPackage* package = package_impl.loaded_package_;
1475       if (package_name != package->GetPackageName()) {
1476         // All packages in the same group are expected to have the same package name.
1477         break;
1478       }
1479 
1480       base::expected<uint32_t, NullOrIOError> resid = package->FindEntryByName(type16, entry16);
1481       if (UNLIKELY(IsIOError(resid))) {
1482          return base::unexpected(resid.error());
1483        }
1484 
1485       if (!resid.has_value() && kAttr16 == type16) {
1486         // Private attributes in libraries (such as the framework) are sometimes encoded
1487         // under the type '^attr-private' in order to leave the ID space of public 'attr'
1488         // free for future additions. Check '^attr-private' for the same name.
1489         resid = package->FindEntryByName(kAttrPrivate16, entry16);
1490       }
1491 
1492       if (resid.has_value()) {
1493         return fix_package_id(*resid, package_group.dynamic_ref_table->mAssignedPackageId);
1494       }
1495     }
1496   }
1497   return base::unexpected(std::nullopt);
1498 }
1499 
RebuildFilterList()1500 void AssetManager2::RebuildFilterList() {
1501   for (PackageGroup& group : package_groups_) {
1502     for (ConfiguredPackage& package : group.packages_) {
1503       package.filtered_configs_.forEachItem([](auto, auto& fcg) { fcg.type_entries.clear(); });
1504       // Create the filters here.
1505       package.loaded_package_->ForEachTypeSpec([&](const TypeSpec& type_spec, uint8_t type_id) {
1506         FilteredConfigGroup* group = nullptr;
1507         for (const auto& type_entry : type_spec.type_entries) {
1508           for (auto & config : configurations_) {
1509             if (type_entry.config.match(config)) {
1510               if (!group) {
1511                 group = &package.filtered_configs_.editItemAt(type_id - 1);
1512               }
1513               group->type_entries.push_back(&type_entry);
1514               break;
1515             }
1516           }
1517         }
1518       });
1519       package.filtered_configs_.trimBuckets(
1520           [](const auto& fcg) { return fcg.type_entries.empty(); });
1521     }
1522   }
1523 }
1524 
InvalidateCaches(uint32_t diff)1525 void AssetManager2::InvalidateCaches(uint32_t diff) {
1526   cached_resolved_values_.clear();
1527 
1528   if (diff == 0xffffffffu) {
1529     // Everything must go.
1530     cached_bags_.clear();
1531     cached_bag_resid_stacks_.clear();
1532     return;
1533   }
1534 
1535   // Be more conservative with what gets purged. Only if the bag has other possible
1536   // variations with respect to what changed (diff) should we remove it.
1537   for (auto stack_it = cached_bag_resid_stacks_.begin();
1538        stack_it != cached_bag_resid_stacks_.end();) {
1539     const auto it = cached_bags_.find(stack_it->first);
1540     if (it == cached_bags_.end()) {
1541       stack_it = cached_bag_resid_stacks_.erase(stack_it);
1542     } else if ((diff & it->second->type_spec_flags) != 0) {
1543       cached_bags_.erase(it);
1544       stack_it = cached_bag_resid_stacks_.erase(stack_it);
1545     } else {
1546       ++stack_it;  // Keep the item in both caches.
1547     }
1548   }
1549 
1550   // Need to ensure that both bag caches are consistent, as we populate them in the same function.
1551   // Iterate over the cached bags to erase the items without the corresponding resid_stack cache
1552   // items.
1553   for (auto it = cached_bags_.begin(); it != cached_bags_.end();) {
1554     if ((diff & it->second->type_spec_flags) != 0) {
1555       it = cached_bags_.erase(it);
1556     } else {
1557       ++it;
1558     }
1559   }
1560 }
1561 
GetAssignedPackageId(const LoadedPackage * package) const1562 uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const {
1563   for (auto& package_group : package_groups_) {
1564     for (auto& package2 : package_group.packages_) {
1565       if (package2.loaded_package_ == package) {
1566         return package_group.dynamic_ref_table->mAssignedPackageId;
1567       }
1568     }
1569   }
1570   return 0;
1571 }
1572 
NewTheme()1573 std::unique_ptr<Theme> AssetManager2::NewTheme() {
1574   constexpr size_t kInitialReserveSize = 32;
1575   auto theme = std::unique_ptr<Theme>(new Theme(this));
1576   theme->keys_.reserve(kInitialReserveSize);
1577   theme->entries_.reserve(kInitialReserveSize);
1578   return theme;
1579 }
1580 
ForEachPackage(base::function_ref<bool (const std::string &,uint8_t)> func,package_property_t excluded_property_flags) const1581 void AssetManager2::ForEachPackage(base::function_ref<bool(const std::string&, uint8_t)> func,
1582                                    package_property_t excluded_property_flags) const {
1583   for (const PackageGroup& package_group : package_groups_) {
1584     const auto loaded_package = package_group.packages_.front().loaded_package_;
1585     if ((loaded_package->GetPropertyFlags() & excluded_property_flags) == 0U
1586         && !func(loaded_package->GetPackageName(),
1587                  package_group.dynamic_ref_table->mAssignedPackageId)) {
1588       return;
1589     }
1590   }
1591 }
1592 
StartOperation() const1593 AssetManager2::ScopedOperation AssetManager2::StartOperation() const {
1594   ++number_of_running_scoped_operations_;
1595   return ScopedOperation(*this);
1596 }
1597 
FinishOperation() const1598 void AssetManager2::FinishOperation() const {
1599   if (number_of_running_scoped_operations_ < 1) {
1600     ALOGW("Invalid FinishOperation() call when there's none happening");
1601     return;
1602   }
1603   if (--number_of_running_scoped_operations_ == 0) {
1604     for (auto&& [_, assets] : apk_assets_) {
1605       assets.clear();
1606     }
1607   }
1608 }
1609 
GetApkAssets(ApkAssetsCookie cookie) const1610 const AssetManager2::ApkAssetsPtr& AssetManager2::GetApkAssets(ApkAssetsCookie cookie) const {
1611   DCHECK(number_of_running_scoped_operations_ > 0) << "Must have an operation running";
1612 
1613   if (cookie < 0 || cookie >= apk_assets_.size()) {
1614     static const ApkAssetsPtr empty{};
1615     return empty;
1616   }
1617   auto& [wptr, res] = apk_assets_[cookie];
1618   if (!res) {
1619     res = wptr.promote();
1620   }
1621   return res;
1622 }
1623 
Theme(AssetManager2 * asset_manager)1624 Theme::Theme(AssetManager2* asset_manager) : asset_manager_(asset_manager) {
1625 }
1626 
1627 Theme::~Theme() = default;
1628 
IsUndefined(const Res_value & value)1629 static bool IsUndefined(const Res_value& value) {
1630   // DATA_NULL_EMPTY (@empty) is a valid resource value and DATA_NULL_UNDEFINED represents
1631   // an absence of a valid value.
1632   return value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY;
1633 }
1634 
ApplyStyle(uint32_t resid,bool force)1635 base::expected<std::monostate, NullOrIOError> Theme::ApplyStyle(uint32_t resid, bool force) {
1636   ATRACE_NAME("Theme::ApplyStyle");
1637 
1638   auto bag = asset_manager_->GetBag(resid);
1639   if (!bag.has_value()) {
1640     return base::unexpected(bag.error());
1641   }
1642 
1643   // Merge the flags from this style.
1644   type_spec_flags_ |= (*bag)->type_spec_flags;
1645 
1646   //
1647   // This function is the most expensive part of applying an frro to the existing app resources,
1648   // and needs to be as efficient as possible.
1649   // The data structure we're working with is two parallel sorted arrays of keys (resource IDs)
1650   // and entries (resource value + some attributes).
1651   // The styles get applied in sequence, starting with an empty set of attributes. Each style
1652   // contains its values for the theme attributes, and gets applied in either normal or forced way:
1653   //  - normal way never overrides the existing attribute, so only unique style attributes are added
1654   //  - forced way overrides anything for that attribute, and if it's undefined it removes the
1655   //    previous value completely
1656   //
1657   // Style attributes come in a Bag data type - a sorted array of attributes with their values. This
1658   // means we don't need to re-sort the attributes ever, and instead:
1659   //  - for an already existing attribute just skip it or apply the forced value
1660   //    - if the forced value is undefined, mark it undefined as well to get rid of it later
1661   //  - for a new attribute append it to the array, forming a new sorted section of new attributes
1662   //    past the end of the original ones (ignore undefined ones here)
1663   //  - inplace merge two sorted sections to form a single sorted array again.
1664   //  - run the last pass to remove all undefined elements
1665   //
1666   // Using this algorithm performs better than a repeated binary search + insert in the middle,
1667   // as that keeps shifting the tail end of the arrays and wasting CPU cycles in memcpy().
1668   //
1669   const auto starting_size = keys_.size();
1670   if (starting_size == 0) {
1671     keys_.reserve((*bag)->entry_count);
1672     entries_.reserve((*bag)->entry_count);
1673   }
1674   bool wrote_undefined = false;
1675   for (auto it = begin(*bag); it != end(*bag); ++it) {
1676     const uint32_t attr_res_id = it->key;
1677     // If the resource ID passed in is not a style, the key can be some other identifier that is not
1678     // a resource ID. We should fail fast instead of operating with strange resource IDs.
1679     if (!is_valid_resid(attr_res_id)) {
1680       return base::unexpected(std::nullopt);
1681     }
1682     const bool is_undefined = IsUndefined(it->value);
1683     if (!force && is_undefined) {
1684       continue;
1685     }
1686     const auto key_it = std::lower_bound(keys_.begin(), keys_.begin() + starting_size, attr_res_id);
1687     if (key_it != keys_.begin() + starting_size && *key_it == attr_res_id) {
1688       const auto entry_it = entries_.begin() + (key_it - keys_.begin());
1689       if (force || IsUndefined(entry_it->value)) {
1690         *entry_it = Entry{it->cookie, (*bag)->type_spec_flags, it->value};
1691         wrote_undefined |= is_undefined;
1692       }
1693     } else if (!is_undefined) {
1694       keys_.emplace_back(attr_res_id);
1695       entries_.emplace_back(it->cookie, (*bag)->type_spec_flags, it->value);
1696     }
1697   }
1698 
1699   if (starting_size && keys_.size() != starting_size) {
1700     std::inplace_merge(
1701         CombinedIterator(keys_.begin(), entries_.begin()),
1702         CombinedIterator(keys_.begin() + starting_size, entries_.begin() + starting_size),
1703         CombinedIterator(keys_.end(), entries_.end()));
1704   }
1705   if (wrote_undefined) {
1706     auto new_end = std::remove_if(CombinedIterator(keys_.begin(), entries_.begin()),
1707                                   CombinedIterator(keys_.end(), entries_.end()),
1708                                   [](const auto& pair) { return IsUndefined(pair.second.value); });
1709     keys_.erase(new_end.it1, keys_.end());
1710     entries_.erase(new_end.it2, entries_.end());
1711   }
1712   if (android::base::kEnableDChecks && !std::is_sorted(keys_.begin(), keys_.end())) {
1713     ALOGW("Bag %u was unsorted in the apk?", unsigned(resid));
1714     return base::unexpected(std::nullopt);
1715   }
1716   return {};
1717 }
1718 
Rebase(AssetManager2 * am,const uint32_t * style_ids,const uint8_t * force,size_t style_count)1719 void Theme::Rebase(AssetManager2* am, const uint32_t* style_ids, const uint8_t* force,
1720                    size_t style_count) {
1721   ATRACE_NAME("Theme::Rebase");
1722   // Reset the entries without changing the vector capacity to prevent reallocations during
1723   // ApplyStyle.
1724   keys_.clear();
1725   entries_.clear();
1726   asset_manager_ = am;
1727   for (size_t i = 0; i < style_count; i++) {
1728     ApplyStyle(style_ids[i], force[i]);
1729   }
1730 }
1731 
GetAttribute(uint32_t resid) const1732 std::optional<AssetManager2::SelectedValue> Theme::GetAttribute(uint32_t resid) const {
1733   constexpr const uint32_t kMaxIterations = 20;
1734   uint32_t type_spec_flags = 0u;
1735   for (uint32_t i = 0; i <= kMaxIterations; i++) {
1736     const auto key_it = std::lower_bound(keys_.begin(), keys_.end(), resid);
1737     if (key_it == keys_.end() || *key_it != resid) {
1738       return std::nullopt;
1739     }
1740     const auto entry_it = entries_.begin() + (key_it - keys_.begin());
1741     if (IsUndefined(entry_it->value)) {
1742       return std::nullopt;
1743     }
1744     type_spec_flags |= entry_it->type_spec_flags;
1745     if (entry_it->value.dataType == Res_value::TYPE_ATTRIBUTE) {
1746       resid = entry_it->value.data;
1747       continue;
1748     }
1749 
1750     return AssetManager2::SelectedValue(entry_it->value.dataType, entry_it->value.data,
1751                                         entry_it->cookie, type_spec_flags, 0U /* resid */,
1752                                         {} /* config */);
1753   }
1754   return std::nullopt;
1755 }
1756 
ResolveAttributeReference(AssetManager2::SelectedValue & value) const1757 base::expected<std::monostate, NullOrIOError> Theme::ResolveAttributeReference(
1758       AssetManager2::SelectedValue& value) const {
1759   if (value.type != Res_value::TYPE_ATTRIBUTE) {
1760     return asset_manager_->ResolveReference(value);
1761   }
1762 
1763   std::optional<AssetManager2::SelectedValue> result = GetAttribute(value.data);
1764   if (!result.has_value()) {
1765     return base::unexpected(std::nullopt);
1766   }
1767 
1768   auto resolve_result = asset_manager_->ResolveReference(*result, true /* cache_value */);
1769   if (resolve_result.has_value()) {
1770     result->flags |= value.flags;
1771     value = *result;
1772   }
1773   return resolve_result;
1774 }
1775 
Clear()1776 void Theme::Clear() {
1777   keys_.clear();
1778   entries_.clear();
1779 }
1780 
SetTo(const Theme & source)1781 base::expected<std::monostate, IOError> Theme::SetTo(const Theme& source) {
1782   if (this == &source) {
1783     return {};
1784   }
1785 
1786   type_spec_flags_ = source.type_spec_flags_;
1787 
1788   if (asset_manager_ == source.asset_manager_) {
1789     keys_ = source.keys_;
1790     entries_ = source.entries_;
1791   } else {
1792     std::unordered_map<ApkAssetsCookie, ApkAssetsCookie> src_to_dest_asset_cookies;
1793     using SourceToDestinationRuntimePackageMap = std::unordered_map<int, int>;
1794     std::unordered_map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map;
1795 
1796     auto op_src = source.asset_manager_->StartOperation();
1797     auto op_dst = asset_manager_->StartOperation();
1798 
1799     for (size_t i = 0; i < source.asset_manager_->GetApkAssetsCount(); i++) {
1800       const auto& src_asset = source.asset_manager_->GetApkAssets(i);
1801       if (!src_asset) {
1802         continue;
1803       }
1804       for (int j = 0; j < asset_manager_->GetApkAssetsCount(); j++) {
1805         const auto& dest_asset = asset_manager_->GetApkAssets(j);
1806         if (src_asset != dest_asset) {
1807           // ResourcesManager caches and reuses ApkAssets when the same apk must be present in
1808           // multiple AssetManagers. Two ApkAssets point to the same version of the same resources
1809           // if they are the same instance.
1810           continue;
1811         }
1812 
1813         // Map the package ids of the asset in the source AssetManager to the package ids of the
1814         // asset in th destination AssetManager.
1815         SourceToDestinationRuntimePackageMap package_map;
1816         for (const auto& loaded_package : src_asset->GetLoadedArsc()->GetPackages()) {
1817           const int src_package_id = source.asset_manager_->GetAssignedPackageId(
1818               loaded_package.get());
1819           const int dest_package_id = asset_manager_->GetAssignedPackageId(loaded_package.get());
1820           package_map[src_package_id] = dest_package_id;
1821         }
1822 
1823         src_to_dest_asset_cookies.insert(std::make_pair(i, j));
1824         src_asset_cookie_id_map.insert(std::make_pair(i, std::move(package_map)));
1825         break;
1826       }
1827     }
1828 
1829     // Reset the data in the destination theme.
1830     keys_.clear();
1831     entries_.clear();
1832 
1833     for (size_t i = 0, size = source.entries_.size(); i != size; ++i) {
1834       const auto& entry = source.entries_[i];
1835       bool is_reference = (entry.value.dataType == Res_value::TYPE_ATTRIBUTE
1836                            || entry.value.dataType == Res_value::TYPE_REFERENCE
1837                            || entry.value.dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE
1838                            || entry.value.dataType == Res_value::TYPE_DYNAMIC_REFERENCE)
1839                           && entry.value.data != 0x0;
1840 
1841       // If the attribute value represents an attribute or reference, the package id of the
1842       // value needs to be rewritten to the package id of the value in the destination.
1843       uint32_t attribute_data = entry.value.data;
1844       if (is_reference) {
1845         // Determine the package id of the reference in the destination AssetManager.
1846         auto value_package_map = src_asset_cookie_id_map.find(entry.cookie);
1847         if (value_package_map == src_asset_cookie_id_map.end()) {
1848           continue;
1849         }
1850 
1851         auto value_dest_package = value_package_map->second.find(
1852             get_package_id(entry.value.data));
1853         if (value_dest_package == value_package_map->second.end()) {
1854           continue;
1855         }
1856 
1857         attribute_data = fix_package_id(entry.value.data, value_dest_package->second);
1858       }
1859 
1860       // Find the cookie of the value in the destination. If the source apk is not loaded in the
1861       // destination, only copy resources that do not reference resources in the source.
1862       ApkAssetsCookie data_dest_cookie;
1863       auto value_dest_cookie = src_to_dest_asset_cookies.find(entry.cookie);
1864       if (value_dest_cookie != src_to_dest_asset_cookies.end()) {
1865         data_dest_cookie = value_dest_cookie->second;
1866       } else {
1867         if (is_reference || entry.value.dataType == Res_value::TYPE_STRING) {
1868           continue;
1869         } else {
1870           data_dest_cookie = 0x0;
1871         }
1872       }
1873 
1874       const auto source_res_id = source.keys_[i];
1875 
1876       // The package id of the attribute needs to be rewritten to the package id of the
1877       // attribute in the destination.
1878       int attribute_dest_package_id = get_package_id(source_res_id);
1879       if (attribute_dest_package_id != 0x01) {
1880         // Find the cookie of the attribute resource id in the source AssetManager
1881         base::expected<FindEntryResult, NullOrIOError> attribute_entry_result =
1882             source.asset_manager_->FindEntry(source_res_id, 0 /* density_override */ ,
1883                                              true /* stop_at_first_match */,
1884                                              true /* ignore_configuration */);
1885         if (UNLIKELY(IsIOError(attribute_entry_result))) {
1886           return base::unexpected(GetIOError(attribute_entry_result.error()));
1887         }
1888         if (!attribute_entry_result.has_value()) {
1889           continue;
1890         }
1891 
1892         // Determine the package id of the attribute in the destination AssetManager.
1893         auto attribute_package_map = src_asset_cookie_id_map.find(
1894             attribute_entry_result->cookie);
1895         if (attribute_package_map == src_asset_cookie_id_map.end()) {
1896           continue;
1897         }
1898         auto attribute_dest_package = attribute_package_map->second.find(
1899             attribute_dest_package_id);
1900         if (attribute_dest_package == attribute_package_map->second.end()) {
1901           continue;
1902         }
1903         attribute_dest_package_id = attribute_dest_package->second;
1904       }
1905 
1906       auto dest_attr_id = make_resid(attribute_dest_package_id, get_type_id(source_res_id),
1907                                      get_entry_id(source_res_id));
1908       const auto key_it = std::lower_bound(keys_.begin(), keys_.end(), dest_attr_id);
1909       const auto entry_it = entries_.begin() + (key_it - keys_.begin());
1910       // Since the entries were cleared, the attribute resource id has yet been mapped to any value.
1911       keys_.insert(key_it, dest_attr_id);
1912       entries_.insert(entry_it, Entry{data_dest_cookie, entry.type_spec_flags,
1913                                       Res_value{.dataType = entry.value.dataType,
1914                                                 .data = attribute_data}});
1915     }
1916   }
1917   return {};
1918 }
1919 
Dump() const1920 void Theme::Dump() const {
1921   LOG(INFO) << base::StringPrintf("Theme(this=%p, AssetManager2=%p)", this, asset_manager_);
1922   for (size_t i = 0, size = keys_.size(); i != size; ++i) {
1923     auto res_id = keys_[i];
1924     const auto& entry = entries_[i];
1925     LOG(INFO) << base::StringPrintf("  entry(0x%08x)=(0x%08x) type=(0x%02x), cookie(%d)",
1926                                     res_id, entry.value.data, entry.value.dataType,
1927                                     entry.cookie);
1928   }
1929 }
1930 
ScopedOperation(const AssetManager2 & am)1931 AssetManager2::ScopedOperation::ScopedOperation(const AssetManager2& am) : am_(am) {
1932 }
1933 
~ScopedOperation()1934 AssetManager2::ScopedOperation::~ScopedOperation() {
1935   am_.FinishOperation();
1936 }
1937 
1938 }  // namespace android
1939