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