diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2021-01-14 07:20:17 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2021-01-14 07:20:17 +0000 |
commit | 8d845b02c9712a8fa1b5abd563a0dee7e521e513 (patch) | |
tree | 8e5eb364d0fd1d25f095fa6274965288ebf44525 /libs/androidfw/AssetManager2.cpp | |
parent | 41903dfea1cd83fdc3bf452c6fed6dddce04412b (diff) | |
parent | 14e8ade9a91ea48cb65b3e14954e13a396e1a94d (diff) |
Merge "Optimize FilterApkAssets by caching config"
Diffstat (limited to 'libs/androidfw/AssetManager2.cpp')
-rw-r--r-- | libs/androidfw/AssetManager2.cpp | 274 |
1 files changed, 105 insertions, 169 deletions
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 3f0600040139..03ab62f48870 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -103,10 +103,10 @@ AssetManager2::AssetManager2() { } bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, - bool invalidate_caches, bool filter_incompatible_configs) { + bool invalidate_caches) { apk_assets_ = apk_assets; BuildDynamicRefTable(); - RebuildFilterList(filter_incompatible_configs); + RebuildFilterList(); if (invalidate_caches) { InvalidateCaches(static_cast<uint32_t>(-1)); } @@ -623,7 +623,8 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry( if (UNLIKELY(logging_enabled)) { last_resolution_.steps.push_back( Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->config.toString(), - overlay_result->package_name}); + overlay_result->package_name, + overlay_result->cookie}); } } } @@ -646,22 +647,21 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( const LoadedPackage* best_package = nullptr; incfs::verified_map_ptr<ResTable_type> best_type; const ResTable_config* best_config = nullptr; - ResTable_config best_config_copy; uint32_t best_offset = 0U; uint32_t type_flags = 0U; - auto resolution_type = Resolution::Step::Type::NO_ENTRY; std::vector<Resolution::Step> resolution_steps; - // If desired_config is the same as the set configuration, then we can use our filtered list - // and we don't need to match the configurations, since they already matched. - const bool use_fast_path = !ignore_configuration && &desired_config == &configuration_; + // If `desired_config` is not the same as the set configuration or the caller will accept a value + // from any configuration, then we cannot use our filtered list of types since it only it contains + // types matched to the set configuration. + const bool use_filtered = !ignore_configuration && &desired_config == &configuration_; const size_t package_count = package_group.packages_.size(); for (size_t pi = 0; pi < package_count; pi++) { const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_; - ApkAssetsCookie cookie = package_group.cookies_[pi]; + const ApkAssetsCookie cookie = package_group.cookies_[pi]; // If the type IDs are offset in this package, we need to take that into account when searching // for a type. @@ -670,130 +670,82 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( continue; } + // Allow custom loader packages to overlay resource values with configurations equivalent to the + // current best configuration. + const bool package_is_loader = loaded_package->IsCustomLoader(); + auto entry_flags = type_spec->GetFlagsForEntryIndex(entry_idx); if (UNLIKELY(!entry_flags.has_value())) { return base::unexpected(entry_flags.error()); } type_flags |= entry_flags.value(); - // If the package is an overlay or custom loader, - // then even configurations that are the same MUST be chosen. - const bool package_is_loader = loaded_package->IsCustomLoader(); - - if (use_fast_path) { - const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; - for (const auto& type_config : filtered_group.type_configs) { - const ResTable_config& this_config = type_config.config; - - // We can skip calling ResTable_config::match() because we know that all candidate - // configurations that do NOT match have been filtered-out. - if (best_config == nullptr) { - resolution_type = Resolution::Step::Type::INITIAL; - } else if (this_config.isBetterThan(*best_config, &desired_config)) { - resolution_type = (package_is_loader) ? Resolution::Step::Type::BETTER_MATCH_LOADER - : Resolution::Step::Type::BETTER_MATCH; - } else if (package_is_loader && this_config.compare(*best_config) == 0) { - resolution_type = Resolution::Step::Type::OVERLAID_LOADER; - } else { - if (UNLIKELY(logging_enabled)) { - resolution_type = (package_is_loader) ? Resolution::Step::Type::SKIPPED_LOADER - : Resolution::Step::Type::SKIPPED; - resolution_steps.push_back(Resolution::Step{resolution_type, - this_config.toString(), - &loaded_package->GetPackageName()}); - } - continue; - } - - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const auto& type = type_config.type; - const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx); - if (UNLIKELY(IsIOError(offset))) { - return base::unexpected(offset.error()); - } - if (!offset.has_value()) { - if (UNLIKELY(logging_enabled)) { - if (package_is_loader) { - resolution_type = Resolution::Step::Type::NO_ENTRY_LOADER; - } else { - resolution_type = Resolution::Step::Type::NO_ENTRY; - } - resolution_steps.push_back(Resolution::Step{resolution_type, - this_config.toString(), - &loaded_package->GetPackageName()}); - } - continue; - } - - best_cookie = cookie; - best_package = loaded_package; - best_type = type; - best_config = &this_config; - best_offset = offset.value(); + const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; + const size_t type_entry_count = (use_filtered) ? filtered_group.type_entries.size() + : type_spec->type_entries.size(); + for (size_t i = 0; i < type_entry_count; i++) { + const TypeSpec::TypeEntry* type_entry = (use_filtered) ? filtered_group.type_entries[i] + : &type_spec->type_entries[i]; + + // We can skip calling ResTable_config::match() if the caller does not care for the + // configuration to match or if we're using the list of types that have already had their + // configuration matched. + const ResTable_config& this_config = type_entry->config; + if (!(use_filtered || ignore_configuration || this_config.match(desired_config))) { + continue; + } + Resolution::Step::Type resolution_type; + if (best_config == nullptr) { + resolution_type = Resolution::Step::Type::INITIAL; + } else if (this_config.isBetterThan(*best_config, &desired_config)) { + resolution_type = Resolution::Step::Type::BETTER_MATCH; + } else if (package_is_loader && this_config.compare(*best_config) == 0) { + resolution_type = Resolution::Step::Type::OVERLAID; + } else { if (UNLIKELY(logging_enabled)) { - last_resolution_.steps.push_back(Resolution::Step{resolution_type, - this_config.toString(), - &loaded_package->GetPackageName()}); + resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::SKIPPED, + this_config.toString(), + &loaded_package->GetPackageName(), + cookie}); } + continue; } - } else { - // This is the slower path, which doesn't use the filtered list of configurations. - // Here we must read the ResTable_config from the mmapped APK, convert it to host endianness - // and fill in any new fields that did not exist when the APK was compiled. - // Furthermore when selecting configurations we can't just record the pointer to the - // ResTable_config, we must copy it. - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - const incfs::verified_map_ptr<ResTable_type>& type = *iter; - - ResTable_config this_config{}; - if (!ignore_configuration) { - this_config.copyFromDtoH(type->config); - if (!this_config.match(desired_config)) { - continue; - } - if (best_config == nullptr) { - resolution_type = Resolution::Step::Type::INITIAL; - } else if (this_config.isBetterThan(*best_config, &desired_config)) { - resolution_type = (package_is_loader) ? Resolution::Step::Type::BETTER_MATCH_LOADER - : Resolution::Step::Type::BETTER_MATCH; - } else if (package_is_loader && this_config.compare(*best_config) == 0) { - resolution_type = Resolution::Step::Type::OVERLAID_LOADER; - } else { - continue; - } - } + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const auto& type = type_entry->type; + const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx); + if (UNLIKELY(IsIOError(offset))) { + return base::unexpected(offset.error()); + } - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx); - if (UNLIKELY(IsIOError(offset))) { - return base::unexpected(offset.error()); - } - if (!offset.has_value()) { - continue; + if (!offset.has_value()) { + if (UNLIKELY(logging_enabled)) { + resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::NO_ENTRY, + this_config.toString(), + &loaded_package->GetPackageName(), + cookie}); } + continue; + } - best_cookie = cookie; - best_package = loaded_package; - best_type = type; - best_config_copy = this_config; - best_config = &best_config_copy; - best_offset = offset.value(); + best_cookie = cookie; + best_package = loaded_package; + best_type = type; + best_config = &this_config; + best_offset = offset.value(); - if (stop_at_first_match) { - // Any configuration will suffice, so break. - break; - } + if (UNLIKELY(logging_enabled)) { + last_resolution_.steps.push_back(Resolution::Step{resolution_type, + this_config.toString(), + &loaded_package->GetPackageName(), + cookie}); + } - if (UNLIKELY(logging_enabled)) { - last_resolution_.steps.push_back(Resolution::Step{resolution_type, - this_config.toString(), - &loaded_package->GetPackageName()}); - } + // Any configuration will suffice, so break. + if (stop_at_first_match) { + break; } } } @@ -851,19 +803,16 @@ std::string AssetManager2::GetLastResourceResolution() const { return {}; } - auto cookie = last_resolution_.cookie; + const ApkAssetsCookie cookie = last_resolution_.cookie; if (cookie == kInvalidCookie) { LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path."; return {}; } - uint32_t resid = last_resolution_.resid; - std::vector<Resolution::Step>& steps = last_resolution_.steps; - std::string resource_name_string; - - const LoadedPackage* package = - apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); + const uint32_t resid = last_resolution_.resid; + const auto package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); + std::string resource_name_string; if (package != nullptr) { auto resource_name = ToResourceName(last_resolution_.type_string_ref, last_resolution_.entry_string_ref, @@ -878,44 +827,24 @@ std::string AssetManager2::GetLastResourceResolution() const { << "\n\tFor config -" << configuration_.toString(); - std::string prefix; - for (Resolution::Step step : steps) { - switch (step.type) { - case Resolution::Step::Type::INITIAL: - prefix = "Found initial"; - break; - case Resolution::Step::Type::BETTER_MATCH: - prefix = "Found better"; - break; - case Resolution::Step::Type::BETTER_MATCH_LOADER: - prefix = "Found better in loader"; - break; - case Resolution::Step::Type::OVERLAID: - prefix = "Overlaid"; - break; - case Resolution::Step::Type::OVERLAID_LOADER: - prefix = "Overlaid by loader"; - break; - case Resolution::Step::Type::SKIPPED: - prefix = "Skipped"; - break; - case Resolution::Step::Type::SKIPPED_LOADER: - prefix = "Skipped loader"; - break; - case Resolution::Step::Type::NO_ENTRY: - prefix = "No entry"; - break; - case Resolution::Step::Type::NO_ENTRY_LOADER: - prefix = "No entry for loader"; - break; - } + for (const Resolution::Step& step : last_resolution_.steps) { + const static std::unordered_map<Resolution::Step::Type, const char*> kStepStrings = { + {Resolution::Step::Type::INITIAL, "Found initial"}, + {Resolution::Step::Type::BETTER_MATCH, "Found better"}, + {Resolution::Step::Type::OVERLAID, "Overlaid"}, + {Resolution::Step::Type::SKIPPED, "Skipped"}, + {Resolution::Step::Type::NO_ENTRY, "No entry"} + }; - if (!prefix.empty()) { - log_stream << "\n\t" << prefix << ": " << *step.package_name; + const auto prefix = kStepStrings.find(step.type); + if (prefix == kStepStrings.end()) { + continue; + } - if (!step.config_name.isEmpty()) { - log_stream << " -" << step.config_name; - } + log_stream << "\n\t" << prefix->second << ": " << *step.package_name << " (" + << apk_assets_[step.cookie]->GetPath() << ")"; + if (!step.config_name.isEmpty()) { + log_stream << " -" << step.config_name; } } @@ -935,6 +864,16 @@ base::expected<AssetManager2::ResourceName, NullOrIOError> AssetManager2::GetRes *result->package_name); } +base::expected<uint32_t, NullOrIOError> AssetManager2::GetResourceTypeSpecFlags( + uint32_t resid) const { + auto result = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, + true /* ignore_configuration */); + if (!result.has_value()) { + return base::unexpected(result.error()); + } + return result->type_flags; +} + base::expected<AssetManager2::SelectedValue, NullOrIOError> AssetManager2::GetResource( uint32_t resid, bool may_be_bag, uint16_t density_override) const { auto result = FindEntry(resid, density_override, false /* stop_at_first_match */, @@ -1333,7 +1272,7 @@ base::expected<uint32_t, NullOrIOError> AssetManager2::GetResourceId( return base::unexpected(std::nullopt); } -void AssetManager2::RebuildFilterList(bool filter_incompatible_configs) { +void AssetManager2::RebuildFilterList() { for (PackageGroup& group : package_groups_) { for (ConfiguredPackage& impl : group.packages_) { // Destroy it. @@ -1343,14 +1282,11 @@ void AssetManager2::RebuildFilterList(bool filter_incompatible_configs) { new (&impl.filtered_configs_) ByteBucketArray<FilteredConfigGroup>(); // Create the filters here. - impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec* spec, uint8_t type_index) { - FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_index); - const auto iter_end = spec->types + spec->type_count; - for (auto iter = spec->types; iter != iter_end; ++iter) { - ResTable_config this_config; - this_config.copyFromDtoH((*iter)->config); - if (!filter_incompatible_configs || this_config.match(configuration_)) { - group.type_configs.push_back(TypeConfig{*iter, this_config}); + impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec& type_spec, uint8_t type_id) { + FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_id - 1); + for (const auto& type_entry : type_spec.type_entries) { + if (type_entry.config.match(configuration_)) { + group.type_entries.push_back(&type_entry); } } }); |