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;
   }