From e3c1a4a9d21e7698e9e5196086198569ac5cc6cd Mon Sep 17 00:00:00 2001 From: David Chaloupka Date: Thu, 18 Jan 2018 13:44:36 +0000 Subject: [PATCH] Handle multiple packages of same name in 'aapt2 convert' aapt2 currently looks-up packages only by package name and then verifies whether the package ID has the expected value. For pre-L we need to be able to handle resource tables having packages of same package name but different IDs. Note that this CL fixes only proto->binary conversion but many other aapt2 commands are still affected. This is because many transformations still consider package name as sufficient identifier of a package. Bug: 72143207 Test: Manual Change-Id: Id8a920d6cd15bec747d3124270f5bcb7f48924cf --- tools/aapt2/ResourceTable.cpp | 23 +++++++++++++++++++ tools/aapt2/ResourceTable.h | 8 ++++++- tools/aapt2/format/proto/ProtoDeserialize.cpp | 3 ++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp index 9905f827d663b..95bf9210ba97f 100644 --- a/tools/aapt2/ResourceTable.cpp +++ b/tools/aapt2/ResourceTable.cpp @@ -47,6 +47,13 @@ static bool less_than_struct_with_name(const std::unique_ptr& lhs, const Stri return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0; } +template +static bool less_than_struct_with_name_and_id(const std::unique_ptr& lhs, + const std::pair>& rhs) { + int name_cmp = lhs->name.compare(0, lhs->name.size(), rhs.first.data(), rhs.first.size()); + return name_cmp < 0 || (name_cmp == 0 && lhs->id < rhs.second); +} + ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) const { const auto last = packages.end(); auto iter = std::lower_bound(packages.begin(), last, name, @@ -79,6 +86,22 @@ ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name, Mayb return package; } +ResourceTablePackage* ResourceTable::CreatePackageAllowingDuplicateNames(const StringPiece& name, + const Maybe id) { + const auto last = packages.end(); + auto iter = std::lower_bound(packages.begin(), last, std::make_pair(name, id), + less_than_struct_with_name_and_id); + + if (iter != last && name == (*iter)->name && id == (*iter)->id) { + return iter->get(); + } + + std::unique_ptr new_package = util::make_unique(); + new_package->name = name.to_string(); + new_package->id = id; + return packages.emplace(iter, std::move(new_package))->get(); +} + ResourceTablePackage* ResourceTable::FindOrCreatePackage(const StringPiece& name) { const auto last = packages.end(); auto iter = std::lower_bound(packages.begin(), last, name, diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h index 95e30c442042a..374fe1ea66a13 100644 --- a/tools/aapt2/ResourceTable.h +++ b/tools/aapt2/ResourceTable.h @@ -229,6 +229,11 @@ class ResourceTable { ResourceTablePackage* CreatePackage(const android::StringPiece& name, Maybe id = {}); + // Attempts to find a package having the specified name and ID. If not found, a new package + // of the specified parameters is created and returned. + ResourceTablePackage* CreatePackageAllowingDuplicateNames(const android::StringPiece& name, + const Maybe id); + std::unique_ptr Clone() const; // The string pool used by this resource table. Values that reference strings must use @@ -239,7 +244,8 @@ class ResourceTable { // destroyed. StringPool string_pool; - // The list of packages in this table, sorted alphabetically by package name. + // The list of packages in this table, sorted alphabetically by package name and increasing + // package ID (missing ID being the lowest). std::vector> packages; // Set of dynamic packages that this table may reference. Their package names get encoded diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp index d8635a989bed8..affaf68ec8fba 100644 --- a/tools/aapt2/format/proto/ProtoDeserialize.cpp +++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp @@ -380,7 +380,8 @@ static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStr std::map id_index; - ResourceTablePackage* pkg = out_table->CreatePackage(pb_package.package_name(), id); + ResourceTablePackage* pkg = + out_table->CreatePackageAllowingDuplicateNames(pb_package.package_name(), id); for (const pb::Type& pb_type : pb_package.type()) { const ResourceType* res_type = ParseResourceType(pb_type.name()); if (res_type == nullptr) {