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