Reorder styleable attributes in shared libraries

This is a similar fix to
https://googleplex-android-review.git.corp.google.com/c/platform/frameworks/base/+/10109386
but deals with styleable attributes instead of style items.

Bug: 147674078
Test: JavaClassGeneratorTest.SortsDynamicAttributesAfterFrameworkAttributes
Change-Id: Ida6572cf07e2b5987e9d8941cf169a37c43578c4
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index c49c370..22e667b 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -279,6 +279,19 @@
   return id.to_string();
 }
 
+// Helper to compare resource IDs, moving dynamic IDs after framework IDs.
+inline bool cmp_ids_dynamic_after_framework(const ResourceId& a, const ResourceId& b) {
+  // If one of a and b is from the framework package (package ID 0x01), and the
+  // other is a dynamic ID (package ID 0x00), then put the dynamic ID after the
+  // framework ID. This ensures that when AssetManager resolves the dynamic IDs,
+  // they will be in sorted order as expected by AssetManager.
+  if ((a.package_id() == kFrameworkPackageId && b.package_id() == 0x00) ||
+      (a.package_id() == 0x00 && b.package_id() == kFrameworkPackageId)) {
+    return b < a;
+  }
+  return a < b;
+}
+
 //
 // ResourceType implementation.
 //
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 4784ecf..eb0ade6 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -59,22 +59,10 @@
   dst[i] = 0;
 }
 
-static bool cmp_style_ids(ResourceId a, ResourceId b) {
-  // If one of a and b is from the framework package (package ID 0x01), and the
-  // other is a dynamic ID (package ID 0x00), then put the dynamic ID after the
-  // framework ID. This ensures that when AssetManager resolves the dynamic IDs,
-  // they will be in sorted order as expected by AssetManager.
-  if ((a.package_id() == kFrameworkPackageId && b.package_id() == 0x00) ||
-      (a.package_id() == 0x00 && b.package_id() == kFrameworkPackageId)) {
-    return b < a;
-  }
-  return a < b;
-}
-
 static bool cmp_style_entries(const Style::Entry& a, const Style::Entry& b) {
   if (a.key.id) {
     if (b.key.id) {
-      return cmp_style_ids(a.key.id.value(), b.key.id.value());
+      return cmp_ids_dynamic_after_framework(a.key.id.value(), b.key.id.value());
     }
     return true;
   } else if (!b.key.id) {
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index bb541fe..0869c57 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -218,13 +218,10 @@
 static bool operator<(const StyleableAttr& lhs, const StyleableAttr& rhs) {
   const ResourceId lhs_id = lhs.attr_ref->id.value_or_default(ResourceId(0));
   const ResourceId rhs_id = rhs.attr_ref->id.value_or_default(ResourceId(0));
-  if (lhs_id < rhs_id) {
-    return true;
-  } else if (lhs_id > rhs_id) {
-    return false;
-  } else {
+  if (lhs_id == rhs_id) {
     return lhs.attr_ref->name.value() < rhs.attr_ref->name.value();
   }
+  return cmp_ids_dynamic_after_framework(lhs_id, rhs_id);
 }
 
 void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const ResourceId& id,
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 1e1fe47..04e2010 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -551,4 +551,39 @@
   ASSERT_TRUE(generator.Generate("android", nullptr));
 }
 
+TEST(JavaClassGeneratorTest, SortsDynamicAttributesAfterFrameworkAttributes) {
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .SetPackageId("android", 0x01)
+          .SetPackageId("lib", 0x00)
+          .AddValue("android:attr/framework_attr", ResourceId(0x01010000),
+                    test::AttributeBuilder().Build())
+          .AddValue("lib:attr/dynamic_attr", ResourceId(0x00010000),
+                    test::AttributeBuilder().Build())
+          .AddValue("lib:styleable/MyStyleable", ResourceId(0x00030000),
+                    test::StyleableBuilder()
+                        .AddItem("android:attr/framework_attr", ResourceId(0x01010000))
+                        .AddItem("lib:attr/dynamic_attr", ResourceId(0x00010000))
+                        .Build())
+          .Build();
+
+  std::unique_ptr<IAaptContext> context =
+      test::ContextBuilder()
+          .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+          .SetNameManglerPolicy(NameManglerPolicy{"custom"})
+          .SetCompilationPackage("custom")
+          .Build();
+  JavaClassGenerator generator(context.get(), table.get(), {});
+
+  std::string output;
+  StringOutputStream out(&output);
+  EXPECT_TRUE(generator.Generate("lib", &out));
+  out.Flush();
+
+  EXPECT_THAT(output, HasSubstr("public static final int[] MyStyleable={"));
+  EXPECT_THAT(output, HasSubstr("0x01010000, 0x00010000"));
+  EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_android_framework_attr=0;"));
+  EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_dynamic_attr=1;"));
+}
+
 }  // namespace aapt