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