Files
frameworks_base/tools/aapt2/java/ProguardRules_test.cpp
Ryan Mitchell 7e5236dc56 Restore Proguard behavior and add minimal flag
We previously changed AAPT2 to correctly only generate keep rules for
the constructors required to inflate the different views. This cause
projects that did not have keep rules for the other constructors that
were accessed through reflection to have runtime crashes. This change
adds a flag to the link stage (--proguard-minimal-keep-rules) that
allows AAPT2 to only keep the constructors required for layout
inflation. If the flag is not present, then AAPT2 will generate less
specific keep rules than keep all constructors.

Bug: 116201243
Test: aapt2_tests
Change-Id: I8bb5cdf8446518ab153ea988e1243ca9494258c7
2018-09-25 15:20:59 -07:00

364 lines
16 KiB
C++

/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "java/ProguardRules.h"
#include "link/Linkers.h"
#include "io/StringStream.h"
#include "test/Test.h"
using ::aapt::io::StringOutputStream;
using ::testing::HasSubstr;
using ::testing::Not;
namespace aapt {
std::string GetKeepSetString(const proguard::KeepSet& set, bool minimal_rules) {
std::string out;
StringOutputStream sout(&out);
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"(
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:name="com.foo.Bar"/>)");
layout->file.name = test::ParseNameOrDie("layout/foo");
proguard::KeepSet 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>(...); }"));
actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
}
TEST(ProguardRulesTest, FragmentClassRuleIsEmitted) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<xml::XmlResource> layout =
test::BuildXmlDom(R"(<fragment class="com.foo.Bar"/>)");
layout->file.name = test::ParseNameOrDie("layout/foo");
proguard::KeepSet 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>(...); }"));
actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
}
TEST(ProguardRulesTest, FragmentNameAndClassRulesAreEmitted) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:name="com.foo.Baz"
class="com.foo.Bar"/>)");
layout->file.name = test::ParseNameOrDie("layout/foo");
proguard::KeepSet 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, /** 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>(...); }"));
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) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
<View xmlns:android="http://schemas.android.com/apk/res/android">
<com.foo.Bar />
</View>)");
layout->file.name = test::ParseNameOrDie("layout/foo");
proguard::KeepSet 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>(...); }"));
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) {
std::unique_ptr<xml::XmlResource> bar_layout = test::BuildXmlDom(R"(
<View xmlns:android="http://schemas.android.com/apk/res/android">
<com.foo.Bar />
</View>)");
bar_layout->file.name = test::ParseNameOrDie("com.foo:layout/bar");
ResourceTable table;
StdErrDiagnostics errDiagnostics;
table.AddResource(bar_layout->file.name, ConfigDescription::DefaultConfig(), "",
util::make_unique<FileReference>(), &errDiagnostics);
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
.SetCompilationPackage("com.foo")
.AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(&table))
.Build();
std::unique_ptr<xml::XmlResource> foo_layout = test::BuildXmlDom(R"(
<View xmlns:android="http://schemas.android.com/apk/res/android">
<include layout="@layout/bar" />
</View>)");
foo_layout->file.name = test::ParseNameOrDie("com.foo:layout/foo");
XmlReferenceLinker xml_linker;
ASSERT_TRUE(xml_linker.Consume(context.get(), bar_layout.get()));
ASSERT_TRUE(xml_linker.Consume(context.get(), foo_layout.get()));
proguard::KeepSet set = proguard::KeepSet(true);
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"));
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) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
<View xmlns:android="http://schemas.android.com/apk/res/android">
<com.foo.Bar />
</View>)");
layout->file.name = test::ParseNameOrDie("layout/foo");
proguard::KeepSet set = proguard::KeepSet(true);
set.AddReference({test::ParseNameOrDie("layout/bar"), {}}, layout->file.name);
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("-if class **.R$layout"));
EXPECT_THAT(actual, HasSubstr("int foo"));
EXPECT_THAT(actual, HasSubstr("int bar"));
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"));
}
TEST(ProguardRulesTest, NonLayoutReferencesAreUnconditional) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
<View xmlns:android="http://schemas.android.com/apk/res/android">
<com.foo.Bar />
</View>)");
layout->file.name = test::ParseNameOrDie("layout/foo");
proguard::KeepSet set = proguard::KeepSet(true);
set.AddReference({test::ParseNameOrDie("style/MyStyle"), {}}, layout->file.name);
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) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:onClick="bar_method" />)");
layout->file.name = test::ParseNameOrDie("layout/foo");
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
EXPECT_THAT(actual, HasSubstr(
"-keepclassmembers class * { *** bar_method(android.view.View); }"));
actual = GetKeepSetString(set, /** minimal_rules */ true);
EXPECT_THAT(actual, HasSubstr(
"-keepclassmembers class * { *** bar_method(android.view.View); }"));
}
TEST(ProguardRulesTest, MenuRulesAreEmitted) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<xml::XmlResource> menu = test::BuildXmlDom(R"(
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:onClick="on_click"
android:actionViewClass="com.foo.Bar"
android:actionProviderClass="com.foo.Baz"
android:name="com.foo.Bat" />
</menu>)");
menu->file.name = test::ParseNameOrDie("menu/foo");
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(context.get(), menu.get(), &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")));
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