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, false);
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, 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, MenuRulesAreEmittedForActionClasses) {
330 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
331 std::unique_ptr<xml::XmlResource> menu = test::BuildXmlDom(R"(
332 <menu xmlns:android="http://schemas.android.com/apk/res/android"
333 xmlns:app="http://schemas.android.com/apk/res-auto">
334 <item android:id="@+id/my_item"
335 app:actionViewClass="com.foo.Bar"
336 app:actionProviderClass="com.foo.Baz" />
337 </menu>)");
338 menu->file.name = test::ParseNameOrDie("menu/foo");
339
340 proguard::KeepSet set;
341 ASSERT_TRUE(proguard::CollectProguardRules(context.get(), menu.get(), &set));
342
343 std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
344 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar"));
345 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz"));
346 }
347
348 TEST(ProguardRulesTest, TransitionPathMotionRulesAreEmitted) {
349 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
350 std::unique_ptr<xml::XmlResource> transition = test::BuildXmlDom(R"(
351 <changeBounds>
352 <pathMotion class="com.foo.Bar"/>
353 </changeBounds>)");
354 transition->file.name = test::ParseNameOrDie("transition/foo");
355
356 proguard::KeepSet set;
357 ASSERT_TRUE(proguard::CollectProguardRules(context.get(), transition.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 TEST(ProguardRulesTest, TransitionRulesAreEmitted) {
368 std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
369 std::unique_ptr<xml::XmlResource> transitionSet = test::BuildXmlDom(R"(
370 <transitionSet>
371 <transition class="com.foo.Bar"/>
372 </transitionSet>)");
373 transitionSet->file.name = test::ParseNameOrDie("transition/foo");
374
375 proguard::KeepSet set;
376 ASSERT_TRUE(proguard::CollectProguardRules(context.get(), transitionSet.get(), &set));
377
378 std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
379 EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
380
381 actual = GetKeepSetString(set, /** minimal_rules */ true);
382 EXPECT_THAT(actual, HasSubstr(
383 "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
384 }
385
386 TEST(ProguardRulesTest, UsageLocationComparator) {
387 proguard::UsageLocation location1 = {{"pkg", ResourceType::kAttr, "x"}};
388 proguard::UsageLocation location2 = {{"pkg", ResourceType::kAttr, "y"}};
389
390 EXPECT_EQ(location1 < location2, true);
391 EXPECT_EQ(location2 < location1, false);
392 }
393
394 } // namespace aapt
395