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