Home | History | Annotate | Download | only in java
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "java/ProguardRules.h"
     18 #include "link/Linkers.h"
     19 
     20 #include "io/StringStream.h"
     21 #include "test/Test.h"
     22 
     23 using ::aapt::io::StringOutputStream;
     24 using ::android::ConfigDescription;
     25 using ::testing::HasSubstr;
     26 using ::testing::Not;
     27 
     28 namespace aapt {
     29 
     30 std::string GetKeepSetString(const proguard::KeepSet& set, bool minimal_rules) {
     31   std::string out;
     32   StringOutputStream sout(&out);
     33   proguard::WriteKeepSet(set, &sout, minimal_rules);
     34   sout.Flush();
     35   return out;
     36 }
     37 
     38 TEST(ProguardRulesTest, ManifestRuleDefaultConstructorOnly) {
     39   std::unique_ptr<xml::XmlResource> manifest = test::BuildXmlDom(R"(
     40       <manifest xmlns:android="http://schemas.android.com/apk/res/android">
     41         <application
     42             android:appComponentFactory="com.foo.BarAppComponentFactory"
     43             android:backupAgent="com.foo.BarBackupAgent"
     44             android:name="com.foo.BarApplication"
     45             android:zygotePreloadName="com.foo.BarZygotePreload"
     46             >
     47           <activity android:name="com.foo.BarActivity"/>
     48           <service android:name="com.foo.BarService"/>
     49           <receiver android:name="com.foo.BarReceiver"/>
     50           <provider android:name="com.foo.BarProvider"/>
     51         </application>
     52         <instrumentation android:name="com.foo.BarInstrumentation"/>
     53       </manifest>)");
     54 
     55   proguard::KeepSet set;
     56   ASSERT_TRUE(proguard::CollectProguardRulesForManifest(manifest.get(), &set, false));
     57 
     58   std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
     59   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarAppComponentFactory { <init>(); }"));
     60   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarBackupAgent { <init>(); }"));
     61   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarApplication { <init>(); }"));
     62   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarActivity { <init>(); }"));
     63   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarService { <init>(); }"));
     64   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarReceiver { <init>(); }"));
     65   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarProvider { <init>(); }"));
     66   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarInstrumentation { <init>(); }"));
     67   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarZygotePreload { <init>(); }"));
     68 
     69   actual = GetKeepSetString(set, /** minimal_rules */ true);
     70   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarAppComponentFactory { <init>(); }"));
     71   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarBackupAgent { <init>(); }"));
     72   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarApplication { <init>(); }"));
     73   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarActivity { <init>(); }"));
     74   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarService { <init>(); }"));
     75   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarReceiver { <init>(); }"));
     76   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarProvider { <init>(); }"));
     77   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarInstrumentation { <init>(); }"));
     78   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarZygotePreload { <init>(); }"));
     79 }
     80 
     81 TEST(ProguardRulesTest, FragmentNameRuleIsEmitted) {
     82   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
     83   std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
     84       <fragment xmlns:android="http://schemas.android.com/apk/res/android"
     85           android:name="com.foo.Bar"/>)");
     86   layout->file.name = test::ParseNameOrDie("layout/foo");
     87 
     88   proguard::KeepSet set;
     89   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
     90 
     91   std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
     92   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
     93 
     94   actual = GetKeepSetString(set, /** minimal_rules */ true);
     95   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
     96 }
     97 
     98 TEST(ProguardRulesTest, FragmentClassRuleIsEmitted) {
     99   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
    100   std::unique_ptr<xml::XmlResource> layout =
    101       test::BuildXmlDom(R"(<fragment class="com.foo.Bar"/>)");
    102   layout->file.name = test::ParseNameOrDie("layout/foo");
    103 
    104   proguard::KeepSet set;
    105   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
    106 
    107   std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
    108   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
    109 
    110   actual = GetKeepSetString(set, /** minimal_rules */ true);
    111   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
    112 }
    113 
    114 TEST(ProguardRulesTest, FragmentNameAndClassRulesAreEmitted) {
    115   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
    116   std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
    117       <fragment xmlns:android="http://schemas.android.com/apk/res/android"
    118           android:name="com.foo.Baz"
    119           class="com.foo.Bar"/>)");
    120   layout->file.name = test::ParseNameOrDie("layout/foo");
    121 
    122   proguard::KeepSet set;
    123   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
    124 
    125   std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
    126   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
    127   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(...); }"));
    128 
    129   actual = GetKeepSetString(set, /** minimal_rules */ true);
    130   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
    131   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(); }"));
    132 }
    133 
    134 TEST(ProguardRulesTest, NavigationFragmentNameAndClassRulesAreEmitted) {
    135   std::unique_ptr<IAaptContext> context = test::ContextBuilder()
    136       .SetCompilationPackage("com.base").Build();
    137   std::unique_ptr<xml::XmlResource> navigation = test::BuildXmlDom(R"(
    138       <navigation
    139           xmlns:android="http://schemas.android.com/apk/res/android"
    140           xmlns:app="http://schemas.android.com/apk/res-auto">
    141           <custom android:id="@id/foo"
    142               android:name="com.package.Foo"/>
    143           <fragment android:id="@id/bar"
    144               android:name="com.package.Bar">
    145               <nested android:id="@id/nested"
    146                   android:name=".Nested"/>
    147           </fragment>
    148       </navigation>
    149   )");
    150 
    151   navigation->file.name = test::ParseNameOrDie("navigation/graph.xml");
    152 
    153   proguard::KeepSet set;
    154   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), navigation.get(), &set));
    155 
    156   std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
    157   EXPECT_THAT(actual, HasSubstr("-keep class com.package.Foo { <init>(...); }"));
    158   EXPECT_THAT(actual, HasSubstr("-keep class com.package.Bar { <init>(...); }"));
    159   EXPECT_THAT(actual, HasSubstr("-keep class com.base.Nested { <init>(...); }"));
    160 
    161   actual = GetKeepSetString(set, /** minimal_rules */ true);
    162   EXPECT_THAT(actual, HasSubstr("-keep class com.package.Foo { <init>(...); }"));
    163   EXPECT_THAT(actual, HasSubstr("-keep class com.package.Bar { <init>(...); }"));
    164   EXPECT_THAT(actual, HasSubstr("-keep class com.base.Nested { <init>(...); }"));
    165 }
    166 
    167 TEST(ProguardRulesTest, CustomViewRulesAreEmitted) {
    168   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
    169   std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
    170       <View xmlns:android="http://schemas.android.com/apk/res/android">
    171         <com.foo.Bar />
    172       </View>)");
    173   layout->file.name = test::ParseNameOrDie("layout/foo");
    174 
    175   proguard::KeepSet set;
    176   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
    177 
    178   std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
    179   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
    180 
    181   actual = GetKeepSetString(set, /** minimal_rules */ true);
    182   EXPECT_THAT(actual, HasSubstr(
    183       "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
    184 }
    185 
    186 TEST(ProguardRulesTest, IncludedLayoutRulesAreConditional) {
    187   std::unique_ptr<xml::XmlResource> bar_layout = test::BuildXmlDom(R"(
    188       <View xmlns:android="http://schemas.android.com/apk/res/android">
    189         <com.foo.Bar />
    190       </View>)");
    191   bar_layout->file.name = test::ParseNameOrDie("com.foo:layout/bar");
    192 
    193   ResourceTable table;
    194   StdErrDiagnostics errDiagnostics;
    195   table.AddResource(bar_layout->file.name, ConfigDescription::DefaultConfig(), "",
    196                     util::make_unique<FileReference>(), &errDiagnostics);
    197 
    198   std::unique_ptr<IAaptContext> context =
    199       test::ContextBuilder()
    200           .SetCompilationPackage("com.foo")
    201           .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(&table))
    202           .Build();
    203 
    204   std::unique_ptr<xml::XmlResource> foo_layout = test::BuildXmlDom(R"(
    205       <View xmlns:android="http://schemas.android.com/apk/res/android">
    206         <include layout="@layout/bar" />
    207       </View>)");
    208   foo_layout->file.name = test::ParseNameOrDie("com.foo:layout/foo");
    209 
    210   XmlReferenceLinker xml_linker;
    211   ASSERT_TRUE(xml_linker.Consume(context.get(), bar_layout.get()));
    212   ASSERT_TRUE(xml_linker.Consume(context.get(), foo_layout.get()));
    213 
    214   proguard::KeepSet set = proguard::KeepSet(true);
    215   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), bar_layout.get(), &set));
    216   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), foo_layout.get(), &set));
    217 
    218   std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
    219   EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
    220   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
    221   EXPECT_THAT(actual, HasSubstr("int foo"));
    222   EXPECT_THAT(actual, HasSubstr("int bar"));
    223 
    224   actual = GetKeepSetString(set, /** minimal_rules */ true);
    225   EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
    226   EXPECT_THAT(actual, HasSubstr(
    227     "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
    228   EXPECT_THAT(actual, HasSubstr("int foo"));
    229   EXPECT_THAT(actual, HasSubstr("int bar"));
    230 }
    231 
    232 TEST(ProguardRulesTest, AliasedLayoutRulesAreConditional) {
    233   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
    234   std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
    235       <View xmlns:android="http://schemas.android.com/apk/res/android">
    236         <com.foo.Bar />
    237       </View>)");
    238   layout->file.name = test::ParseNameOrDie("layout/foo");
    239 
    240   proguard::KeepSet set = proguard::KeepSet(true);
    241   set.AddReference({test::ParseNameOrDie("layout/bar"), {}}, layout->file.name);
    242   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
    243 
    244   std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
    245   EXPECT_THAT(actual, HasSubstr(
    246       "-keep class com.foo.Bar { <init>(...); }"));
    247   EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
    248   EXPECT_THAT(actual, HasSubstr("int foo"));
    249   EXPECT_THAT(actual, HasSubstr("int bar"));
    250 
    251   actual = GetKeepSetString(set, /** minimal_rules */ true);
    252   EXPECT_THAT(actual, HasSubstr(
    253     "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
    254   EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
    255   EXPECT_THAT(actual, HasSubstr("int foo"));
    256   EXPECT_THAT(actual, HasSubstr("int bar"));
    257 }
    258 
    259 TEST(ProguardRulesTest, NonLayoutReferencesAreUnconditional) {
    260   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
    261   std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
    262       <View xmlns:android="http://schemas.android.com/apk/res/android">
    263         <com.foo.Bar />
    264       </View>)");
    265   layout->file.name = test::ParseNameOrDie("layout/foo");
    266 
    267   proguard::KeepSet set = proguard::KeepSet(true);
    268   set.AddReference({test::ParseNameOrDie("style/MyStyle"), {}}, layout->file.name);
    269   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
    270 
    271   std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
    272   EXPECT_THAT(actual, Not(HasSubstr("-if")));
    273   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
    274 
    275   actual = GetKeepSetString(set, /** minimal_rules */ true);
    276   EXPECT_THAT(actual, Not(HasSubstr("-if")));
    277   EXPECT_THAT(actual, HasSubstr(
    278     "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
    279 }
    280 
    281 TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) {
    282   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
    283   std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
    284       <View xmlns:android="http://schemas.android.com/apk/res/android"
    285           android:onClick="bar_method" />)");
    286   layout->file.name = test::ParseNameOrDie("layout/foo");
    287 
    288   proguard::KeepSet set;
    289   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
    290 
    291   std::string actual = GetKeepSetString(set,  /** minimal_rules */ false);
    292   EXPECT_THAT(actual, HasSubstr(
    293       "-keepclassmembers class * { *** bar_method(android.view.View); }"));
    294 
    295   actual = GetKeepSetString(set,  /** minimal_rules */ true);
    296   EXPECT_THAT(actual, HasSubstr(
    297     "-keepclassmembers class * { *** bar_method(android.view.View); }"));
    298 }
    299 
    300 TEST(ProguardRulesTest, MenuRulesAreEmitted) {
    301   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
    302   std::unique_ptr<xml::XmlResource> menu = test::BuildXmlDom(R"(
    303       <menu xmlns:android="http://schemas.android.com/apk/res/android">
    304         <item android:onClick="on_click"
    305             android:actionViewClass="com.foo.Bar"
    306             android:actionProviderClass="com.foo.Baz"
    307             android:name="com.foo.Bat" />
    308       </menu>)");
    309   menu->file.name = test::ParseNameOrDie("menu/foo");
    310 
    311   proguard::KeepSet set;
    312   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), menu.get(), &set));
    313 
    314   std::string actual = GetKeepSetString(set,  /** minimal_rules */ false);
    315   EXPECT_THAT(actual, HasSubstr(
    316     "-keepclassmembers class * { *** on_click(android.view.MenuItem); }"));
    317   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
    318   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(...); }"));
    319   EXPECT_THAT(actual, Not(HasSubstr("com.foo.Bat")));
    320 
    321   actual = GetKeepSetString(set,  /** minimal_rules */ true);
    322   EXPECT_THAT(actual, HasSubstr(
    323     "-keepclassmembers class * { *** on_click(android.view.MenuItem); }"));
    324   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(android.content.Context); }"));
    325   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(android.content.Context); }"));
    326   EXPECT_THAT(actual, Not(HasSubstr("com.foo.Bat")));
    327 }
    328 
    329 TEST(ProguardRulesTest, TransitionPathMotionRulesAreEmitted) {
    330   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
    331   std::unique_ptr<xml::XmlResource> transition = test::BuildXmlDom(R"(
    332       <changeBounds>
    333         <pathMotion class="com.foo.Bar"/>
    334       </changeBounds>)");
    335   transition->file.name = test::ParseNameOrDie("transition/foo");
    336 
    337   proguard::KeepSet set;
    338   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), transition.get(), &set));
    339 
    340   std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
    341   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
    342 
    343   actual = GetKeepSetString(set, /** minimal_rules */ true);
    344   EXPECT_THAT(actual, HasSubstr(
    345     "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
    346 }
    347 
    348 TEST(ProguardRulesTest, TransitionRulesAreEmitted) {
    349   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
    350   std::unique_ptr<xml::XmlResource> transitionSet = test::BuildXmlDom(R"(
    351       <transitionSet>
    352         <transition class="com.foo.Bar"/>
    353       </transitionSet>)");
    354   transitionSet->file.name = test::ParseNameOrDie("transition/foo");
    355 
    356   proguard::KeepSet set;
    357   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), transitionSet.get(), &set));
    358 
    359   std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
    360   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
    361 
    362   actual = GetKeepSetString(set, /** minimal_rules */ true);
    363   EXPECT_THAT(actual, HasSubstr(
    364     "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
    365 }
    366 
    367 }  // namespace aapt
    368