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