diff options
Diffstat (limited to 'libs/androidfw/AssetManager2.cpp')
-rw-r--r-- | libs/androidfw/AssetManager2.cpp | 399 |
1 files changed, 201 insertions, 198 deletions
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 5667f9283241..415d3e36adf9 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -18,6 +18,7 @@ #include "androidfw/AssetManager2.h" +#include <iterator> #include <set> #include "android-base/logging.h" @@ -35,7 +36,9 @@ namespace android { -AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } +AssetManager2::AssetManager2() { + memset(&configuration_, 0, sizeof(configuration_)); +} bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches) { @@ -55,9 +58,9 @@ void AssetManager2::BuildDynamicRefTable() { int next_package_id = 0x02; const size_t apk_assets_count = apk_assets_.size(); for (size_t i = 0; i < apk_assets_count; i++) { - const ApkAssets* apk_asset = apk_assets_[i]; - for (const std::unique_ptr<const LoadedPackage>& package : - apk_asset->GetLoadedArsc()->GetPackages()) { + const LoadedArsc* loaded_arsc = apk_assets_[i]->GetLoadedArsc(); + + for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) { // Get the package ID or assign one if a shared library. int package_id; if (package->IsDynamic()) { @@ -261,9 +264,7 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, } ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, - bool stop_at_first_match, LoadedArscEntry* out_entry, - ResTable_config* out_selected_config, - uint32_t* out_flags) { + bool stop_at_first_match, FindEntryResult* out_entry) { ATRACE_CALL(); // Might use this if density_override != 0. @@ -292,29 +293,27 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri return kInvalidCookie; } - LoadedArscEntry best_entry; - ResTable_config best_config; + FindEntryResult best_entry; ApkAssetsCookie best_cookie = kInvalidCookie; uint32_t cumulated_flags = 0u; const PackageGroup& package_group = package_groups_[idx]; const size_t package_count = package_group.packages_.size(); + FindEntryResult current_entry; for (size_t i = 0; i < package_count; i++) { - LoadedArscEntry current_entry; - ResTable_config current_config; - uint32_t current_flags = 0; - const LoadedPackage* loaded_package = package_group.packages_[i]; - if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry, - ¤t_config, ¤t_flags)) { + if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry)) { continue; } - cumulated_flags |= current_flags; + cumulated_flags |= current_entry.type_flags; - if (best_cookie == kInvalidCookie || current_config.isBetterThan(best_config, desired_config)) { + const ResTable_config* current_config = current_entry.config; + const ResTable_config* best_config = best_entry.config; + if (best_cookie == kInvalidCookie || + current_config->isBetterThan(*best_config, desired_config) || + (loaded_package->IsOverlay() && current_config->compare(*best_config) == 0)) { best_entry = current_entry; - best_config = current_config; best_cookie = package_group.cookies_[i]; if (stop_at_first_match) { break; @@ -328,19 +327,16 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri *out_entry = best_entry; out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; - *out_selected_config = best_config; - *out_flags = cumulated_flags; + out_entry->type_flags = cumulated_flags; return best_cookie; } bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { ATRACE_CALL(); - LoadedArscEntry entry; - ResTable_config config; - uint32_t flags = 0u; - ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, - true /* stop_at_first_match */, &entry, &config, &flags); + FindEntryResult entry; + ApkAssetsCookie cookie = + FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */, &entry); if (cookie == kInvalidCookie) { return false; } @@ -374,11 +370,14 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { } bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { - LoadedArscEntry entry; - ResTable_config config; - ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, - false /* stop_at_first_match */, &entry, &config, out_flags); - return cookie != kInvalidCookie; + FindEntryResult entry; + ApkAssetsCookie cookie = + FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); + if (cookie != kInvalidCookie) { + *out_flags = entry.type_flags; + return cookie; + } + return kInvalidCookie; } ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, @@ -387,16 +386,14 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, uint32_t* out_flags) { ATRACE_CALL(); - LoadedArscEntry entry; - ResTable_config config; - uint32_t flags = 0u; + FindEntryResult entry; ApkAssetsCookie cookie = - FindEntry(resid, density_override, false /* stop_at_first_match */, &entry, &config, &flags); + FindEntry(resid, density_override, false /* stop_at_first_match */, &entry); if (cookie == kInvalidCookie) { return kInvalidCookie; } - if (dtohl(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) { + if (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) { if (!may_be_bag) { LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid); return kInvalidCookie; @@ -405,8 +402,8 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Create a reference since we can't represent this complex type as a Res_value. out_value->dataType = Res_value::TYPE_REFERENCE; out_value->data = resid; - *out_selected_config = config; - *out_flags = flags; + *out_selected_config = *entry.config; + *out_flags = entry.type_flags; return cookie; } @@ -417,8 +414,8 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Convert the package ID to the runtime assigned package ID. entry.dynamic_ref_table->lookupResourceValue(out_value); - *out_selected_config = config; - *out_flags = flags; + *out_selected_config = *entry.config; + *out_flags = entry.type_flags; return cookie; } @@ -429,7 +426,6 @@ ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_valu ATRACE_CALL(); constexpr const int kMaxIterations = 20; - *out_last_reference = 0u; for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && in_out_value->data != 0u && iteration < kMaxIterations; iteration++) { @@ -461,11 +457,9 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { return cached_iter->second.get(); } - LoadedArscEntry entry; - ResTable_config config; - uint32_t flags = 0u; - ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, - false /* stop_at_first_match */, &entry, &config, &flags); + FindEntryResult entry; + ApkAssetsCookie cookie = + FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); if (cookie == kInvalidCookie) { return nullptr; } @@ -503,13 +497,20 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { } } new_entry->cookie = cookie; - new_entry->value.copyFrom_dtoh(map_entry->value); new_entry->key = new_key; new_entry->key_pool = nullptr; new_entry->type_pool = nullptr; + new_entry->value.copyFrom_dtoh(map_entry->value); + status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value); + if (err != NO_ERROR) { + LOG(ERROR) << base::StringPrintf( + "Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType, + new_entry->value.data, new_key); + return nullptr; + } ++new_entry; } - new_bag->type_spec_flags = flags; + new_bag->type_spec_flags = entry.type_flags; new_bag->entry_count = static_cast<uint32_t>(entry_count); ResolvedBag* result = new_bag.get(); cached_bags_[resid] = std::move(new_bag); @@ -527,14 +528,11 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { return nullptr; } - // Combine flags from the parent and our own bag. - flags |= parent_bag->type_spec_flags; - // Create the max possible entries we can make. Once we construct the bag, // we will realloc to fit to size. const size_t max_count = parent_bag->entry_count + dtohl(map->count); - ResolvedBag* new_bag = reinterpret_cast<ResolvedBag*>( - malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry)))); + util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>( + malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry))))}; ResolvedBag::Entry* new_entry = new_bag->entries; const ResolvedBag::Entry* parent_entry = parent_bag->entries; @@ -554,10 +552,17 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { // Use the child key if it comes before the parent // or is equal to the parent (overrides). new_entry->cookie = cookie; - new_entry->value.copyFrom_dtoh(map_entry->value); new_entry->key = child_key; new_entry->key_pool = nullptr; new_entry->type_pool = nullptr; + new_entry->value.copyFrom_dtoh(map_entry->value); + status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value); + if (err != NO_ERROR) { + LOG(ERROR) << base::StringPrintf( + "Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType, + new_entry->value.data, child_key); + return nullptr; + } ++map_entry; } else { // Take the parent entry as-is. @@ -582,10 +587,16 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { } } new_entry->cookie = cookie; - new_entry->value.copyFrom_dtoh(map_entry->value); new_entry->key = new_key; new_entry->key_pool = nullptr; new_entry->type_pool = nullptr; + new_entry->value.copyFrom_dtoh(map_entry->value); + status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value); + if (err != NO_ERROR) { + LOG(ERROR) << base::StringPrintf("Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", + new_entry->value.dataType, new_entry->value.data, new_key); + return nullptr; + } ++map_entry; ++new_entry; } @@ -601,15 +612,15 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { // Resize the resulting array to fit. const size_t actual_count = new_entry - new_bag->entries; if (actual_count != max_count) { - new_bag = reinterpret_cast<ResolvedBag*>( - realloc(new_bag, sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry)))); + new_bag.reset(reinterpret_cast<ResolvedBag*>(realloc( + new_bag.release(), sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry))))); } - util::unique_cptr<ResolvedBag> final_bag{new_bag}; - final_bag->type_spec_flags = flags; - final_bag->entry_count = static_cast<uint32_t>(actual_count); - ResolvedBag* result = final_bag.get(); - cached_bags_[resid] = std::move(final_bag); + // Combine flags from the parent and our own bag. + new_bag->type_spec_flags = entry.type_flags | parent_bag->type_spec_flags; + new_bag->entry_count = static_cast<uint32_t>(actual_count); + ResolvedBag* result = new_bag.get(); + cached_bags_[resid] = std::move(new_bag); return result; } @@ -699,7 +710,37 @@ void AssetManager2::InvalidateCaches(uint32_t diff) { } } -std::unique_ptr<Theme> AssetManager2::NewTheme() { return std::unique_ptr<Theme>(new Theme(this)); } +std::unique_ptr<Theme> AssetManager2::NewTheme() { + return std::unique_ptr<Theme>(new Theme(this)); +} + +Theme::Theme(AssetManager2* asset_manager) : asset_manager_(asset_manager) { +} + +Theme::~Theme() = default; + +namespace { + +struct ThemeEntry { + ApkAssetsCookie cookie; + uint32_t type_spec_flags; + Res_value value; +}; + +struct ThemeType { + int entry_count; + ThemeEntry entries[0]; +}; + +constexpr size_t kTypeCount = std::numeric_limits<uint8_t>::max() + 1; + +} // namespace + +struct Theme::Package { + // Each element of Type will be a dynamically sized object + // allocated to have the entries stored contiguously with the Type. + std::array<util::unique_cptr<ThemeType>, kTypeCount> types; +}; bool Theme::ApplyStyle(uint32_t resid, bool force) { ATRACE_CALL(); @@ -712,71 +753,69 @@ bool Theme::ApplyStyle(uint32_t resid, bool force) { // Merge the flags from this style. type_spec_flags_ |= bag->type_spec_flags; - // On the first iteration, verify the attribute IDs and - // update the entry count in each type. - const auto bag_iter_end = end(bag); - for (auto bag_iter = begin(bag); bag_iter != bag_iter_end; ++bag_iter) { + int last_type_idx = -1; + int last_package_idx = -1; + Package* last_package = nullptr; + ThemeType* last_type = nullptr; + + // Iterate backwards, because each bag is sorted in ascending key ID order, meaning we will only + // need to perform one resize per type. + using reverse_bag_iterator = std::reverse_iterator<const ResolvedBag::Entry*>; + const auto bag_iter_end = reverse_bag_iterator(begin(bag)); + for (auto bag_iter = reverse_bag_iterator(end(bag)); bag_iter != bag_iter_end; ++bag_iter) { const uint32_t attr_resid = bag_iter->key; - // If the resource ID passed in is not a style, the key can be - // some other identifier that is not a resource ID. + // If the resource ID passed in is not a style, the key can be some other identifier that is not + // a resource ID. We should fail fast instead of operating with strange resource IDs. if (!is_valid_resid(attr_resid)) { return false; } - const uint32_t package_idx = get_package_id(attr_resid); - - // The type ID is 1-based, so subtract 1 to get an index. - const uint32_t type_idx = get_type_id(attr_resid) - 1; - const uint32_t entry_idx = get_entry_id(attr_resid); - - std::unique_ptr<Package>& package = packages_[package_idx]; - if (package == nullptr) { - package.reset(new Package()); - } - - util::unique_cptr<Type>& type = package->types[type_idx]; - if (type == nullptr) { - // Set the initial capacity to take up a total amount of 1024 bytes. - constexpr uint32_t kInitialCapacity = (1024u - sizeof(Type)) / sizeof(Entry); - const uint32_t initial_capacity = std::max(entry_idx, kInitialCapacity); - type.reset( - reinterpret_cast<Type*>(calloc(sizeof(Type) + (initial_capacity * sizeof(Entry)), 1))); - type->entry_capacity = initial_capacity; - } + // We don't use the 0-based index for the type so that we can avoid doing ID validation + // upon lookup. Instead, we keep space for the type ID 0 in our data structures. Since + // the construction of this type is guarded with a resource ID check, it will never be + // populated, and querying type ID 0 will always fail. + const int package_idx = get_package_id(attr_resid); + const int type_idx = get_type_id(attr_resid); + const int entry_idx = get_entry_id(attr_resid); - // Set the entry_count to include this entry. We will populate - // and resize the array as necessary in the next pass. - if (entry_idx + 1 > type->entry_count) { - // Increase the entry count to include this. - type->entry_count = entry_idx + 1; + if (last_package_idx != package_idx) { + std::unique_ptr<Package>& package = packages_[package_idx]; + if (package == nullptr) { + package.reset(new Package()); + } + last_package_idx = package_idx; + last_package = package.get(); + last_type_idx = -1; } - } - // On the second pass, we will realloc to fit the entry counts - // and populate the structures. - for (auto bag_iter = begin(bag); bag_iter != bag_iter_end; ++bag_iter) { - const uint32_t attr_resid = bag_iter->key; - const uint32_t package_idx = get_package_id(attr_resid); - const uint32_t type_idx = get_type_id(attr_resid) - 1; - const uint32_t entry_idx = get_entry_id(attr_resid); - Package* package = packages_[package_idx].get(); - util::unique_cptr<Type>& type = package->types[type_idx]; - if (type->entry_count != type->entry_capacity) { - // Resize to fit the actual entries that will be included. - Type* type_ptr = type.release(); - type.reset(reinterpret_cast<Type*>( - realloc(type_ptr, sizeof(Type) + (type_ptr->entry_count * sizeof(Entry))))); - if (type->entry_capacity < type->entry_count) { - // Clear the newly allocated memory (which does not get zero initialized). - // We need to do this because we |= type_spec_flags. - memset(type->entries + type->entry_capacity, 0, - sizeof(Entry) * (type->entry_count - type->entry_capacity)); + if (last_type_idx != type_idx) { + util::unique_cptr<ThemeType>& type = last_package->types[type_idx]; + if (type == nullptr) { + // Allocate enough memory to contain this entry_idx. Since we're iterating in reverse over + // a sorted list of attributes, this shouldn't be resized again during this method call. + type.reset(reinterpret_cast<ThemeType*>( + calloc(sizeof(ThemeType) + (entry_idx + 1) * sizeof(ThemeEntry), 1))); + type->entry_count = entry_idx + 1; + } else if (entry_idx >= type->entry_count) { + // Reallocate the memory to contain this entry_idx. Since we're iterating in reverse over + // a sorted list of attributes, this shouldn't be resized again during this method call. + const int new_count = entry_idx + 1; + type.reset(reinterpret_cast<ThemeType*>( + realloc(type.release(), sizeof(ThemeType) + (new_count * sizeof(ThemeEntry))))); + + // Clear out the newly allocated space (which isn't zeroed). + memset(type->entries + type->entry_count, 0, + (new_count - type->entry_count) * sizeof(ThemeEntry)); + type->entry_count = new_count; } - type->entry_capacity = type->entry_count; + last_type_idx = type_idx; + last_type = type.get(); } - Entry& entry = type->entries[entry_idx]; - if (force || entry.value.dataType == Res_value::TYPE_NULL) { + + ThemeEntry& entry = last_type->entries[entry_idx]; + if (force || (entry.value.dataType == Res_value::TYPE_NULL && + entry.value.data != Res_value::DATA_NULL_EMPTY)) { entry.cookie = bag_iter->cookie; entry.type_spec_flags |= bag->type_spec_flags; entry.value = bag_iter->value; @@ -787,88 +826,46 @@ bool Theme::ApplyStyle(uint32_t resid, bool force) { ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, uint32_t* out_flags) const { - constexpr const int kMaxIterations = 20; + int cnt = 20; uint32_t type_spec_flags = 0u; - for (int iterations_left = kMaxIterations; iterations_left > 0; iterations_left--) { - if (!is_valid_resid(resid)) { - return kInvalidCookie; - } - - const uint32_t package_idx = get_package_id(resid); - - // Type ID is 1-based, subtract 1 to get the index. - const uint32_t type_idx = get_type_id(resid) - 1; - const uint32_t entry_idx = get_entry_id(resid); - + do { + const int package_idx = get_package_id(resid); const Package* package = packages_[package_idx].get(); - if (package == nullptr) { - return kInvalidCookie; - } - - const Type* type = package->types[type_idx].get(); - if (type == nullptr) { - return kInvalidCookie; - } - - if (entry_idx >= type->entry_count) { - return kInvalidCookie; - } - - const Entry& entry = type->entries[entry_idx]; - type_spec_flags |= entry.type_spec_flags; - - switch (entry.value.dataType) { - case Res_value::TYPE_NULL: - return kInvalidCookie; - - case Res_value::TYPE_ATTRIBUTE: - resid = entry.value.data; - break; - - case Res_value::TYPE_DYNAMIC_ATTRIBUTE: { - // Resolve the dynamic attribute to a normal attribute - // (with the right package ID). - resid = entry.value.data; - const DynamicRefTable* ref_table = - asset_manager_->GetDynamicRefTableForPackage(package_idx); - if (ref_table == nullptr || ref_table->lookupResourceId(&resid) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve dynamic attribute 0x%08x", resid); - return kInvalidCookie; - } - } break; - - case Res_value::TYPE_DYNAMIC_REFERENCE: { - // Resolve the dynamic reference to a normal reference - // (with the right package ID). - out_value->dataType = Res_value::TYPE_REFERENCE; - out_value->data = entry.value.data; - const DynamicRefTable* ref_table = - asset_manager_->GetDynamicRefTableForPackage(package_idx); - if (ref_table == nullptr || ref_table->lookupResourceId(&out_value->data) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve dynamic reference 0x%08x", - out_value->data); - return kInvalidCookie; - } - - if (out_flags != nullptr) { + if (package != nullptr) { + // The themes are constructed with a 1-based type ID, so no need to decrement here. + const int type_idx = get_type_id(resid); + const ThemeType* type = package->types[type_idx].get(); + if (type != nullptr) { + const int entry_idx = get_entry_id(resid); + if (entry_idx < type->entry_count) { + const ThemeEntry& entry = type->entries[entry_idx]; + type_spec_flags |= entry.type_spec_flags; + + if (entry.value.dataType == Res_value::TYPE_ATTRIBUTE) { + if (cnt > 0) { + cnt--; + resid = entry.value.data; + continue; + } + return kInvalidCookie; + } + + // @null is different than @empty. + if (entry.value.dataType == Res_value::TYPE_NULL && + entry.value.data != Res_value::DATA_NULL_EMPTY) { + return kInvalidCookie; + } + + *out_value = entry.value; *out_flags = type_spec_flags; + return entry.cookie; } - return entry.cookie; } - - default: - *out_value = entry.value; - if (out_flags != nullptr) { - *out_flags = type_spec_flags; - } - return entry.cookie; } - } - - LOG(WARNING) << base::StringPrintf("Too many (%d) attribute references, stopped at: 0x%08x", - kMaxIterations, resid); + break; + } while (true); return kInvalidCookie; } @@ -903,30 +900,36 @@ bool Theme::SetTo(const Theme& o) { return true; } - if (asset_manager_ != o.asset_manager_) { - return false; - } - type_spec_flags_ = o.type_spec_flags_; + const bool copy_only_system = asset_manager_ != o.asset_manager_; + for (size_t p = 0; p < packages_.size(); p++) { const Package* package = o.packages_[p].get(); - if (package == nullptr) { + if (package == nullptr || (copy_only_system && p != 0x01)) { + // The other theme doesn't have this package, clear ours. packages_[p].reset(); continue; } + if (packages_[p] == nullptr) { + // The other theme has this package, but we don't. Make one. + packages_[p].reset(new Package()); + } + for (size_t t = 0; t < package->types.size(); t++) { - const Type* type = package->types[t].get(); + const ThemeType* type = package->types[t].get(); if (type == nullptr) { + // The other theme doesn't have this type, clear ours. packages_[p]->types[t].reset(); continue; } - const size_t type_alloc_size = sizeof(Type) + (type->entry_capacity * sizeof(Entry)); + // Create a new type and update it to theirs. + const size_t type_alloc_size = sizeof(ThemeType) + (type->entry_count * sizeof(ThemeEntry)); void* copied_data = malloc(type_alloc_size); memcpy(copied_data, type, type_alloc_size); - packages_[p]->types[t].reset(reinterpret_cast<Type*>(copied_data)); + packages_[p]->types[t].reset(reinterpret_cast<ThemeType*>(copied_data)); } } return true; |