AAPT2: Add Proguard rules for nav fragments
Adds generation of proguard rules for fragments in res/navigation. All
android:name attributes have keep rules generated for the classes they
reference.
Bug: 69929974
Test: aapt2_tests
Change-Id: I05a87484ab357ea5629b73caad8488182f474e1f
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 74edf6e..ab2aab2 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -500,7 +500,7 @@
return {};
}
- if (options_.update_proguard_spec && !proguard::CollectProguardRules(doc, keep_set_)) {
+ if (options_.update_proguard_spec && !proguard::CollectProguardRules(context_, doc, keep_set_)) {
return {};
}
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index ffcef89..2857d5a 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -189,6 +189,29 @@
DISALLOW_COPY_AND_ASSIGN(XmlResourceVisitor);
};
+class NavigationVisitor : public BaseVisitor {
+ public:
+ NavigationVisitor(const ResourceFile& file, KeepSet* keep_set, const std::string& package)
+ : BaseVisitor(file, keep_set), package_(package) {
+ }
+
+ void Visit(xml::Element* node) override {
+ const auto& attr = node->FindAttribute(xml::kSchemaAndroid, "name");
+ if (attr != nullptr && !attr->value.empty()) {
+ std::string name = (attr->value[0] == '.') ? package_ + attr->value : attr->value;
+ if (util::IsJavaClassName(name)) {
+ AddClass(node->line_number, name);
+ }
+ }
+
+ BaseVisitor::Visit(node);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NavigationVisitor);
+ const std::string package_;
+};
+
class TransitionVisitor : public BaseVisitor {
public:
TransitionVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set) {
@@ -291,7 +314,7 @@
return false;
}
-bool CollectProguardRules(xml::XmlResource* res, KeepSet* keep_set) {
+bool CollectProguardRules(IAaptContext* context_, xml::XmlResource* res, KeepSet* keep_set) {
if (!res->root) {
return false;
}
@@ -309,6 +332,12 @@
break;
}
+ case ResourceType::kNavigation: {
+ NavigationVisitor visitor(res->file, keep_set, context_->GetCompilationPackage());
+ res->root->Accept(&visitor);
+ break;
+ }
+
case ResourceType::kTransition: {
TransitionVisitor visitor(res->file, keep_set);
res->root->Accept(&visitor);
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index 46827ee..343272e 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -79,7 +79,7 @@
bool CollectProguardRulesForManifest(xml::XmlResource* res, KeepSet* keep_set,
bool main_dex_only = false);
-bool CollectProguardRules(xml::XmlResource* res, KeepSet* keep_set);
+bool CollectProguardRules(IAaptContext* context, xml::XmlResource* res, KeepSet* keep_set);
bool CollectResourceReferences(IAaptContext* context, ResourceTable* table, KeepSet* keep_set);
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index 37d1a5f..876c7a7 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -42,7 +42,7 @@
layout->file.name = test::ParseNameOrDie("layout/foo");
proguard::KeepSet set;
- ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
+ ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
std::string actual = GetKeepSetString(set);
@@ -56,7 +56,7 @@
layout->file.name = test::ParseNameOrDie("layout/foo");
proguard::KeepSet set;
- ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
+ ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
std::string actual = GetKeepSetString(set);
@@ -72,7 +72,7 @@
layout->file.name = test::ParseNameOrDie("layout/foo");
proguard::KeepSet set;
- ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
+ ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
std::string actual = GetKeepSetString(set);
@@ -80,6 +80,34 @@
EXPECT_THAT(actual, HasSubstr("com.foo.Baz"));
}
+TEST(ProguardRulesTest, NavigationFragmentNameAndClassRulesAreEmitted) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .SetCompilationPackage("com.base").Build();
+ std::unique_ptr<xml::XmlResource> navigation = test::BuildXmlDom(R"(
+ <navigation
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <custom android:id="@id/foo"
+ android:name="com.package.Foo"/>
+ <fragment android:id="@id/bar"
+ android:name="com.package.Bar">
+ <nested android:id="@id/nested"
+ android:name=".Nested"/>
+ </fragment>
+ </navigation>
+ )");
+
+ navigation->file.name = test::ParseNameOrDie("navigation/graph.xml");
+
+ proguard::KeepSet set;
+ ASSERT_TRUE(proguard::CollectProguardRules(context.get(), navigation.get(), &set));
+
+ std::string actual = GetKeepSetString(set);
+ EXPECT_THAT(actual, HasSubstr("com.package.Foo"));
+ EXPECT_THAT(actual, HasSubstr("com.package.Bar"));
+ EXPECT_THAT(actual, HasSubstr("com.base.Nested"));
+}
+
TEST(ProguardRulesTest, CustomViewRulesAreEmitted) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
@@ -89,7 +117,7 @@
layout->file.name = test::ParseNameOrDie("layout/foo");
proguard::KeepSet set;
- ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
+ ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
std::string actual = GetKeepSetString(set);
@@ -125,8 +153,8 @@
ASSERT_TRUE(xml_linker.Consume(context.get(), foo_layout.get()));
proguard::KeepSet set = proguard::KeepSet(true);
- ASSERT_TRUE(proguard::CollectProguardRules(bar_layout.get(), &set));
- ASSERT_TRUE(proguard::CollectProguardRules(foo_layout.get(), &set));
+ ASSERT_TRUE(proguard::CollectProguardRules(context.get(), bar_layout.get(), &set));
+ ASSERT_TRUE(proguard::CollectProguardRules(context.get(), foo_layout.get(), &set));
std::string actual = GetKeepSetString(set);
@@ -147,7 +175,7 @@
proguard::KeepSet set = proguard::KeepSet(true);
set.AddReference({test::ParseNameOrDie("layout/bar"), {}}, layout->file.name);
- ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
+ ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
std::string actual = GetKeepSetString(set);
@@ -168,7 +196,7 @@
proguard::KeepSet set = proguard::KeepSet(true);
set.AddReference({test::ParseNameOrDie("style/MyStyle"), {}}, layout->file.name);
- ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
+ ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
std::string actual = GetKeepSetString(set);
@@ -184,7 +212,7 @@
layout->file.name = test::ParseNameOrDie("layout/foo");
proguard::KeepSet set;
- ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
+ ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
std::string actual = GetKeepSetString(set);
@@ -203,7 +231,7 @@
menu->file.name = test::ParseNameOrDie("menu/foo");
proguard::KeepSet set;
- ASSERT_TRUE(proguard::CollectProguardRules(menu.get(), &set));
+ ASSERT_TRUE(proguard::CollectProguardRules(context.get(), menu.get(), &set));
std::string actual = GetKeepSetString(set);