diff options
Diffstat (limited to 'tools/aapt2/java')
-rw-r--r-- | tools/aapt2/java/JavaClassGenerator.cpp | 22 | ||||
-rw-r--r-- | tools/aapt2/java/JavaClassGenerator_test.cpp | 67 | ||||
-rw-r--r-- | tools/aapt2/java/ProguardRules.cpp | 131 | ||||
-rw-r--r-- | tools/aapt2/java/ProguardRules.h | 38 | ||||
-rw-r--r-- | tools/aapt2/java/ProguardRules_test.cpp | 217 |
5 files changed, 391 insertions, 84 deletions
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp index 6b07b1e96261..d1a70a75a44e 100644 --- a/tools/aapt2/java/JavaClassGenerator.cpp +++ b/tools/aapt2/java/JavaClassGenerator.cpp @@ -256,9 +256,20 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res styleable_attr.field_name = TransformNestedAttr(attr.name.value(), array_field_name, package_name_to_generate); + Reference ref = attr; + if (attr.name.value().package.empty()) { + + // If the resource does not have a package name, set the package to the unmangled package name + // of the styleable declaration because attributes without package names would have been + // declared in the same package as the styleable. + ref.name = ResourceName(package_name_to_generate, ref.name.value().type, + ref.name.value().entry); + } + // Look up the symbol so that we can write out in the comments what are possible legal values // for this attribute. - const SymbolTable::Symbol* symbol = context_->GetExternalSymbols()->FindByReference(attr); + const SymbolTable::Symbol* symbol = context_->GetExternalSymbols()->FindByReference(ref); + if (symbol && symbol->attribute) { // Copy the symbol data structure because the returned instance can be destroyed. styleable_attr.symbol = *symbol; @@ -303,7 +314,7 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res const ResourceName& attr_name = entry.attr_ref->name.value(); styleable_comment << "<tr><td><code>{@link #" << entry.field_name << " " << (!attr_name.package.empty() ? attr_name.package - : context_->GetCompilationPackage()) + : package_name_to_generate) << ":" << attr_name.entry << "}</code></td>"; // Only use the comment up until the first '.'. This is to stay compatible with @@ -347,7 +358,9 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res } // Add the Styleable array to the Styleable class. - out_class_def->AddMember(std::move(array_def)); + if (out_class_def != nullptr) { + out_class_def->AddMember(std::move(array_def)); + } // Now we emit the indices into the array. for (size_t i = 0; i < attr_count; i++) { @@ -372,7 +385,7 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res StringPiece package_name = attr_name.package; if (package_name.empty()) { - package_name = context_->GetCompilationPackage(); + package_name = package_name_to_generate; } std::unique_ptr<IntMember> index_member = @@ -578,7 +591,6 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, if (out_r_txt != nullptr) { r_txt_printer = util::make_unique<Printer>(out_r_txt); } - // Generate an onResourcesLoaded() callback if requested. if (out != nullptr && options_.rewrite_callback_options) { rewrite_method = diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp index e449546f9399..fa208be120ed 100644 --- a/tools/aapt2/java/JavaClassGenerator_test.cpp +++ b/tools/aapt2/java/JavaClassGenerator_test.cpp @@ -107,6 +107,55 @@ TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) { EXPECT_THAT(output, Not(HasSubstr("com_foo$two"))); } +TEST(JavaClassGeneratorTest, StyleableAttributesWithDifferentPackageName) { + std::unique_ptr<ResourceTable> table = + test::ResourceTableBuilder() + .SetPackageId("android", 0x01) + .SetPackageId("app", 0x7f) + .AddValue("app:attr/foo", ResourceId(0x7f010000), + test::AttributeBuilder().Build()) + .AddValue("app:attr/bar", ResourceId(0x7f010001), + test::AttributeBuilder().Build()) + .AddValue("android:attr/baz", ResourceId(0x01010000), + test::AttributeBuilder().Build()) + .AddValue("app:styleable/MyStyleable", ResourceId(0x7f030000), + test::StyleableBuilder() + .AddItem("app:attr/foo", ResourceId(0x7f010000)) + .AddItem("attr/bar", ResourceId(0x7f010001)) + .AddItem("android:attr/baz", ResourceId(0x01010000)) + .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("app", &out)); + out.Flush(); + + EXPECT_THAT(output, Not(HasSubstr("public static final int baz=0x01010000;"))); + EXPECT_THAT(output, HasSubstr("public static final int foo=0x7f010000;")); + EXPECT_THAT(output, HasSubstr("public static final int bar=0x7f010001;")); + + EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_android_baz=0;")); + EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_foo=1;")); + EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_bar=2;")); + + EXPECT_THAT(output, HasSubstr("@link #MyStyleable_android_baz android:baz")); + EXPECT_THAT(output, HasSubstr("@link #MyStyleable_foo app:foo")); + EXPECT_THAT(output, HasSubstr("@link #MyStyleable_bar app:bar")); + + EXPECT_THAT(output, HasSubstr("@link android.R.attr#baz")); + EXPECT_THAT(output, HasSubstr("@link app.R.attr#foo")); + EXPECT_THAT(output, HasSubstr("@link app.R.attr#bar")); +} + TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() @@ -438,4 +487,22 @@ TEST(JavaClassGeneratorTest, GenerateOnResourcesLoadedCallbackForSharedLibrary) EXPECT_THAT(output, HasSubstr("com.boo.R.onResourcesLoaded")); } +TEST(JavaClassGeneratorTest, OnlyGenerateRText) { + std::unique_ptr<ResourceTable> table = + test::ResourceTableBuilder() + .SetPackageId("android", 0x01) + .AddValue("android:attr/foo", ResourceId(0x01010000), util::make_unique<Attribute>()) + .AddValue("android:styleable/hey.dude", ResourceId(0x01020000), + test::StyleableBuilder() + .AddItem("android:attr/foo", ResourceId(0x01010000)) + .Build()) + .Build(); + + std::unique_ptr<IAaptContext> context = + test::ContextBuilder().SetPackageId(0x01).SetCompilationPackage("android").Build(); + JavaClassGenerator generator(context.get(), table.get(), {}); + + ASSERT_TRUE(generator.Generate("android", nullptr)); +} + } // namespace aapt diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp index ffcef8966654..52e168ed47aa 100644 --- a/tools/aapt2/java/ProguardRules.cpp +++ b/tools/aapt2/java/ProguardRules.cpp @@ -39,7 +39,11 @@ class BaseVisitor : public xml::Visitor { public: using xml::Visitor::Visit; - BaseVisitor(const ResourceFile& file, KeepSet* keep_set) : file_(file), keep_set_(keep_set) { + BaseVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set, "...") { + } + + BaseVisitor(const ResourceFile& file, KeepSet* keep_set, const std::string& ctor_signature) + : file_(file), keep_set_(keep_set), ctor_signature_(ctor_signature) { } void Visit(xml::Element* node) override { @@ -50,11 +54,11 @@ class BaseVisitor : public xml::Visitor { // This is a custom view, let's figure out the class name from this. std::string package = maybe_package.value().package + "." + node->name; if (util::IsJavaClassName(package)) { - AddClass(node->line_number, package); + AddClass(node->line_number, package, ctor_signature_); } } } else if (util::IsJavaClassName(node->name)) { - AddClass(node->line_number, node->name); + AddClass(node->line_number, node->name, ctor_signature_); } for (const auto& child : node->children) { @@ -74,13 +78,18 @@ class BaseVisitor : public xml::Visitor { protected: ResourceFile file_; KeepSet* keep_set_; + std::string ctor_signature_; - virtual void AddClass(size_t line_number, const std::string& class_name) { - keep_set_->AddConditionalClass({file_.name, file_.source.WithLine(line_number)}, class_name); + virtual void AddClass(size_t line_number, const std::string& class_name, + const std::string& ctor_signature) { + keep_set_->AddConditionalClass({file_.name, file_.source.WithLine(line_number)}, + {class_name, ctor_signature}); } - void AddMethod(size_t line_number, const std::string& method_name) { - keep_set_->AddMethod({file_.name, file_.source.WithLine(line_number)}, method_name); + void AddMethod(size_t line_number, const std::string& method_name, + const std::string& method_signature) { + keep_set_->AddMethod({file_.name, file_.source.WithLine(line_number)}, + {method_name, method_signature}); } void AddReference(size_t line_number, Reference* ref) { @@ -100,32 +109,39 @@ class BaseVisitor : public xml::Visitor { class LayoutVisitor : public BaseVisitor { public: - LayoutVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set) { + LayoutVisitor(const ResourceFile& file, KeepSet* keep_set) + : BaseVisitor(file, keep_set, "android.content.Context, android.util.AttributeSet") { } void Visit(xml::Element* node) override { - bool check_class = false; - bool check_name = false; + bool is_view = false; + bool is_fragment = false; if (node->namespace_uri.empty()) { if (node->name == "view") { - check_class = true; + is_view = true; } else if (node->name == "fragment") { - check_class = check_name = true; + is_fragment = true; } } else if (node->namespace_uri == xml::kSchemaAndroid) { - check_name = node->name == "fragment"; + is_fragment = node->name == "fragment"; } for (const auto& attr : node->attributes) { - if (check_class && attr.namespace_uri.empty() && attr.name == "class" && - util::IsJavaClassName(attr.value)) { - AddClass(node->line_number, attr.value); - } else if (check_name && attr.namespace_uri == xml::kSchemaAndroid && - attr.name == "name" && util::IsJavaClassName(attr.value)) { - AddClass(node->line_number, attr.value); - } else if (attr.namespace_uri == xml::kSchemaAndroid && - attr.name == "onClick") { - AddMethod(node->line_number, attr.value); + if (attr.namespace_uri.empty() && attr.name == "class") { + if (util::IsJavaClassName(attr.value)) { + if (is_view) { + AddClass(node->line_number, attr.value, + "android.content.Context, android.util.AttributeSet"); + } else if (is_fragment) { + AddClass(node->line_number, attr.value, ""); + } + } + } else if (attr.namespace_uri == xml::kSchemaAndroid && attr.name == "name") { + if (is_fragment && util::IsJavaClassName(attr.value)) { + AddClass(node->line_number, attr.value, ""); + } + } else if (attr.namespace_uri == xml::kSchemaAndroid && attr.name == "onClick") { + AddMethod(node->line_number, attr.value, "android.view.View"); } } @@ -147,9 +163,9 @@ class MenuVisitor : public BaseVisitor { if (attr.namespace_uri == xml::kSchemaAndroid) { if ((attr.name == "actionViewClass" || attr.name == "actionProviderClass") && util::IsJavaClassName(attr.value)) { - AddClass(node->line_number, attr.value); + AddClass(node->line_number, attr.value, "android.content.Context"); } else if (attr.name == "onClick") { - AddMethod(node->line_number, attr.value); + AddMethod(node->line_number, attr.value, "android.view.MenuItem"); } } } @@ -178,7 +194,7 @@ class XmlResourceVisitor : public BaseVisitor { xml::Attribute* attr = node->FindAttribute(xml::kSchemaAndroid, "fragment"); if (attr && util::IsJavaClassName(attr->value)) { - AddClass(node->line_number, attr->value); + AddClass(node->line_number, attr->value, ""); } } @@ -189,6 +205,29 @@ class XmlResourceVisitor : public BaseVisitor { 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) { @@ -200,7 +239,8 @@ class TransitionVisitor : public BaseVisitor { if (check_class) { xml::Attribute* attr = node->FindAttribute({}, "class"); if (attr && util::IsJavaClassName(attr->value)) { - AddClass(node->line_number, attr->value); + AddClass(node->line_number, attr->value, + "android.content.Context, android.util.AttributeSet"); } } @@ -231,7 +271,14 @@ class ManifestVisitor : public BaseVisitor { if (attr) { Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value); if (result) { - AddClass(node->line_number, result.value()); + AddClass(node->line_number, result.value(), ""); + } + } + attr = node->FindAttribute(xml::kSchemaAndroid, "appComponentFactory"); + if (attr) { + Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value); + if (result) { + AddClass(node->line_number, result.value(), ""); } } if (main_dex_only_) { @@ -262,7 +309,7 @@ class ManifestVisitor : public BaseVisitor { if (get_name) { Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value); if (result) { - AddClass(node->line_number, result.value()); + AddClass(node->line_number, result.value(), ""); } } } @@ -270,7 +317,8 @@ class ManifestVisitor : public BaseVisitor { BaseVisitor::Visit(node); } - virtual void AddClass(size_t line_number, const std::string& class_name) override { + virtual void AddClass(size_t line_number, const std::string& class_name, + const std::string& ctor_signature) override { keep_set_->AddManifestClass({file_.name, file_.source.WithLine(line_number)}, class_name); } @@ -291,7 +339,7 @@ bool CollectProguardRulesForManifest(xml::XmlResource* res, KeepSet* keep_set, b 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 +357,12 @@ bool CollectProguardRules(xml::XmlResource* res, KeepSet* keep_set) { 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); @@ -330,13 +384,13 @@ bool CollectProguardRules(xml::XmlResource* res, KeepSet* keep_set) { return true; } -void WriteKeepSet(const KeepSet& keep_set, OutputStream* out) { +void WriteKeepSet(const KeepSet& keep_set, OutputStream* out, bool minimal_keep) { Printer printer(out); for (const auto& entry : keep_set.manifest_class_set_) { for (const UsageLocation& location : entry.second) { printer.Print("# Referenced at ").Println(location.source.to_string()); } - printer.Print("-keep class ").Print(entry.first).Println(" { <init>(...); }"); + printer.Print("-keep class ").Print(entry.first).Println(" { <init>(); }"); } for (const auto& entry : keep_set.conditional_class_set_) { @@ -352,13 +406,19 @@ void WriteKeepSet(const KeepSet& keep_set, OutputStream* out) { printer.Print("-if class **.R$layout { int ") .Print(JavaClassGenerator::TransformToFieldName(location.name.entry)) .Println("; }"); - printer.Print("-keep class ").Print(entry.first).Println(" { <init>(...); }"); + + printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>("); + printer.Print((minimal_keep) ? entry.first.signature : "..."); + printer.Println("); }"); } } else { for (const UsageLocation& location : entry.second) { printer.Print("# Referenced at ").Println(location.source.to_string()); } - printer.Print("-keep class ").Print(entry.first).Println(" { <init>(...); }"); + + printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>("); + printer.Print((minimal_keep) ? entry.first.signature : "..."); + printer.Println("); }"); } printer.Println(); } @@ -367,7 +427,8 @@ void WriteKeepSet(const KeepSet& keep_set, OutputStream* out) { for (const UsageLocation& location : entry.second) { printer.Print("# Referenced at ").Println(location.source.to_string()); } - printer.Print("-keepclassmembers class * { *** ").Print(entry.first).Println("(...); }"); + printer.Print("-keepclassmembers class * { *** ").Print(entry.first.name) + .Print("(").Print(entry.first.signature).Println("); }"); printer.Println(); } } diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h index 46827ee7cf93..38b4860d1d61 100644 --- a/tools/aapt2/java/ProguardRules.h +++ b/tools/aapt2/java/ProguardRules.h @@ -40,6 +40,11 @@ struct UsageLocation { Source source; }; +struct NameAndSignature { + std::string name; + std::string signature; +}; + class KeepSet { public: KeepSet() = default; @@ -51,12 +56,13 @@ class KeepSet { manifest_class_set_[class_name].insert(file); } - inline void AddConditionalClass(const UsageLocation& file, const std::string& class_name) { - conditional_class_set_[class_name].insert(file); + inline void AddConditionalClass(const UsageLocation& file, + const NameAndSignature& class_and_signature) { + conditional_class_set_[class_and_signature].insert(file); } - inline void AddMethod(const UsageLocation& file, const std::string& method_name) { - method_set_[method_name].insert(file); + inline void AddMethod(const UsageLocation& file, const NameAndSignature& name_and_signature) { + method_set_[name_and_signature].insert(file); } inline void AddReference(const UsageLocation& file, const ResourceName& resource_name) { @@ -64,26 +70,26 @@ class KeepSet { } private: - friend void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out); + friend void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out, bool minimal_keep); friend bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set, std::set<UsageLocation>* locations); bool conditional_keep_rules_ = false; std::map<std::string, std::set<UsageLocation>> manifest_class_set_; - std::map<std::string, std::set<UsageLocation>> method_set_; - std::map<std::string, std::set<UsageLocation>> conditional_class_set_; + std::map<NameAndSignature, std::set<UsageLocation>> method_set_; + std::map<NameAndSignature, std::set<UsageLocation>> conditional_class_set_; std::map<ResourceName, std::set<UsageLocation>> reference_set_; }; 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); -void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out); +void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out, bool minimal_keep); bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set, std::set<UsageLocation>* locations); @@ -100,6 +106,20 @@ inline int operator<(const UsageLocation& lhs, const UsageLocation& rhs) { return lhs.name.compare(rhs.name); } +// +// NameAndSignature implementation. +// + +inline bool operator<(const NameAndSignature& lhs, const NameAndSignature& rhs) { + if (lhs.name < rhs.name) { + return true; + } + if (lhs.name == rhs.name) { + return lhs.signature < rhs.signature; + } + return false; +} + } // namespace proguard } // namespace aapt diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp index f774e3a1245a..da24907417fa 100644 --- a/tools/aapt2/java/ProguardRules_test.cpp +++ b/tools/aapt2/java/ProguardRules_test.cpp @@ -27,14 +27,54 @@ using ::testing::Not; namespace aapt { -std::string GetKeepSetString(const proguard::KeepSet& set) { +std::string GetKeepSetString(const proguard::KeepSet& set, bool minimal_rules) { std::string out; StringOutputStream sout(&out); - proguard::WriteKeepSet(set, &sout); + proguard::WriteKeepSet(set, &sout, minimal_rules); sout.Flush(); return out; } +TEST(ProguardRulesTest, ManifestRuleDefaultConstructorOnly) { + std::unique_ptr<xml::XmlResource> manifest = test::BuildXmlDom(R"( + <manifest xmlns:android="http://schemas.android.com/apk/res/android"> + <application + android:appComponentFactory="com.foo.BarAppComponentFactory" + android:backupAgent="com.foo.BarBackupAgent" + android:name="com.foo.BarApplication" + > + <activity android:name="com.foo.BarActivity"/> + <service android:name="com.foo.BarService"/> + <receiver android:name="com.foo.BarReceiver"/> + <provider android:name="com.foo.BarProvider"/> + </application> + <instrumentation android:name="com.foo.BarInstrumentation"/> + </manifest>)"); + + proguard::KeepSet set; + ASSERT_TRUE(proguard::CollectProguardRulesForManifest(manifest.get(), &set, false)); + + std::string actual = GetKeepSetString(set, /** minimal_rules */ false); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarAppComponentFactory { <init>(); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarBackupAgent { <init>(); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarApplication { <init>(); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarActivity { <init>(); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarService { <init>(); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarReceiver { <init>(); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarProvider { <init>(); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarInstrumentation { <init>(); }")); + + actual = GetKeepSetString(set, /** minimal_rules */ true); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarAppComponentFactory { <init>(); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarBackupAgent { <init>(); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarApplication { <init>(); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarActivity { <init>(); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarService { <init>(); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarReceiver { <init>(); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarProvider { <init>(); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarInstrumentation { <init>(); }")); +} + TEST(ProguardRulesTest, FragmentNameRuleIsEmitted) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"( @@ -43,11 +83,13 @@ TEST(ProguardRulesTest, FragmentNameRuleIsEmitted) { 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); + std::string actual = GetKeepSetString(set, /** minimal_rules */ false); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }")); - EXPECT_THAT(actual, HasSubstr("com.foo.Bar")); + actual = GetKeepSetString(set, /** minimal_rules */ true); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }")); } TEST(ProguardRulesTest, FragmentClassRuleIsEmitted) { @@ -57,11 +99,13 @@ TEST(ProguardRulesTest, FragmentClassRuleIsEmitted) { 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); + std::string actual = GetKeepSetString(set, /** minimal_rules */ false); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }")); - EXPECT_THAT(actual, HasSubstr("com.foo.Bar")); + actual = GetKeepSetString(set, /** minimal_rules */ true); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }")); } TEST(ProguardRulesTest, FragmentNameAndClassRulesAreEmitted) { @@ -73,12 +117,48 @@ TEST(ProguardRulesTest, FragmentNameAndClassRulesAreEmitted) { 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, /** minimal_rules */ false); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(...); }")); + + actual = GetKeepSetString(set, /** minimal_rules */ true); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(); }")); +} + +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); + std::string actual = GetKeepSetString(set, /** minimal_rules */ false); + EXPECT_THAT(actual, HasSubstr("-keep class com.package.Foo { <init>(...); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.package.Bar { <init>(...); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.base.Nested { <init>(...); }")); - EXPECT_THAT(actual, HasSubstr("com.foo.Bar")); - EXPECT_THAT(actual, HasSubstr("com.foo.Baz")); + actual = GetKeepSetString(set, /** minimal_rules */ true); + EXPECT_THAT(actual, HasSubstr("-keep class com.package.Foo { <init>(...); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.package.Bar { <init>(...); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.base.Nested { <init>(...); }")); } TEST(ProguardRulesTest, CustomViewRulesAreEmitted) { @@ -90,11 +170,14 @@ TEST(ProguardRulesTest, CustomViewRulesAreEmitted) { 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); + std::string actual = GetKeepSetString(set, /** minimal_rules */ false); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }")); - EXPECT_THAT(actual, HasSubstr("com.foo.Bar")); + actual = GetKeepSetString(set, /** minimal_rules */ true); + EXPECT_THAT(actual, HasSubstr( + "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }")); } TEST(ProguardRulesTest, IncludedLayoutRulesAreConditional) { @@ -126,16 +209,21 @@ TEST(ProguardRulesTest, IncludedLayoutRulesAreConditional) { 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)); - - std::string actual = GetKeepSetString(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, /** minimal_rules */ false); EXPECT_THAT(actual, HasSubstr("-if class **.R$layout")); EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }")); EXPECT_THAT(actual, HasSubstr("int foo")); EXPECT_THAT(actual, HasSubstr("int bar")); - EXPECT_THAT(actual, HasSubstr("com.foo.Bar")); + + actual = GetKeepSetString(set, /** minimal_rules */ true); + EXPECT_THAT(actual, HasSubstr("-if class **.R$layout")); + EXPECT_THAT(actual, HasSubstr( + "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }")); + EXPECT_THAT(actual, HasSubstr("int foo")); + EXPECT_THAT(actual, HasSubstr("int bar")); } TEST(ProguardRulesTest, AliasedLayoutRulesAreConditional) { @@ -148,15 +236,21 @@ TEST(ProguardRulesTest, AliasedLayoutRulesAreConditional) { 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); + std::string actual = GetKeepSetString(set, /** minimal_rules */ false); + EXPECT_THAT(actual, HasSubstr( + "-keep class com.foo.Bar { <init>(...); }")); + EXPECT_THAT(actual, HasSubstr("-if class **.R$layout")); + EXPECT_THAT(actual, HasSubstr("int foo")); + EXPECT_THAT(actual, HasSubstr("int bar")); - EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }")); + actual = GetKeepSetString(set, /** minimal_rules */ true); + EXPECT_THAT(actual, HasSubstr( + "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }")); EXPECT_THAT(actual, HasSubstr("-if class **.R$layout")); EXPECT_THAT(actual, HasSubstr("int foo")); EXPECT_THAT(actual, HasSubstr("int bar")); - EXPECT_THAT(actual, HasSubstr("com.foo.Bar")); } TEST(ProguardRulesTest, NonLayoutReferencesAreUnconditional) { @@ -169,12 +263,16 @@ TEST(ProguardRulesTest, NonLayoutReferencesAreUnconditional) { proguard::KeepSet set = proguard::KeepSet(true); set.AddReference({test::ParseNameOrDie("style/MyStyle"), {}}, layout->file.name); - ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set)); - - std::string actual = GetKeepSetString(set); + ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set)); + std::string actual = GetKeepSetString(set, /** minimal_rules */ false); EXPECT_THAT(actual, Not(HasSubstr("-if"))); EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }")); + + actual = GetKeepSetString(set, /** minimal_rules */ true); + EXPECT_THAT(actual, Not(HasSubstr("-if"))); + EXPECT_THAT(actual, HasSubstr( + "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }")); } TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) { @@ -185,11 +283,15 @@ TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) { 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); + std::string actual = GetKeepSetString(set, /** minimal_rules */ false); + EXPECT_THAT(actual, HasSubstr( + "-keepclassmembers class * { *** bar_method(android.view.View); }")); - EXPECT_THAT(actual, HasSubstr("bar_method")); + actual = GetKeepSetString(set, /** minimal_rules */ true); + EXPECT_THAT(actual, HasSubstr( + "-keepclassmembers class * { *** bar_method(android.view.View); }")); } TEST(ProguardRulesTest, MenuRulesAreEmitted) { @@ -204,14 +306,59 @@ TEST(ProguardRulesTest, MenuRulesAreEmitted) { 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); + std::string actual = GetKeepSetString(set, /** minimal_rules */ false); + EXPECT_THAT(actual, HasSubstr( + "-keepclassmembers class * { *** on_click(android.view.MenuItem); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(...); }")); + EXPECT_THAT(actual, Not(HasSubstr("com.foo.Bat"))); - EXPECT_THAT(actual, HasSubstr("on_click")); - EXPECT_THAT(actual, HasSubstr("com.foo.Bar")); - EXPECT_THAT(actual, HasSubstr("com.foo.Baz")); + actual = GetKeepSetString(set, /** minimal_rules */ true); + EXPECT_THAT(actual, HasSubstr( + "-keepclassmembers class * { *** on_click(android.view.MenuItem); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(android.content.Context); }")); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(android.content.Context); }")); EXPECT_THAT(actual, Not(HasSubstr("com.foo.Bat"))); } +TEST(ProguardRulesTest, TransitionPathMotionRulesAreEmitted) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); + std::unique_ptr<xml::XmlResource> transition = test::BuildXmlDom(R"( + <changeBounds> + <pathMotion class="com.foo.Bar"/> + </changeBounds>)"); + transition->file.name = test::ParseNameOrDie("transition/foo"); + + proguard::KeepSet set; + ASSERT_TRUE(proguard::CollectProguardRules(context.get(), transition.get(), &set)); + + std::string actual = GetKeepSetString(set, /** minimal_rules */ false); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }")); + + actual = GetKeepSetString(set, /** minimal_rules */ true); + EXPECT_THAT(actual, HasSubstr( + "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }")); +} + +TEST(ProguardRulesTest, TransitionRulesAreEmitted) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); + std::unique_ptr<xml::XmlResource> transitionSet = test::BuildXmlDom(R"( + <transitionSet> + <transition class="com.foo.Bar"/> + </transitionSet>)"); + transitionSet->file.name = test::ParseNameOrDie("transition/foo"); + + proguard::KeepSet set; + ASSERT_TRUE(proguard::CollectProguardRules(context.get(), transitionSet.get(), &set)); + + std::string actual = GetKeepSetString(set, /** minimal_rules */ false); + EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }")); + + actual = GetKeepSetString(set, /** minimal_rules */ true); + EXPECT_THAT(actual, HasSubstr( + "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }")); +} + } // namespace aapt |