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
GetKeepSetString(const proguard::KeepSet & set,bool minimal_rules)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
TEST(ProguardRulesTest,ManifestRuleDefaultConstructorOnly)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, FragmentContainerViewNameRuleIsEmitted) {
135 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
136 std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
137 <androidx.fragment.app.FragmentContainerView
138 xmlns:android="http://schemas.android.com/apk/res/android"
139 android:name="com.foo.Bar"/>)");
140 layout->file.name = test::ParseNameOrDie("layout/foo");
141
142 proguard::KeepSet set;
143 ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
144
145 std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
146 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
147
148 actual = GetKeepSetString(set, /** minimal_rules */ true);
149 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
150 }
151
152 TEST(ProguardRulesTest, FragmentContainerViewClassRuleIsEmitted) {
153 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
154 std::unique_ptr<xml::XmlResource> layout =
155 test::BuildXmlDom(R"(<androidx.fragment.app.FragmentContainerView class="com.foo.Bar"/>)");
156 layout->file.name = test::ParseNameOrDie("layout/foo");
157
158 proguard::KeepSet set;
159 ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
160
161 std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
162 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
163
164 actual = GetKeepSetString(set, /** minimal_rules */ true);
165 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
166 }
167
168 TEST(ProguardRulesTest, FragmentContainerViewNameAndClassRulesAreEmitted) {
169 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
170 std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
171 <androidx.fragment.app.FragmentContainerView
172 xmlns:android="http://schemas.android.com/apk/res/android"
173 android:name="com.foo.Baz"
174 class="com.foo.Bar"/>)");
175 layout->file.name = test::ParseNameOrDie("layout/foo");
176
177 proguard::KeepSet set;
178 ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
179
180 std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
181 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
182 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(...); }"));
183
184 actual = GetKeepSetString(set, /** minimal_rules */ true);
185 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
186 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(); }"));
187 }
188
189 TEST(ProguardRulesTest, NavigationFragmentNameAndClassRulesAreEmitted) {
190 std::unique_ptr<IAaptContext> context = test::ContextBuilder()
191 .SetCompilationPackage("com.base").Build();
192 std::unique_ptr<xml::XmlResource> navigation = test::BuildXmlDom(R"(
193 <navigation
194 xmlns:android="http://schemas.android.com/apk/res/android"
195 xmlns:app="http://schemas.android.com/apk/res-auto">
196 <custom android:id="@id/foo"
197 android:name="com.package.Foo"/>
198 <fragment android:id="@id/bar"
199 android:name="com.package.Bar">
200 <nested android:id="@id/nested"
201 android:name=".Nested"/>
202 </fragment>
203 </navigation>
204 )");
205
206 navigation->file.name = test::ParseNameOrDie("navigation/graph.xml");
207
208 proguard::KeepSet set;
209 ASSERT_TRUE(proguard::CollectProguardRules(context.get(), navigation.get(), &set));
210
211 std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
212 EXPECT_THAT(actual, HasSubstr("-keep class com.package.Foo { <init>(...); }"));
213 EXPECT_THAT(actual, HasSubstr("-keep class com.package.Bar { <init>(...); }"));
214 EXPECT_THAT(actual, HasSubstr("-keep class com.base.Nested { <init>(...); }"));
215
216 actual = GetKeepSetString(set, /** minimal_rules */ true);
217 EXPECT_THAT(actual, HasSubstr("-keep class com.package.Foo { <init>(...); }"));
218 EXPECT_THAT(actual, HasSubstr("-keep class com.package.Bar { <init>(...); }"));
219 EXPECT_THAT(actual, HasSubstr("-keep class com.base.Nested { <init>(...); }"));
220 }
221
222 TEST(ProguardRulesTest, CustomViewRulesAreEmitted) {
223 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
224 std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
225 <View xmlns:android="http://schemas.android.com/apk/res/android">
226 <com.foo.Bar />
227 </View>)");
228 layout->file.name = test::ParseNameOrDie("layout/foo");
229
230 proguard::KeepSet set;
231 ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
232
233 std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
234 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
235
236 actual = GetKeepSetString(set, /** minimal_rules */ true);
237 EXPECT_THAT(actual, HasSubstr(
238 "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
239 }
240
241 TEST(ProguardRulesTest, IncludedLayoutRulesAreConditional) {
242 std::unique_ptr<xml::XmlResource> bar_layout = test::BuildXmlDom(R"(
243 <View xmlns:android="http://schemas.android.com/apk/res/android">
244 <com.foo.Bar />
245 </View>)");
246 bar_layout->file.name = test::ParseNameOrDie("com.foo:layout/bar");
247
248 ResourceTable table;
249 StdErrDiagnostics errDiagnostics;
250 table.AddResource(bar_layout->file.name, ConfigDescription::DefaultConfig(), "",
251 util::make_unique<FileReference>(), &errDiagnostics);
252
253 std::unique_ptr<IAaptContext> context =
254 test::ContextBuilder()
255 .SetCompilationPackage("com.foo")
256 .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(&table))
257 .Build();
258
259 std::unique_ptr<xml::XmlResource> foo_layout = test::BuildXmlDom(R"(
260 <View xmlns:android="http://schemas.android.com/apk/res/android">
261 <include layout="@layout/bar" />
262 </View>)");
263 foo_layout->file.name = test::ParseNameOrDie("com.foo:layout/foo");
264
265 XmlReferenceLinker xml_linker;
266 ASSERT_TRUE(xml_linker.Consume(context.get(), bar_layout.get()));
267 ASSERT_TRUE(xml_linker.Consume(context.get(), foo_layout.get()));
268
269 proguard::KeepSet set = proguard::KeepSet(true);
270 ASSERT_TRUE(proguard::CollectProguardRules(context.get(), bar_layout.get(), &set));
271 ASSERT_TRUE(proguard::CollectProguardRules(context.get(), foo_layout.get(), &set));
272
273 std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
274 EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
275 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
276 EXPECT_THAT(actual, HasSubstr("int foo"));
277 EXPECT_THAT(actual, HasSubstr("int bar"));
278
279 actual = GetKeepSetString(set, /** minimal_rules */ true);
280 EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
281 EXPECT_THAT(actual, HasSubstr(
282 "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
283 EXPECT_THAT(actual, HasSubstr("int foo"));
284 EXPECT_THAT(actual, HasSubstr("int bar"));
285 }
286
287 TEST(ProguardRulesTest, AliasedLayoutRulesAreConditional) {
288 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
289 std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
290 <View xmlns:android="http://schemas.android.com/apk/res/android">
291 <com.foo.Bar />
292 </View>)");
293 layout->file.name = test::ParseNameOrDie("layout/foo");
294
295 proguard::KeepSet set = proguard::KeepSet(true);
296 set.AddReference({test::ParseNameOrDie("layout/bar"), {}}, layout->file.name);
297 ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
298
299 std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
300 EXPECT_THAT(actual, HasSubstr(
301 "-keep class com.foo.Bar { <init>(...); }"));
302 EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
303 EXPECT_THAT(actual, HasSubstr("int foo"));
304 EXPECT_THAT(actual, HasSubstr("int bar"));
305
306 actual = GetKeepSetString(set, /** minimal_rules */ true);
307 EXPECT_THAT(actual, HasSubstr(
308 "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
309 EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
310 EXPECT_THAT(actual, HasSubstr("int foo"));
311 EXPECT_THAT(actual, HasSubstr("int bar"));
312 }
313
314 TEST(ProguardRulesTest, NonLayoutReferencesAreUnconditional) {
315 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
316 std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
317 <View xmlns:android="http://schemas.android.com/apk/res/android">
318 <com.foo.Bar />
319 </View>)");
320 layout->file.name = test::ParseNameOrDie("layout/foo");
321
322 proguard::KeepSet set = proguard::KeepSet(true);
323 set.AddReference({test::ParseNameOrDie("style/MyStyle"), {}}, layout->file.name);
324 ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
325
326 std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
327 EXPECT_THAT(actual, Not(HasSubstr("-if")));
328 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
329
330 actual = GetKeepSetString(set, /** minimal_rules */ true);
331 EXPECT_THAT(actual, Not(HasSubstr("-if")));
332 EXPECT_THAT(actual, HasSubstr(
333 "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
334 }
335
336 TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) {
337 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
338 std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
339 <View xmlns:android="http://schemas.android.com/apk/res/android"
340 android:onClick="bar_method" />)");
341 layout->file.name = test::ParseNameOrDie("layout/foo");
342
343 proguard::KeepSet set;
344 ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
345
346 std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
347 EXPECT_THAT(actual, HasSubstr(
348 "-keepclassmembers class * { *** bar_method(android.view.View); }"));
349
350 actual = GetKeepSetString(set, /** minimal_rules */ true);
351 EXPECT_THAT(actual, HasSubstr(
352 "-keepclassmembers class * { *** bar_method(android.view.View); }"));
353 }
354
355 TEST(ProguardRulesTest, MenuRulesAreEmitted) {
356 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
357 std::unique_ptr<xml::XmlResource> menu = test::BuildXmlDom(R"(
358 <menu xmlns:android="http://schemas.android.com/apk/res/android">
359 <item android:onClick="on_click"
360 android:actionViewClass="com.foo.Bar"
361 android:actionProviderClass="com.foo.Baz"
362 android:name="com.foo.Bat" />
363 </menu>)");
364 menu->file.name = test::ParseNameOrDie("menu/foo");
365
366 proguard::KeepSet set;
367 ASSERT_TRUE(proguard::CollectProguardRules(context.get(), menu.get(), &set));
368
369 std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
370 EXPECT_THAT(actual, HasSubstr(
371 "-keepclassmembers class * { *** on_click(android.view.MenuItem); }"));
372 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
373 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(...); }"));
374 EXPECT_THAT(actual, Not(HasSubstr("com.foo.Bat")));
375
376 actual = GetKeepSetString(set, /** minimal_rules */ true);
377 EXPECT_THAT(actual, HasSubstr(
378 "-keepclassmembers class * { *** on_click(android.view.MenuItem); }"));
379 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(android.content.Context); }"));
380 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(android.content.Context); }"));
381 EXPECT_THAT(actual, Not(HasSubstr("com.foo.Bat")));
382 }
383
384 TEST(ProguardRulesTest, TransitionPathMotionRulesAreEmitted) {
385 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
386 std::unique_ptr<xml::XmlResource> transition = test::BuildXmlDom(R"(
387 <changeBounds>
388 <pathMotion class="com.foo.Bar"/>
389 </changeBounds>)");
390 transition->file.name = test::ParseNameOrDie("transition/foo");
391
392 proguard::KeepSet set;
393 ASSERT_TRUE(proguard::CollectProguardRules(context.get(), transition.get(), &set));
394
395 std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
396 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
397
398 actual = GetKeepSetString(set, /** minimal_rules */ true);
399 EXPECT_THAT(actual, HasSubstr(
400 "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
401 }
402
403 TEST(ProguardRulesTest, TransitionRulesAreEmitted) {
404 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
405 std::unique_ptr<xml::XmlResource> transitionSet = test::BuildXmlDom(R"(
406 <transitionSet>
407 <transition class="com.foo.Bar"/>
408 </transitionSet>)");
409 transitionSet->file.name = test::ParseNameOrDie("transition/foo");
410
411 proguard::KeepSet set;
412 ASSERT_TRUE(proguard::CollectProguardRules(context.get(), transitionSet.get(), &set));
413
414 std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
415 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
416
417 actual = GetKeepSetString(set, /** minimal_rules */ true);
418 EXPECT_THAT(actual, HasSubstr(
419 "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
420 }
421
422 TEST(ProguardRulesTest, UsageLocationComparator) {
423 proguard::UsageLocation location1 = {{"pkg", ResourceType::kAttr, "x"}};
424 proguard::UsageLocation location2 = {{"pkg", ResourceType::kAttr, "y"}};
425
426 EXPECT_EQ(location1 < location2, true);
427 EXPECT_EQ(location2 < location1, false);
428 }
429
430 } // namespace aapt
431