From 2fedba9a32d9e92344eaf6e9faf5b43e1bc2ae70 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Fri, 23 Apr 2021 07:47:38 -0700 Subject: Add to aapt2 To allow apps that compiled against a pre-release SDK to continue working for a period of time after API finalization, a new tag, , has been added to aapt2. When finalizing the framework resource API, converting tags to will cause aapt2 to generate the resource table so that there is a resource entry for the old non-finalized (staged) resource ID and another entry for the finalized resource ID of newly finalized resources. This allows an application that compiled against the pre-release SDK to continue resolving resources using pre-release resource IDs. All references to pre-release resource IDs will be rewritten to their finalized resource IDs through the information stored in the new staged alias chunk. This allows applications compiled against resources to use the newly finalized resource ID without re-compilation. When an application is re-compiled against the SDK with tags, the application will use the finalized resource IDs. This change limits the use of the alias chunk to the framework for S. Bug: 183411356 Test: aapt2_test Change-Id: Iba1c3033c3c2f32de8e4a19b58d3921c971092c4 --- tools/aapt2/ResourceTable.cpp | 149 ++++++++++++++++++++++++++++++++---------- 1 file changed, 113 insertions(+), 36 deletions(-) (limited to 'tools/aapt2/ResourceTable.cpp') diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp index 45ea65430bb6..8ab1493c6ab3 100644 --- a/tools/aapt2/ResourceTable.cpp +++ b/tools/aapt2/ResourceTable.cpp @@ -83,6 +83,20 @@ T* FindElementsRunAction(const android::StringPiece& name, Elements& entries, Fu return action(found, iter); } +struct ConfigKey { + const ConfigDescription* config; + const StringPiece& product; +}; + +template +bool lt_config_key_ref(const T& lhs, const ConfigKey& rhs) { + int cmp = lhs->config.compare(*rhs.config); + if (cmp == 0) { + cmp = StringPiece(lhs->product).compare(rhs.product); + } + return cmp < 0; +} + } // namespace ResourceTable::ResourceTable(ResourceTable::Validation validation) : validation_(validation) { @@ -134,23 +148,10 @@ ResourceEntry* ResourceTableType::FindOrCreateEntry(const android::StringPiece& }); } -struct ConfigKey { - const ConfigDescription* config; - const StringPiece& product; -}; - -bool lt_config_key_ref(const std::unique_ptr& lhs, const ConfigKey& rhs) { - int cmp = lhs->config.compare(*rhs.config); - if (cmp == 0) { - cmp = StringPiece(lhs->product).compare(rhs.product); - } - return cmp < 0; -} - ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config, android::StringPiece product) { auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, - lt_config_key_ref); + lt_config_key_ref>); if (iter != values.end()) { ResourceConfigValue* value = iter->get(); if (value->config == config && StringPiece(value->product) == product) { @@ -163,7 +164,7 @@ ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config, const ResourceConfigValue* ResourceEntry::FindValue(const android::ConfigDescription& config, android::StringPiece product) const { auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, - lt_config_key_ref); + lt_config_key_ref>); if (iter != values.end()) { ResourceConfigValue* value = iter->get(); if (value->config == config && StringPiece(value->product) == product) { @@ -176,7 +177,7 @@ const ResourceConfigValue* ResourceEntry::FindValue(const android::ConfigDescrip ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& config, const StringPiece& product) { auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, - lt_config_key_ref); + lt_config_key_ref>); if (iter != values.end()) { ResourceConfigValue* value = iter->get(); if (value->config == config && StringPiece(value->product) == product) { @@ -296,6 +297,7 @@ ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* exist return CollisionResult::kConflict; } +namespace { template struct SortedVectorInserter : public Comparer { std::pair::iterator> LowerBound(std::vector& el, @@ -313,7 +315,7 @@ struct SortedVectorInserter : public Comparer { if (found) { return &*it; } - return &*el.insert(it, std::move(value)); + return &*el.insert(it, std::forward(value)); } }; @@ -331,35 +333,77 @@ struct TypeViewComparer { }; struct EntryViewComparer { - bool operator()(const ResourceEntry* lhs, const ResourceEntry* rhs) { - return less_than_struct_with_name_and_id( - *lhs, std::make_pair(rhs->name, rhs->id)); + bool operator()(const ResourceTableEntryView& lhs, const ResourceTableEntryView& rhs) { + return less_than_struct_with_name_and_id( + lhs, std::make_pair(rhs.name, rhs.id)); } }; -ResourceTableView ResourceTable::GetPartitionedView() const { - ResourceTableView view; +void InsertEntryIntoTableView(ResourceTableView& table, const ResourceTablePackage* package, + const ResourceTableType* type, const std::string& entry_name, + const Maybe& id, const Visibility& visibility, + const Maybe& allow_new, + const Maybe& overlayable_item, + const Maybe& staged_id, + const std::vector>& values) { SortedVectorInserter package_inserter; SortedVectorInserter type_inserter; - SortedVectorInserter entry_inserter; + SortedVectorInserter entry_inserter; + + ResourceTablePackageView new_package{package->name, + id ? id.value().package_id() : Maybe{}}; + auto view_package = package_inserter.Insert(table.packages, std::move(new_package)); + + ResourceTableTypeView new_type{type->type, id ? id.value().type_id() : Maybe{}}; + auto view_type = type_inserter.Insert(view_package->types, std::move(new_type)); + + if (visibility.level == Visibility::Level::kPublic) { + // Only mark the type visibility level as public, it doesn't care about being private. + view_type->visibility_level = Visibility::Level::kPublic; + } + + ResourceTableEntryView new_entry{.name = entry_name, + .id = id ? id.value().entry_id() : Maybe{}, + .visibility = visibility, + .allow_new = allow_new, + .overlayable_item = overlayable_item, + .staged_id = staged_id}; + for (auto& value : values) { + new_entry.values.emplace_back(value.get()); + } + + entry_inserter.Insert(view_type->entries, std::move(new_entry)); +} +} // namespace + +const ResourceConfigValue* ResourceTableEntryView::FindValue(const ConfigDescription& config, + android::StringPiece product) const { + auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, + lt_config_key_ref); + if (iter != values.end()) { + const ResourceConfigValue* value = *iter; + if (value->config == config && StringPiece(value->product) == product) { + return value; + } + } + return nullptr; +} +ResourceTableView ResourceTable::GetPartitionedView(const ResourceTableViewOptions& options) const { + ResourceTableView view; for (const auto& package : packages) { for (const auto& type : package->types) { for (const auto& entry : type->entries) { - ResourceTablePackageView new_package{ - package->name, entry->id ? entry->id.value().package_id() : Maybe{}}; - auto view_package = package_inserter.Insert(view.packages, std::move(new_package)); - - ResourceTableTypeView new_type{type->type, - entry->id ? entry->id.value().type_id() : Maybe{}}; - auto view_type = type_inserter.Insert(view_package->types, std::move(new_type)); - - if (entry->visibility.level == Visibility::Level::kPublic) { - // Only mark the type visibility level as public, it doesn't care about being private. - view_type->visibility_level = Visibility::Level::kPublic; + InsertEntryIntoTableView(view, package.get(), type.get(), entry->name, entry->id, + entry->visibility, entry->allow_new, entry->overlayable_item, + entry->staged_id, entry->values); + + if (options.create_alias_entries && entry->staged_id) { + auto alias_id = entry->staged_id.value().id; + InsertEntryIntoTableView(view, package.get(), type.get(), entry->name, alias_id, + entry->visibility, entry->allow_new, entry->overlayable_item, {}, + entry->values); } - - entry_inserter.Insert(view_type->entries, entry.get()); } } } @@ -368,6 +412,8 @@ ResourceTableView ResourceTable::GetPartitionedView() const { // for the same resource type within the same package. For this reason, if there are types with // multiple type ids, each type needs to exist in its own package in order to be queried by name. std::vector new_packages; + SortedVectorInserter package_inserter; + SortedVectorInserter type_inserter; for (auto& package : view.packages) { // If a new package was already created for a different type within this package, then // we can reuse those packages for other types that need to be extracted from this package. @@ -498,6 +544,10 @@ bool ResourceTable::AddResource(NewResource&& res, IDiagnostics* diag) { entry->allow_new = res.allow_new.value(); } + if (res.staged_id.has_value()) { + entry->staged_id = res.staged_id.value(); + } + if (res.value != nullptr) { auto config_value = entry->FindOrCreateValue(res.config, res.product); if (!config_value->value) { @@ -575,6 +625,28 @@ Maybe ResourceTable::FindResource(const ResourceNam return {}; } +bool ResourceTable::RemoveResource(const ResourceNameRef& name, ResourceId id) const { + ResourceTablePackage* package = FindPackage(name.package); + if (package == nullptr) { + return {}; + } + + ResourceTableType* type = package->FindType(name.type); + if (type == nullptr) { + return {}; + } + + auto entry_it = std::equal_range(type->entries.begin(), type->entries.end(), name.entry, + NameEqualRange{}); + for (auto it = entry_it.first; it != entry_it.second; ++it) { + if ((*it)->id == id) { + type->entries.erase(it); + return true; + } + } + return false; +} + std::unique_ptr ResourceTable::Clone() const { std::unique_ptr new_table = util::make_unique(); CloningValueTransformer cloner(&new_table->string_pool); @@ -640,6 +712,11 @@ NewResourceBuilder& NewResourceBuilder::SetAllowNew(AllowNew allow_new) { return *this; } +NewResourceBuilder& NewResourceBuilder::SetStagedId(StagedId staged_alias) { + res_.staged_id = std::move(staged_alias); + return *this; +} + NewResourceBuilder& NewResourceBuilder::SetAllowMangled(bool allow_mangled) { res_.allow_mangled = allow_mangled; return *this; -- cgit v1.2.3-59-g8ed1b