AAPT2: Add workaround for non-standard package IDs
The dynamic ref table used to map build-time IDs to runtime IDs
is mainly used for shared resource libraries and has a few built-in
mappings (app 0x7f and framework 0x01).
Using a non-standard package ID like 0x80 causes a failure in package ID
lookup. The solution is to ship the dynamic_ref_table with an identity mapping
with any resource table that uses a non-standard package ID.
Adds some tests to ensure this works correctly.
Bug: 37498913
Test: make libandroidfw_tests
Test: make aapt2_tests
Change-Id: Ic3f67942384d34e7fdcbc94ded360e940e3ebc8a
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 60b01e3..b872ebb 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -274,7 +274,13 @@
if (!attr.namespace_uri.empty()) {
std::cerr << attr.namespace_uri << ":";
}
- std::cerr << attr.name << "=" << attr.value << "\n";
+ std::cerr << attr.name;
+
+ if (attr.compiled_attribute) {
+ std::cerr << "(" << attr.compiled_attribute.value().id.value_or_default(ResourceId(0x0))
+ << ")";
+ }
+ std::cerr << "=" << attr.value << "\n";
}
const size_t previous_size = prefix_.size();
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp
index d44b3e0..f4d0226 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -573,10 +573,17 @@
// Write the ResTable header.
ChunkWriter table_writer(buffer_);
- ResTable_header* table_header =
- table_writer.StartChunk<ResTable_header>(RES_TABLE_TYPE);
+ ResTable_header* table_header = table_writer.StartChunk<ResTable_header>(RES_TABLE_TYPE);
table_header->packageCount = util::HostToDevice32(table->packages.size());
+ // Write a self mapping entry for this package if the ID is non-standard (0x7f).
+ if (context->GetPackageType() == PackageType::kApp) {
+ const uint8_t package_id = context->GetPackageId();
+ if (package_id != kFrameworkPackageId && package_id != kAppPackageId) {
+ table->included_packages_[package_id] = context->GetCompilationPackage();
+ }
+ }
+
// Flatten the values string pool.
StringPool::FlattenUtf8(table_writer.buffer(), table->string_pool);
diff --git a/tools/aapt2/flatten/TableFlattener_test.cpp b/tools/aapt2/flatten/TableFlattener_test.cpp
index 8dff3a2..6d1350d 100644
--- a/tools/aapt2/flatten/TableFlattener_test.cpp
+++ b/tools/aapt2/flatten/TableFlattener_test.cpp
@@ -400,7 +400,7 @@
const DynamicRefTable* dynamic_ref_table = result.getDynamicRefTableForCookie(1);
ASSERT_NE(nullptr, dynamic_ref_table);
- const KeyedVector<String16, uint8_t> entries = dynamic_ref_table->entries();
+ const KeyedVector<String16, uint8_t>& entries = dynamic_ref_table->entries();
ssize_t idx = entries.indexOfKey(android::String16("lib_one"));
ASSERT_GE(idx, 0);
@@ -411,6 +411,26 @@
EXPECT_EQ(0x03u, entries.valueAt(idx));
}
+TEST_F(TableFlattenerTest, PackageWithNonStandardIdHasDynamicRefTable) {
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder().SetCompilationPackage("app").SetPackageId(0x80).Build();
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .SetPackageId("app", 0x80)
+ .AddSimple("app:id/foo", ResourceId(0x80010000))
+ .Build();
+
+ ResTable result;
+ ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result));
+
+ const DynamicRefTable* dynamic_ref_table = result.getDynamicRefTableForCookie(1);
+ ASSERT_NE(nullptr, dynamic_ref_table);
+
+ const KeyedVector<String16, uint8_t>& entries = dynamic_ref_table->entries();
+ ssize_t idx = entries.indexOfKey(android::String16("app"));
+ ASSERT_GE(idx, 0);
+ EXPECT_EQ(0x80u, entries.valueAt(idx));
+}
+
TEST_F(TableFlattenerTest, LongPackageNameIsTruncated) {
std::string kPackageName(256, 'F');
diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/flatten/XmlFlattener_test.cpp
index 494d9d2..f0613e7 100644
--- a/tools/aapt2/flatten/XmlFlattener_test.cpp
+++ b/tools/aapt2/flatten/XmlFlattener_test.cpp
@@ -35,13 +35,16 @@
.SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
.AddSymbolSource(
test::StaticSymbolSourceBuilder()
- .AddSymbol("android:attr/id", ResourceId(0x010100d0),
- test::AttributeBuilder().Build())
+ .AddPublicSymbol("android:attr/id", ResourceId(0x010100d0),
+ test::AttributeBuilder().Build())
.AddSymbol("com.app.test:id/id", ResourceId(0x7f020000))
.AddPublicSymbol("android:attr/paddingStart", ResourceId(0x010103b3),
test::AttributeBuilder().Build())
.AddPublicSymbol("android:attr/colorAccent", ResourceId(0x01010435),
test::AttributeBuilder().Build())
+ .AddSymbol("com.app.test.feature:id/foo", ResourceId(0x80020000))
+ .AddSymbol("com.app.test.feature:attr/foo", ResourceId(0x80010000),
+ test::AttributeBuilder().Build())
.Build())
.Build();
}
@@ -65,7 +68,7 @@
}
protected:
- std::unique_ptr<IAaptContext> context_;
+ std::unique_ptr<test::Context> context_;
};
TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {
@@ -218,14 +221,10 @@
EXPECT_EQ(tree.indexOfStyle(), 1);
}
-/*
- * The device ResXMLParser in libandroidfw differentiates between empty
- * namespace and null
- * namespace.
- */
+// The device ResXMLParser in libandroidfw differentiates between empty namespace and null
+// namespace.
TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) {
- std::unique_ptr<xml::XmlResource> doc =
- test::BuildXmlDom("<View package=\"android\"/>");
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom("<View package=\"android\"/>");
android::ResXMLTree tree;
ASSERT_TRUE(Flatten(doc.get(), &tree));
@@ -261,4 +260,44 @@
EXPECT_NE(nullptr, tree.getAttributeStringValue(idx, &len));
}
+TEST_F(XmlFlattenerTest, FlattenNonStandardPackageId) {
+ context_->SetCompilationPackage("com.app.test.feature");
+ context_->SetPackageId(0x80);
+ context_->SetNameManglerPolicy({"com.app.test.feature"});
+
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@id/foo"
+ app:foo="@id/foo" />)EOF");
+
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
+
+ // The tree needs a custom DynamicRefTable since it is not using a standard app ID (0x7f).
+ android::DynamicRefTable dynamic_ref_table;
+ dynamic_ref_table.addMapping(0x80, 0x80);
+
+ android::ResXMLTree tree(&dynamic_ref_table);
+ ASSERT_TRUE(Flatten(doc.get(), &tree));
+
+ while (tree.next() != android::ResXMLTree::START_TAG) {
+ ASSERT_NE(android::ResXMLTree::BAD_DOCUMENT, tree.getEventType());
+ ASSERT_NE(android::ResXMLTree::END_DOCUMENT, tree.getEventType());
+ }
+
+ ssize_t idx;
+
+ idx = tree.indexOfAttribute(xml::kSchemaAndroid, "id");
+ ASSERT_GE(idx, 0);
+ EXPECT_EQ(idx, tree.indexOfID());
+ EXPECT_EQ(ResourceId(0x010100d0), ResourceId(tree.getAttributeNameResID(idx)));
+
+ idx = tree.indexOfAttribute(xml::kSchemaAuto, "foo");
+ ASSERT_GE(idx, 0);
+ EXPECT_EQ(ResourceId(0x80010000), ResourceId(tree.getAttributeNameResID(idx)));
+ EXPECT_EQ(android::Res_value::TYPE_REFERENCE, tree.getAttributeDataType(idx));
+ EXPECT_EQ(ResourceId(0x80020000), tree.getAttributeData(idx));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
index 29d1838..0564db0 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -52,15 +52,27 @@
return compilation_package_.value();
}
+ void SetCompilationPackage(const android::StringPiece& package) {
+ compilation_package_ = package.to_string();
+ }
+
uint8_t GetPackageId() override {
CHECK(bool(package_id_)) << "package ID not set";
return package_id_.value();
}
+ void SetPackageId(uint8_t package_id) {
+ package_id_ = package_id;
+ }
+
NameMangler* GetNameMangler() override {
return &name_mangler_;
}
+ void SetNameManglerPolicy(const NameManglerPolicy& policy) {
+ name_mangler_ = NameMangler(policy);
+ }
+
bool IsVerbose() override {
return false;
}