diff options
author | Ryan Mitchell <rtmitchell@google.com> | 2021-06-09 16:35:35 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2021-06-09 16:35:35 +0000 |
commit | 015bca79f459dedc003b0bcdce004c22ca1ae55f (patch) | |
tree | ceb41c802a01c9ff5c1393b4c6e5b287cb5fa64e /libs/androidfw/AssetManager2.cpp | |
parent | b9be267361e558b711bbbc01441755253433d779 (diff) | |
parent | 7b8091ad7c0292bc17c666a9eae27a2e0f7ee4da (diff) |
Merge changes I2710cfea,I03fb31ee,Iec512b31 into sc-dev
* changes:
ResourcesImpl.ThemeImpl NativeAllocationRegistry
Rebase ThemeImpl rather than reallocate memory
Sparse native theme representation
Diffstat (limited to 'libs/androidfw/AssetManager2.cpp')
-rw-r--r-- | libs/androidfw/AssetManager2.cpp | 434 |
1 files changed, 147 insertions, 287 deletions
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 3a0153fa3dd8..0cde3d1242c8 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -1345,7 +1345,10 @@ uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const } std::unique_ptr<Theme> AssetManager2::NewTheme() { - return std::unique_ptr<Theme>(new Theme(this)); + constexpr size_t kInitialReserveSize = 32; + auto theme = std::unique_ptr<Theme>(new Theme(this)); + theme->entries_.reserve(kInitialReserveSize); + return theme; } Theme::Theme(AssetManager2* asset_manager) : asset_manager_(asset_manager) { @@ -1353,28 +1356,20 @@ Theme::Theme(AssetManager2* asset_manager) : asset_manager_(asset_manager) { Theme::~Theme() = default; -namespace { - -struct ThemeEntry { +struct Theme::Entry { + uint32_t attr_res_id; 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; +namespace { +struct ThemeEntryKeyComparer { + bool operator() (const Theme::Entry& entry, uint32_t attr_res_id) const noexcept { + return entry.attr_res_id < attr_res_id; + } }; +} // namespace base::expected<std::monostate, NullOrIOError> Theme::ApplyStyle(uint32_t resid, bool force) { ATRACE_NAME("Theme::ApplyStyle"); @@ -1387,116 +1382,74 @@ base::expected<std::monostate, NullOrIOError> Theme::ApplyStyle(uint32_t resid, // Merge the flags from this style. type_spec_flags_ |= (*bag)->type_spec_flags; - 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 rbegin = reverse_bag_iterator(begin(*bag)); - for (auto it = reverse_bag_iterator(end(*bag)); it != rbegin; ++it) { - const uint32_t attr_resid = it->key; + for (auto it = begin(*bag); it != end(*bag); ++it) { + const uint32_t attr_res_id = it->key; // 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)) { + if (!is_valid_resid(attr_res_id)) { return base::unexpected(std::nullopt); } - // 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); - - 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; + // DATA_NULL_EMPTY (@empty) is a valid resource value and DATA_NULL_UNDEFINED represents + // an absence of a valid value. + bool is_undefined = it->value.dataType == Res_value::TYPE_NULL && + it->value.data != Res_value::DATA_NULL_EMPTY; + if (!force && is_undefined) { + continue; } - 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; + Theme::Entry new_entry{attr_res_id, it->cookie, (*bag)->type_spec_flags, it->value}; + auto entry_it = std::lower_bound(entries_.begin(), entries_.end(), attr_res_id, + ThemeEntryKeyComparer{}); + if (entry_it != entries_.end() && entry_it->attr_res_id == attr_res_id) { + if (is_undefined) { + // DATA_NULL_UNDEFINED clears the value of the attribute in the theme only when `force` is + /// true. + entries_.erase(entry_it); + } else if (force) { + *entry_it = new_entry; } - last_type_idx = type_idx; - last_type = type.get(); - } - - 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 = it->cookie; - entry.type_spec_flags |= (*bag)->type_spec_flags; - entry.value = it->value; + } else { + entries_.insert(entry_it, new_entry); } } return {}; } +void Theme::Rebase(AssetManager2* am, const uint32_t* style_ids, const uint8_t* force, + size_t style_count) { + ATRACE_NAME("Theme::Rebase"); + // Reset the entries without changing the vector capacity to prevent reallocations during + // ApplyStyle. + entries_.clear(); + asset_manager_ = am; + for (size_t i = 0; i < style_count; i++) { + ApplyStyle(style_ids[i], force[i]); + } +} + std::optional<AssetManager2::SelectedValue> Theme::GetAttribute(uint32_t resid) const { - int cnt = 20; + constexpr const uint32_t kMaxIterations = 20; uint32_t type_spec_flags = 0u; - do { - const int package_idx = get_package_id(resid); - const Package* package = packages_[package_idx].get(); - 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 std::nullopt; - } - - // @null is different than @empty. - if (entry.value.dataType == Res_value::TYPE_NULL && - entry.value.data != Res_value::DATA_NULL_EMPTY) { - return std::nullopt; - } + for (uint32_t i = 0; i <= kMaxIterations; i++) { + auto entry_it = std::lower_bound(entries_.begin(), entries_.end(), resid, + ThemeEntryKeyComparer{}); + if (entry_it == entries_.end() || entry_it->attr_res_id != resid) { + return std::nullopt; + } - return AssetManager2::SelectedValue(entry.value.dataType, entry.value.data, entry.cookie, - type_spec_flags, 0U /* resid */, {} /* config */); - } - } + type_spec_flags |= entry_it->type_spec_flags; + if (entry_it->value.dataType == Res_value::TYPE_ATTRIBUTE) { + resid = entry_it->value.data; + continue; } - break; - } while (true); + + return AssetManager2::SelectedValue(entry_it->value.dataType, entry_it->value.data, + entry_it->cookie, type_spec_flags, 0U /* resid */, + {} /* config */); + } return std::nullopt; } @@ -1520,56 +1473,25 @@ base::expected<std::monostate, NullOrIOError> Theme::ResolveAttributeReference( } void Theme::Clear() { - type_spec_flags_ = 0u; - for (std::unique_ptr<Package>& package : packages_) { - package.reset(); - } + entries_.clear(); } -base::expected<std::monostate, IOError> Theme::SetTo(const Theme& o) { - if (this == &o) { +base::expected<std::monostate, IOError> Theme::SetTo(const Theme& source) { + if (this == &source) { return {}; } - type_spec_flags_ = o.type_spec_flags_; - - if (asset_manager_ == o.asset_manager_) { - // The theme comes from the same asset manager so all theme data can be copied exactly - for (size_t p = 0; p < packages_.size(); p++) { - const Package *package = o.packages_[p].get(); - if (package == nullptr) { - // 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 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; - } + type_spec_flags_ = source.type_spec_flags_; - // 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<ThemeType *>(copied_data)); - } - } + if (asset_manager_ == source.asset_manager_) { + entries_ = source.entries_; } else { std::map<ApkAssetsCookie, ApkAssetsCookie> src_to_dest_asset_cookies; typedef std::map<int, int> SourceToDestinationRuntimePackageMap; std::map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map; // Determine which ApkAssets are loaded in both theme AssetManagers. - const auto src_assets = o.asset_manager_->GetApkAssets(); + const auto src_assets = source.asset_manager_->GetApkAssets(); for (size_t i = 0; i < src_assets.size(); i++) { const ApkAssets* src_asset = src_assets[i]; @@ -1587,7 +1509,8 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& o) { // asset in th destination AssetManager. SourceToDestinationRuntimePackageMap package_map; for (const auto& loaded_package : src_asset->GetLoadedArsc()->GetPackages()) { - const int src_package_id = o.asset_manager_->GetAssignedPackageId(loaded_package.get()); + const int src_package_id = source.asset_manager_->GetAssignedPackageId( + loaded_package.get()); const int dest_package_id = asset_manager_->GetAssignedPackageId(loaded_package.get()); package_map[src_package_id] = dest_package_id; } @@ -1599,130 +1522,88 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& o) { } // Reset the data in the destination theme. - for (size_t p = 0; p < packages_.size(); p++) { - if (packages_[p] != nullptr) { - packages_[p].reset(); - } - } - - for (size_t p = 0; p < packages_.size(); p++) { - const Package *package = o.packages_[p].get(); - if (package == nullptr) { - continue; - } - - for (size_t t = 0; t < package->types.size(); t++) { - const ThemeType *type = package->types[t].get(); - if (type == nullptr) { + entries_.clear(); + + for (const auto& entry : source.entries_) { + bool is_reference = (entry.value.dataType == Res_value::TYPE_ATTRIBUTE + || entry.value.dataType == Res_value::TYPE_REFERENCE + || entry.value.dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE + || entry.value.dataType == Res_value::TYPE_DYNAMIC_REFERENCE) + && entry.value.data != 0x0; + + // If the attribute value represents an attribute or reference, the package id of the + // value needs to be rewritten to the package id of the value in the destination. + uint32_t attribute_data = entry.value.data; + if (is_reference) { + // Determine the package id of the reference in the destination AssetManager. + auto value_package_map = src_asset_cookie_id_map.find(entry.cookie); + if (value_package_map == src_asset_cookie_id_map.end()) { continue; } - for (size_t e = 0; e < type->entry_count; e++) { - const ThemeEntry &entry = type->entries[e]; - if (entry.value.dataType == Res_value::TYPE_NULL && - entry.value.data != Res_value::DATA_NULL_EMPTY) { - continue; - } - - bool is_reference = (entry.value.dataType == Res_value::TYPE_ATTRIBUTE - || entry.value.dataType == Res_value::TYPE_REFERENCE - || entry.value.dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE - || entry.value.dataType == Res_value::TYPE_DYNAMIC_REFERENCE) - && entry.value.data != 0x0; - - // If the attribute value represents an attribute or reference, the package id of the - // value needs to be rewritten to the package id of the value in the destination. - uint32_t attribute_data = entry.value.data; - if (is_reference) { - // Determine the package id of the reference in the destination AssetManager. - auto value_package_map = src_asset_cookie_id_map.find(entry.cookie); - if (value_package_map == src_asset_cookie_id_map.end()) { - continue; - } - - auto value_dest_package = value_package_map->second.find( - get_package_id(entry.value.data)); - if (value_dest_package == value_package_map->second.end()) { - continue; - } - - attribute_data = fix_package_id(entry.value.data, value_dest_package->second); - } - - // Find the cookie of the value in the destination. If the source apk is not loaded in the - // destination, only copy resources that do not reference resources in the source. - ApkAssetsCookie data_dest_cookie; - auto value_dest_cookie = src_to_dest_asset_cookies.find(entry.cookie); - if (value_dest_cookie != src_to_dest_asset_cookies.end()) { - data_dest_cookie = value_dest_cookie->second; - } else { - if (is_reference || entry.value.dataType == Res_value::TYPE_STRING) { - continue; - } else { - data_dest_cookie = 0x0; - } - } + auto value_dest_package = value_package_map->second.find( + get_package_id(entry.value.data)); + if (value_dest_package == value_package_map->second.end()) { + continue; + } - // The package id of the attribute needs to be rewritten to the package id of the - // attribute in the destination. - int attribute_dest_package_id = p; - if (attribute_dest_package_id != 0x01) { - // Find the cookie of the attribute resource id in the source AssetManager - base::expected<FindEntryResult, NullOrIOError> attribute_entry_result = - o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ , - true /* stop_at_first_match */, - true /* ignore_configuration */); - if (UNLIKELY(IsIOError(attribute_entry_result))) { - return base::unexpected(GetIOError(attribute_entry_result.error())); - } - if (!attribute_entry_result.has_value()) { - continue; - } - - // Determine the package id of the attribute in the destination AssetManager. - auto attribute_package_map = src_asset_cookie_id_map.find( - attribute_entry_result->cookie); - if (attribute_package_map == src_asset_cookie_id_map.end()) { - continue; - } - auto attribute_dest_package = attribute_package_map->second.find( - attribute_dest_package_id); - if (attribute_dest_package == attribute_package_map->second.end()) { - continue; - } - attribute_dest_package_id = attribute_dest_package->second; - } + attribute_data = fix_package_id(entry.value.data, value_dest_package->second); + } - // Lazily instantiate the destination package. - std::unique_ptr<Package>& dest_package = packages_[attribute_dest_package_id]; - if (dest_package == nullptr) { - dest_package.reset(new Package()); - } + // Find the cookie of the value in the destination. If the source apk is not loaded in the + // destination, only copy resources that do not reference resources in the source. + ApkAssetsCookie data_dest_cookie; + auto value_dest_cookie = src_to_dest_asset_cookies.find(entry.cookie); + if (value_dest_cookie != src_to_dest_asset_cookies.end()) { + data_dest_cookie = value_dest_cookie->second; + } else { + if (is_reference || entry.value.dataType == Res_value::TYPE_STRING) { + continue; + } else { + data_dest_cookie = 0x0; + } + } - // Lazily instantiate and resize the destination type. - util::unique_cptr<ThemeType>& dest_type = dest_package->types[t]; - if (dest_type == nullptr || dest_type->entry_count < type->entry_count) { - const size_t type_alloc_size = sizeof(ThemeType) - + (type->entry_count * sizeof(ThemeEntry)); - void* dest_data = malloc(type_alloc_size); - memset(dest_data, 0, type->entry_count * sizeof(ThemeEntry)); - - // Copy the existing destination type values if the type is resized. - if (dest_type != nullptr) { - memcpy(dest_data, type, sizeof(ThemeType) - + (dest_type->entry_count * sizeof(ThemeEntry))); - } - - dest_type.reset(reinterpret_cast<ThemeType *>(dest_data)); - dest_type->entry_count = type->entry_count; - } + // The package id of the attribute needs to be rewritten to the package id of the + // attribute in the destination. + int attribute_dest_package_id = get_package_id(entry.attr_res_id); + if (attribute_dest_package_id != 0x01) { + // Find the cookie of the attribute resource id in the source AssetManager + base::expected<FindEntryResult, NullOrIOError> attribute_entry_result = + source.asset_manager_->FindEntry(entry.attr_res_id, 0 /* density_override */ , + true /* stop_at_first_match */, + true /* ignore_configuration */); + if (UNLIKELY(IsIOError(attribute_entry_result))) { + return base::unexpected(GetIOError(attribute_entry_result.error())); + } + if (!attribute_entry_result.has_value()) { + continue; + } - dest_type->entries[e].cookie = data_dest_cookie; - dest_type->entries[e].value.dataType = entry.value.dataType; - dest_type->entries[e].value.data = attribute_data; - dest_type->entries[e].type_spec_flags = entry.type_spec_flags; + // Determine the package id of the attribute in the destination AssetManager. + auto attribute_package_map = src_asset_cookie_id_map.find( + attribute_entry_result->cookie); + if (attribute_package_map == src_asset_cookie_id_map.end()) { + continue; + } + auto attribute_dest_package = attribute_package_map->second.find( + attribute_dest_package_id); + if (attribute_dest_package == attribute_package_map->second.end()) { + continue; } + attribute_dest_package_id = attribute_dest_package->second; } + + auto dest_attr_id = make_resid(attribute_dest_package_id, get_type_id(entry.attr_res_id), + get_entry_id(entry.attr_res_id)); + Theme::Entry new_entry{dest_attr_id, data_dest_cookie, entry.type_spec_flags, + Res_value{.dataType = entry.value.dataType, + .data = attribute_data}}; + + // Since the entries were cleared, the attribute resource id has yet been mapped to any value. + auto entry_it = std::lower_bound(entries_.begin(), entries_.end(), dest_attr_id, + ThemeEntryKeyComparer{}); + entries_.insert(entry_it, new_entry); } } return {}; @@ -1730,31 +1611,10 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& o) { void Theme::Dump() const { LOG(INFO) << base::StringPrintf("Theme(this=%p, AssetManager2=%p)", this, asset_manager_); - - for (int p = 0; p < packages_.size(); p++) { - auto& package = packages_[p]; - if (package == nullptr) { - continue; - } - - for (int t = 0; t < package->types.size(); t++) { - auto& type = package->types[t]; - if (type == nullptr) { - continue; - } - - for (int e = 0; e < type->entry_count; e++) { - auto& entry = type->entries[e]; - if (entry.value.dataType == Res_value::TYPE_NULL && - entry.value.data != Res_value::DATA_NULL_EMPTY) { - continue; - } - - LOG(INFO) << base::StringPrintf(" entry(0x%08x)=(0x%08x) type=(0x%02x), cookie(%d)", - make_resid(p, t, e), entry.value.data, - entry.value.dataType, entry.cookie); - } - } + for (auto& entry : entries_) { + LOG(INFO) << base::StringPrintf(" entry(0x%08x)=(0x%08x) type=(0x%02x), cookie(%d)", + entry.attr_res_id, entry.value.data, entry.value.dataType, + entry.cookie); } } |