1 /*
2 * Copyright (C) 2018 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 "Link.h"
18
19 #include "Diagnostics.h"
20 #include "LoadedApk.h"
21 #include "android-base/file.h"
22 #include "android-base/stringprintf.h"
23 #include "test/Test.h"
24
25 using testing::Eq;
26 using testing::HasSubstr;
27 using testing::IsNull;
28 using testing::Ne;
29 using testing::NotNull;
30
31 namespace aapt {
32
33 using LinkTest = CommandTestFixture;
34
TEST_F(LinkTest,RemoveRawXmlStrings)35 TEST_F(LinkTest, RemoveRawXmlStrings) {
36 StdErrDiagnostics diag;
37 const std::string compiled_files_dir = GetTestPath("compiled");
38 ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)",
39 compiled_files_dir, &diag));
40
41 const std::string out_apk = GetTestPath("out.apk");
42 std::vector<std::string> link_args = {
43 "--manifest", GetDefaultManifest(),
44 "-o", out_apk,
45 };
46
47 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
48
49 // Load the binary xml tree
50 android::ResXMLTree tree;
51 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
52 ASSERT_THAT(apk, Ne(nullptr));
53
54 std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
55 ASSERT_THAT(data, Ne(nullptr));
56 AssertLoadXml(apk.get(), data.get(), &tree);
57
58 // Check that the raw string index has not been assigned
59 EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1));
60 }
61
TEST_F(LinkTest,KeepRawXmlStrings)62 TEST_F(LinkTest, KeepRawXmlStrings) {
63 StdErrDiagnostics diag;
64 const std::string compiled_files_dir = GetTestPath("compiled");
65 ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)",
66 compiled_files_dir, &diag));
67
68 const std::string out_apk = GetTestPath("out.apk");
69 std::vector<std::string> link_args = {
70 "--manifest", GetDefaultManifest(),
71 "-o", out_apk,
72 "--keep-raw-values"
73 };
74
75 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
76
77 // Load the binary xml tree
78 android::ResXMLTree tree;
79 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
80 ASSERT_THAT(apk, Ne(nullptr));
81
82 std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
83 ASSERT_THAT(data, Ne(nullptr));
84 AssertLoadXml(apk.get(), data.get(), &tree);
85
86 // Check that the raw string index has been set to the correct string pool entry
87 int32_t raw_index = tree.getAttributeValueStringID(0);
88 ASSERT_THAT(raw_index, Ne(-1));
89 EXPECT_THAT(android::util::GetString(tree.getStrings(), static_cast<size_t>(raw_index)),
90 Eq("007"));
91 }
92
TEST_F(LinkTest,NoCompressAssets)93 TEST_F(LinkTest, NoCompressAssets) {
94 StdErrDiagnostics diag;
95 std::string content(500, 'a');
96 WriteFile(GetTestPath("assets/testtxt"), content);
97 WriteFile(GetTestPath("assets/testtxt2"), content);
98 WriteFile(GetTestPath("assets/test.txt"), content);
99 WriteFile(GetTestPath("assets/test.hello.txt"), content);
100 WriteFile(GetTestPath("assets/test.hello.xml"), content);
101
102 const std::string out_apk = GetTestPath("out.apk");
103 std::vector<std::string> link_args = {
104 "--manifest", GetDefaultManifest(),
105 "-o", out_apk,
106 "-0", ".txt",
107 "-0", "txt2",
108 "-0", ".hello.txt",
109 "-0", "hello.xml",
110 "-A", GetTestPath("assets")
111 };
112
113 ASSERT_TRUE(Link(link_args, &diag));
114
115 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
116 ASSERT_THAT(apk, Ne(nullptr));
117 io::IFileCollection* zip = apk->GetFileCollection();
118 ASSERT_THAT(zip, Ne(nullptr));
119
120 auto file = zip->FindFile("assets/testtxt");
121 ASSERT_THAT(file, Ne(nullptr));
122 EXPECT_TRUE(file->WasCompressed());
123
124 file = zip->FindFile("assets/testtxt2");
125 ASSERT_THAT(file, Ne(nullptr));
126 EXPECT_FALSE(file->WasCompressed());
127
128 file = zip->FindFile("assets/test.txt");
129 ASSERT_THAT(file, Ne(nullptr));
130 EXPECT_FALSE(file->WasCompressed());
131
132 file = zip->FindFile("assets/test.hello.txt");
133 ASSERT_THAT(file, Ne(nullptr));
134 EXPECT_FALSE(file->WasCompressed());
135
136 file = zip->FindFile("assets/test.hello.xml");
137 ASSERT_THAT(file, Ne(nullptr));
138 EXPECT_FALSE(file->WasCompressed());
139 }
140
TEST_F(LinkTest,NoCompressResources)141 TEST_F(LinkTest, NoCompressResources) {
142 StdErrDiagnostics diag;
143 std::string content(500, 'a');
144 const std::string compiled_files_dir = GetTestPath("compiled");
145 ASSERT_TRUE(CompileFile(GetTestPath("res/raw/testtxt"), content, compiled_files_dir, &diag));
146 ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test.txt"), content, compiled_files_dir, &diag));
147 ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test1.hello.txt"), content, compiled_files_dir,
148 &diag));
149 ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test2.goodbye.xml"), content, compiled_files_dir,
150 &diag));
151
152 const std::string out_apk = GetTestPath("out.apk");
153 std::vector<std::string> link_args = {
154 "--manifest", GetDefaultManifest(),
155 "-o", out_apk,
156 "-0", ".txt",
157 "-0", ".hello.txt",
158 "-0", "goodbye.xml",
159 };
160
161 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
162
163 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
164 ASSERT_THAT(apk, Ne(nullptr));
165 io::IFileCollection* zip = apk->GetFileCollection();
166 ASSERT_THAT(zip, Ne(nullptr));
167
168 auto file = zip->FindFile("res/raw/testtxt");
169 ASSERT_THAT(file, Ne(nullptr));
170 EXPECT_TRUE(file->WasCompressed());
171
172 file = zip->FindFile("res/raw/test.txt");
173 ASSERT_THAT(file, Ne(nullptr));
174 EXPECT_FALSE(file->WasCompressed());
175
176 file = zip->FindFile("res/raw/test1.hello.hello.txt");
177 ASSERT_THAT(file, Ne(nullptr));
178 EXPECT_FALSE(file->WasCompressed());
179
180 file = zip->FindFile("res/raw/test2.goodbye.goodbye.xml");
181 ASSERT_THAT(file, Ne(nullptr));
182 EXPECT_FALSE(file->WasCompressed());
183 }
184
TEST_F(LinkTest,OverlayStyles)185 TEST_F(LinkTest, OverlayStyles) {
186 StdErrDiagnostics diag;
187 const std::string compiled_files_dir = GetTestPath("compiled");
188 const std::string override_files_dir = GetTestPath("compiled-override");
189 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
190 R"(<resources>
191 <style name="MyStyle">
192 <item name="android:textColor">#123</item>
193 </style>
194 </resources>)",
195 compiled_files_dir, &diag));
196 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values-override.xml"),
197 R"(<resources>
198 <style name="MyStyle">
199 <item name="android:background">#456</item>
200 </style>
201 </resources>)",
202 override_files_dir, &diag));
203
204
205 const std::string out_apk = GetTestPath("out.apk");
206 std::vector<std::string> link_args = {
207 "--manifest", GetDefaultManifest(kDefaultPackageName),
208 "-o", out_apk,
209 };
210 const auto override_files = file::FindFiles(override_files_dir, &diag);
211 for (const auto &override_file : override_files.value()) {
212 link_args.push_back("-R");
213 link_args.push_back(file::BuildPath({override_files_dir, override_file}));
214 }
215 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
216
217 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
218 ASSERT_THAT(apk, Ne(nullptr));
219
220 const Style* actual_style = test::GetValue<Style>(
221 apk->GetResourceTable(), std::string(kDefaultPackageName) + ":style/MyStyle");
222 ASSERT_NE(actual_style, nullptr);
223 ASSERT_EQ(actual_style->entries.size(), 2);
224 EXPECT_EQ(actual_style->entries[0].key.id, 0x01010098); // android:textColor
225 EXPECT_EQ(actual_style->entries[1].key.id, 0x010100d4); // android:background
226 }
227
TEST_F(LinkTest,OverrideStylesInsteadOfOverlaying)228 TEST_F(LinkTest, OverrideStylesInsteadOfOverlaying) {
229 StdErrDiagnostics diag;
230 const std::string compiled_files_dir = GetTestPath("compiled");
231 const std::string override_files_dir = GetTestPath("compiled-override");
232 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
233 R"(<resources>
234 <style name="MyStyle">
235 <item name="android:textColor">#123</item>
236 </style>
237 </resources>)",
238 compiled_files_dir, &diag));
239 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values-override.xml"),
240 R"(<resources>
241 <style name="MyStyle">
242 <item name="android:background">#456</item>
243 </style>
244 </resources>)",
245 override_files_dir, &diag));
246
247
248 const std::string out_apk = GetTestPath("out.apk");
249 std::vector<std::string> link_args = {
250 "--manifest", GetDefaultManifest(kDefaultPackageName),
251 "--override-styles-instead-of-overlaying",
252 "-o", out_apk,
253 };
254 const auto override_files = file::FindFiles(override_files_dir, &diag);
255 for (const auto &override_file : override_files.value()) {
256 link_args.push_back("-R");
257 link_args.push_back(file::BuildPath({override_files_dir, override_file}));
258 }
259 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
260
261 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
262 ASSERT_THAT(apk, Ne(nullptr));
263
264 const Style* actual_style = test::GetValue<Style>(
265 apk->GetResourceTable(), std::string(kDefaultPackageName) + ":style/MyStyle");
266 ASSERT_NE(actual_style, nullptr);
267 ASSERT_EQ(actual_style->entries.size(), 1);
268 EXPECT_EQ(actual_style->entries[0].key.id, 0x010100d4); // android:background
269 }
270
TEST_F(LinkTest,AppInfoWithUsesSplit)271 TEST_F(LinkTest, AppInfoWithUsesSplit) {
272 StdErrDiagnostics diag;
273 const std::string base_files_dir = GetTestPath("base");
274 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
275 R"(<resources>
276 <string name="bar">bar</string>
277 </resources>)",
278 base_files_dir, &diag));
279 const std::string base_apk = GetTestPath("base.apk");
280 std::vector<std::string> link_args = {
281 "--manifest", GetDefaultManifest("com.aapt2.app"),
282 "-o", base_apk,
283 };
284 ASSERT_TRUE(Link(link_args, base_files_dir, &diag));
285
286 const std::string feature_manifest = GetTestPath("feature_manifest.xml");
287 WriteFile(feature_manifest, android::base::StringPrintf(R"(
288 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
289 package="com.aapt2.app" split="feature1">
290 </manifest>)"));
291 const std::string feature_files_dir = GetTestPath("feature");
292 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
293 R"(<resources>
294 <string name="foo">foo</string>
295 </resources>)",
296 feature_files_dir, &diag));
297 const std::string feature_apk = GetTestPath("feature.apk");
298 const std::string feature_package_id = "0x80";
299 link_args = {
300 "--manifest", feature_manifest,
301 "-I", base_apk,
302 "--package-id", feature_package_id,
303 "-o", feature_apk,
304 };
305 ASSERT_TRUE(Link(link_args, feature_files_dir, &diag));
306
307 const std::string feature2_manifest = GetTestPath("feature2_manifest.xml");
308 WriteFile(feature2_manifest, android::base::StringPrintf(R"(
309 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
310 package="com.aapt2.app" split="feature2">
311 <uses-split android:name="feature1"/>
312 </manifest>)"));
313 const std::string feature2_files_dir = GetTestPath("feature2");
314 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
315 R"(<resources>
316 <string-array name="string_array">
317 <item>@string/bar</item>
318 <item>@string/foo</item>
319 </string-array>
320 </resources>)",
321 feature2_files_dir, &diag));
322 const std::string feature2_apk = GetTestPath("feature2.apk");
323 const std::string feature2_package_id = "0x81";
324 link_args = {
325 "--manifest", feature2_manifest,
326 "-I", base_apk,
327 "-I", feature_apk,
328 "--package-id", feature2_package_id,
329 "-o", feature2_apk,
330 };
331 ASSERT_TRUE(Link(link_args, feature2_files_dir, &diag));
332 }
333
334 TEST_F(LinkTest, SharedLibraryAttributeRJava) {
335 StdErrDiagnostics diag;
336 const std::string lib_values =
337 R"(<resources>
338 <attr name="foo"/>
339 <public type="attr" name="foo" id="0x00010001"/>
340 <declare-styleable name="LibraryStyleable">
341 <attr name="foo" />
342 </declare-styleable>
343 </resources>)";
344
345 const std::string client_values =
346 R"(<resources>
347 <attr name="bar" />
348 <declare-styleable name="ClientStyleable">
349 <attr name="com.example.lib:foo" />
350 <attr name="bar" />
351 </declare-styleable>
352 </resources>)";
353
354 // Build a library with a public attribute
355 const std::string lib_res = GetTestPath("library-res");
356 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), lib_values, lib_res, &diag));
357
358 const std::string lib_apk = GetTestPath("library.apk");
359 const std::string lib_java = GetTestPath("library_java");
360 // clang-format off
361 auto lib_manifest = ManifestBuilder(this)
362 .SetPackageName("com.example.lib")
363 .Build();
364
365 auto lib_link_args = LinkCommandBuilder(this)
366 .SetManifestFile(lib_manifest)
367 .AddFlag("--shared-lib")
368 .AddParameter("--java", lib_java)
369 .AddCompiledResDir(lib_res, &diag)
370 .Build(lib_apk);
371 // clang-format on
372 ASSERT_TRUE(Link(lib_link_args, &diag));
373
374 const std::string lib_r_java = lib_java + "/com/example/lib/R.java";
375 std::string lib_r_contents;
376 ASSERT_TRUE(android::base::ReadFileToString(lib_r_java, &lib_r_contents));
377 EXPECT_THAT(lib_r_contents, HasSubstr(" public static int foo=0x00010001;"));
378 EXPECT_THAT(lib_r_contents, HasSubstr(" com.example.lib.R.attr.foo"));
379
380 // Build a client that uses the library attribute in a declare-styleable
381 const std::string client_res = GetTestPath("client-res");
382 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), client_values, client_res, &diag));
383
384 const std::string client_apk = GetTestPath("client.apk");
385 const std::string client_java = GetTestPath("client_java");
386 // clang-format off
387 auto client_manifest = ManifestBuilder(this)
388 .SetPackageName("com.example.client")
389 .Build();
390
391 auto client_link_args = LinkCommandBuilder(this)
392 .SetManifestFile(client_manifest)
393 .AddParameter("--java", client_java)
394 .AddParameter("-I", lib_apk)
395 .AddCompiledResDir(client_res, &diag)
396 .Build(client_apk);
397 // clang-format on
398 ASSERT_TRUE(Link(client_link_args, &diag));
399
400 const std::string client_r_java = client_java + "/com/example/client/R.java";
401 std::string client_r_contents;
402 ASSERT_TRUE(android::base::ReadFileToString(client_r_java, &client_r_contents));
403 EXPECT_THAT(client_r_contents, HasSubstr(" com.example.lib.R.attr.foo, 0x7f010000"));
404 }
405
406 struct SourceXML {
407 std::string res_file_path;
408 std::string file_contents;
409 };
410
411 static void BuildApk(const std::vector<SourceXML>& source_files, const std::string& apk_path,
412 LinkCommandBuilder&& link_args, CommandTestFixture* fixture,
413 android::IDiagnostics* diag) {
414 TemporaryDir res_dir;
415 TemporaryDir compiled_res_dir;
416 for (auto& source_file : source_files) {
417 ASSERT_TRUE(fixture->CompileFile(res_dir.path + source_file.res_file_path,
418 source_file.file_contents, compiled_res_dir.path, diag));
419 }
420 ASSERT_TRUE(fixture->Link(
421 link_args.AddCompiledResDir(compiled_res_dir.path, diag).Build(apk_path), diag));
422 }
423
424 static void BuildSDK(const std::vector<SourceXML>& source_files, const std::string& apk_path,
425 const std::string& java_root_path, CommandTestFixture* fixture,
426 android::IDiagnostics* diag) {
427 auto android_manifest = ManifestBuilder(fixture).SetPackageName("android").Build();
428
429 auto android_link_args = LinkCommandBuilder(fixture)
430 .SetManifestFile(android_manifest)
431 .AddParameter("--private-symbols", "com.android.internal")
432 .AddParameter("--java", java_root_path);
433
434 BuildApk(source_files, apk_path, std::move(android_link_args), fixture, diag);
435 }
436
437 static void BuildNonFinalizedSDK(const std::string& apk_path, const std::string& java_path,
438 CommandTestFixture* fixture, android::IDiagnostics* diag) {
439 const std::string android_values =
440 R"(<resources>
441 <public type="attr" name="finalized_res" id="0x01010001"/>
442
443 <!-- S staged attributes (Not support staged resources in the same type id) -->
444 <staging-public-group type="attr" first-id="0x01fc0050">
445 <public name="staged_s_res" />
446 </staging-public-group>
447
448 <staging-public-group type="string" first-id="0x01fd0080">
449 <public name="staged_s_string" />
450 </staging-public-group>
451
452 <!-- SV2 staged attributes (support staged resources in a separate type id) -->
453 <staging-public-group type="attr" first-id="0x01ff0049">
454 <public name="staged_s2_res" />
455 </staging-public-group>
456
457 <!-- T staged attributes (support staged resources in multiple separate type ids) -->
458 <staging-public-group type="attr" first-id="0x01fe0063">
459 <public name="staged_t_res" />
460 </staging-public-group>
461
462 <attr name="finalized_res" />
463 <attr name="staged_s_res" />
464 <attr name="staged_s2_res" />
465 <attr name="staged_t_res" />
466 <string name="staged_s_string">Hello</string>
467 </resources>)";
468
469 SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values};
470 BuildSDK({source_xml}, apk_path, java_path, fixture, diag);
471 }
472
473 static void BuildFinalizedSDK(const std::string& apk_path, const std::string& java_path,
474 CommandTestFixture* fixture, android::IDiagnostics* diag) {
475 const std::string android_values =
476 R"(<resources>
477 <public type="attr" name="finalized_res" id="0x01010001"/>
478 <public type="attr" name="staged_s_res" id="0x01010002"/>
479 <public type="attr" name="staged_s2_res" id="0x01010003"/>
480 <public type="string" name="staged_s_string" id="0x01020000"/>
481
482 <!-- S staged attributes (Not support staged resources in the same type id) -->
483 <staging-public-group-final type="attr" first-id="0x01fc0050">
484 <public name="staged_s_res" />
485 </staging-public-group-final>
486
487 <staging-public-group-final type="string" first-id="0x01fd0080">
488 <public name="staged_s_string" />
489 </staging-public-group-final>
490
491 <!-- SV2 staged attributes (support staged resources in a separate type id) -->
492 <staging-public-group-final type="attr" first-id="0x01ff0049">
493 <public name="staged_s2_res" />
494 </staging-public-group-final>
495
496 <!-- T staged attributes (support staged resources in multiple separate type ids) -->
497 <staging-public-group type="attr" first-id="0x01fe0063">
498 <public name="staged_t_res" />
499 </staging-public-group>
500
501 <attr name="finalized_res" />
502 <attr name="staged_s_res" />
503 <attr name="staged_s2_res" />
504 <attr name="staged_t_res" />
505 <string name="staged_s_string">Hello</string>
506 </resources>)";
507
508 SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values};
509 BuildSDK({source_xml}, apk_path, java_path, fixture, diag);
510 }
511
512 static void BuildAppAgainstSDK(const std::string& apk_path, const std::string& java_path,
513 const std::string& sdk_path, CommandTestFixture* fixture,
514 android::IDiagnostics* diag) {
515 const std::string app_values =
516 R"(<resources xmlns:android="http://schemas.android.com/apk/res/android">
517 <attr name="bar" />
518 <style name="MyStyle">
519 <item name="android:staged_s_res">@android:string/staged_s_string</item>
520 </style>
521 <declare-styleable name="ClientStyleable">
522 <attr name="android:finalized_res" />
523 <attr name="android:staged_s_res" />
524 <attr name="bar" />
525 </declare-styleable>
526 <public name="MyStyle" type="style" id="0x7f020000" />
527 </resources>)";
528
529 SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = app_values};
530
531 auto app_manifest = ManifestBuilder(fixture).SetPackageName("com.example.app").Build();
532
533 auto app_link_args = LinkCommandBuilder(fixture)
534 .SetManifestFile(app_manifest)
535 .AddParameter("--java", java_path)
536 .AddParameter("-I", sdk_path);
537
538 BuildApk({source_xml}, apk_path, std::move(app_link_args), fixture, diag);
539 }
540
541 TEST_F(LinkTest, StagedAndroidApi) {
542 StdErrDiagnostics diag;
543 const std::string android_apk = GetTestPath("android.apk");
544 const std::string android_java = GetTestPath("android-java");
545 BuildNonFinalizedSDK(android_apk, android_java, this, &diag);
546
547 const std::string android_r_java = android_java + "/android/R.java";
548 std::string android_r_contents;
549 ASSERT_TRUE(android::base::ReadFileToString(android_r_java, &android_r_contents));
550 EXPECT_THAT(android_r_contents, HasSubstr("public static final int finalized_res=0x01010001;"));
551 EXPECT_THAT(
552 android_r_contents,
553 HasSubstr("public static final int staged_s_res; static { staged_s_res=0x01fc0050; }"));
554 EXPECT_THAT(
555 android_r_contents,
556 HasSubstr("public static final int staged_s_string; static { staged_s_string=0x01fd0080; }"));
557 EXPECT_THAT(
558 android_r_contents,
559 HasSubstr("public static final int staged_s2_res; static { staged_s2_res=0x01ff0049; }"));
560 EXPECT_THAT(
561 android_r_contents,
562 HasSubstr("public static final int staged_t_res; static { staged_t_res=0x01fe0063; }"));
563
564 const std::string app_apk = GetTestPath("app.apk");
565 const std::string app_java = GetTestPath("app-java");
566 BuildAppAgainstSDK(app_apk, app_java, android_apk, this, &diag);
567
568 const std::string client_r_java = app_java + "/com/example/app/R.java";
569 std::string client_r_contents;
570 ASSERT_TRUE(android::base::ReadFileToString(client_r_java, &client_r_contents));
571 EXPECT_THAT(client_r_contents, HasSubstr(" 0x01010001, android.R.attr.staged_s_res, 0x7f010000"));
572
573 // Test that the resource ids of staged and non-staged resource can be retrieved
574 android::AssetManager2 am;
575 auto android_asset = android::ApkAssets::Load(android_apk);
576 ASSERT_THAT(android_asset, NotNull());
577 ASSERT_TRUE(am.SetApkAssets({android_asset}));
578
579 auto result = am.GetResourceId("android:attr/finalized_res");
580 ASSERT_TRUE(result.has_value());
581 EXPECT_THAT(*result, Eq(0x01010001));
582
583 result = am.GetResourceId("android:attr/staged_s_res");
584 ASSERT_TRUE(result.has_value());
585 EXPECT_THAT(*result, Eq(0x01fc0050));
586
587 result = am.GetResourceId("android:string/staged_s_string");
588 ASSERT_TRUE(result.has_value());
589 EXPECT_THAT(*result, Eq(0x01fd0080));
590
591 result = am.GetResourceId("android:attr/staged_s2_res");
592 ASSERT_TRUE(result.has_value());
593 EXPECT_THAT(*result, Eq(0x01ff0049));
594
595 result = am.GetResourceId("android:attr/staged_t_res");
596 ASSERT_TRUE(result.has_value());
597 EXPECT_THAT(*result, Eq(0x01fe0063));
598 }
599
600 TEST_F(LinkTest, FinalizedAndroidApi) {
601 StdErrDiagnostics diag;
602 const std::string android_apk = GetTestPath("android.apk");
603 const std::string android_java = GetTestPath("android-java");
604 BuildFinalizedSDK(android_apk, android_java, this, &diag);
605
606 const std::string android_r_java = android_java + "/android/R.java";
607 std::string android_r_contents;
608 ASSERT_TRUE(android::base::ReadFileToString(android_r_java, &android_r_contents));
609 EXPECT_THAT(android_r_contents, HasSubstr("public static final int finalized_res=0x01010001;"));
610 EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s_res=0x01010002;"));
611 EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s_string=0x01020000;"));
612 EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s2_res=0x01010003;"));
613 EXPECT_THAT(
614 android_r_contents,
615 HasSubstr("public static final int staged_t_res; static { staged_t_res=0x01fe0063; }"));
616 ;
617
618 // Build an application against the non-finalized SDK and then load it into an AssetManager with
619 // the finalized SDK.
620 const std::string non_finalized_android_apk = GetTestPath("non-finalized-android.apk");
621 const std::string non_finalized_android_java = GetTestPath("non-finalized-android-java");
622 BuildNonFinalizedSDK(non_finalized_android_apk, non_finalized_android_java, this, &diag);
623
624 const std::string app_apk = GetTestPath("app.apk");
625 const std::string app_java = GetTestPath("app-java");
626 BuildAppAgainstSDK(app_apk, app_java, non_finalized_android_apk, this, &diag);
627
628 android::AssetManager2 am;
629 auto android_asset = android::ApkAssets::Load(android_apk);
630 auto app_against_non_final = android::ApkAssets::Load(app_apk);
631 ASSERT_THAT(android_asset, NotNull());
632 ASSERT_THAT(app_against_non_final, NotNull());
633 ASSERT_TRUE(am.SetApkAssets({android_asset, app_against_non_final}));
634
635 auto result = am.GetResourceId("android:attr/finalized_res");
636 ASSERT_TRUE(result.has_value());
637 EXPECT_THAT(*result, Eq(0x01010001));
638
639 result = am.GetResourceId("android:attr/staged_s_res");
640 ASSERT_TRUE(result.has_value());
641 EXPECT_THAT(*result, Eq(0x01010002));
642
643 result = am.GetResourceId("android:string/staged_s_string");
644 ASSERT_TRUE(result.has_value());
645 EXPECT_THAT(*result, Eq(0x01020000));
646
647 result = am.GetResourceId("android:attr/staged_s2_res");
648 ASSERT_TRUE(result.has_value());
649 EXPECT_THAT(*result, Eq(0x01010003));
650
651 {
652 auto style = am.GetBag(0x7f020000);
653 ASSERT_TRUE(style.has_value());
654
655 auto& entry = (*style)->entries[0];
656 EXPECT_THAT(entry.key, Eq(0x01010002));
657 EXPECT_THAT(entry.value.dataType, Eq(android::Res_value::TYPE_REFERENCE));
658 EXPECT_THAT(entry.value.data, Eq(0x01020000));
659 }
660
661 // Re-compile the application against the finalized SDK and then load it into an AssetManager with
662 // the finalized SDK.
663 const std::string app_apk_respin = GetTestPath("app-respin.apk");
664 const std::string app_java_respin = GetTestPath("app-respin-java");
665 BuildAppAgainstSDK(app_apk_respin, app_java_respin, android_apk, this, &diag);
666
667 auto app_against_final = android::ApkAssets::Load(app_apk_respin);
668 ASSERT_THAT(app_against_final, NotNull());
669 ASSERT_TRUE(am.SetApkAssets({android_asset, app_against_final}));
670
671 {
672 auto style = am.GetBag(0x7f020000);
673 ASSERT_TRUE(style.has_value());
674
675 auto& entry = (*style)->entries[0];
676 EXPECT_THAT(entry.key, Eq(0x01010002));
677 EXPECT_THAT(entry.value.dataType, Eq(android::Res_value::TYPE_REFERENCE));
678 EXPECT_THAT(entry.value.data, Eq(0x01020000));
679 }
680 }
681
682 TEST_F(LinkTest, MacroSubstitution) {
683 StdErrDiagnostics diag;
684 const std::string values =
685 R"(<resources xmlns:an="http://schemas.android.com/apk/res/android">
686 <macro name="is_enabled">true</macro>
687 <macro name="deep_is_enabled">@macro/is_enabled</macro>
688 <macro name="attr_ref">?is_enabled_attr</macro>
689 <macro name="raw_string">Hello World!</macro>
690 <macro name="android_ref">@an:color/primary_text_dark</macro>
691
692 <attr name="is_enabled_attr" />
693 <public type="attr" name="is_enabled_attr" id="0x7f010000"/>
694
695 <string name="is_enabled_str">@macro/is_enabled</string>
696 <bool name="is_enabled_bool">@macro/deep_is_enabled</bool>
697
698 <array name="my_array">
699 <item>@macro/is_enabled</item>
700 </array>
701
702 <style name="MyStyle">
703 <item name="android:background">@macro/attr_ref</item>
704 <item name="android:fontFamily">@macro/raw_string</item>
705 </style>
706 </resources>)";
707
708 const std::string xml_values =
709 R"(<SomeLayout xmlns:android="http://schemas.android.com/apk/res/android"
710 android:background="@macro/android_ref"
711 android:fontFamily="@macro/raw_string">
712 </SomeLayout>)";
713
714 // Build a library with a public attribute
715 const std::string lib_res = GetTestPath("test-res");
716 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), values, lib_res, &diag));
717 ASSERT_TRUE(CompileFile(GetTestPath("res/layout/layout.xml"), xml_values, lib_res, &diag));
718
719 const std::string lib_apk = GetTestPath("test.apk");
720 // clang-format off
721 auto lib_link_args = LinkCommandBuilder(this)
722 .SetManifestFile(ManifestBuilder(this).SetPackageName("com.test").Build())
723 .AddCompiledResDir(lib_res, &diag)
724 .AddFlag("--no-auto-version")
725 .Build(lib_apk);
726 // clang-format on
727 ASSERT_TRUE(Link(lib_link_args, &diag));
728
729 auto apk = LoadedApk::LoadApkFromPath(lib_apk, &diag);
730 ASSERT_THAT(apk, NotNull());
731
732 // Test that the type flags determines the value type
733 auto actual_bool =
734 test::GetValue<BinaryPrimitive>(apk->GetResourceTable(), "com.test:bool/is_enabled_bool");
735 ASSERT_THAT(actual_bool, NotNull());
736 EXPECT_EQ(android::Res_value::TYPE_INT_BOOLEAN, actual_bool->value.dataType);
737 EXPECT_EQ(0xffffffffu, actual_bool->value.data);
738
739 auto actual_str =
740 test::GetValue<String>(apk->GetResourceTable(), "com.test:string/is_enabled_str");
741 ASSERT_THAT(actual_str, NotNull());
742 EXPECT_EQ(*actual_str->value, "true");
743
744 // Test nested data structures
745 auto actual_array = test::GetValue<Array>(apk->GetResourceTable(), "com.test:array/my_array");
746 ASSERT_THAT(actual_array, NotNull());
747 EXPECT_THAT(actual_array->elements.size(), Eq(1));
748
749 auto array_el_ref = ValueCast<BinaryPrimitive>(actual_array->elements[0].get());
750 ASSERT_THAT(array_el_ref, NotNull());
751 EXPECT_THAT(array_el_ref->value.dataType, Eq(android::Res_value::TYPE_INT_BOOLEAN));
752 EXPECT_THAT(array_el_ref->value.data, Eq(0xffffffffu));
753
754 auto actual_style = test::GetValue<Style>(apk->GetResourceTable(), "com.test:style/MyStyle");
755 ASSERT_THAT(actual_style, NotNull());
756 EXPECT_THAT(actual_style->entries.size(), Eq(2));
757
758 {
759 auto style_el = ValueCast<Reference>(actual_style->entries[0].value.get());
760 ASSERT_THAT(style_el, NotNull());
761 EXPECT_THAT(style_el->reference_type, Eq(Reference::Type::kAttribute));
762 EXPECT_THAT(style_el->id, Eq(0x7f010000));
763 }
764
765 {
766 auto style_el = ValueCast<String>(actual_style->entries[1].value.get());
767 ASSERT_THAT(style_el, NotNull());
768 EXPECT_THAT(*style_el->value, Eq("Hello World!"));
769 }
770
771 // Test substitution in compiled xml files
772 auto xml = apk->LoadXml("res/layout/layout.xml", &diag);
773 ASSERT_THAT(xml, NotNull());
774
775 auto& xml_attrs = xml->root->attributes;
776 ASSERT_THAT(xml_attrs.size(), Eq(2));
777
778 auto attr_value = ValueCast<Reference>(xml_attrs[0].compiled_value.get());
779 ASSERT_THAT(attr_value, NotNull());
780 EXPECT_THAT(attr_value->reference_type, Eq(Reference::Type::kResource));
781 EXPECT_THAT(attr_value->id, Eq(0x01060001));
782
783 EXPECT_THAT(xml_attrs[1].compiled_value.get(), IsNull());
784 EXPECT_THAT(xml_attrs[1].value, Eq("Hello World!"));
785 }
786
787 TEST_F(LinkTest, LocaleConfigVerification) {
788 StdErrDiagnostics diag;
789 const std::string compiled_files_dir = GetTestPath("compiled");
790
791 // Normal case
792 ASSERT_TRUE(CompileFile(GetTestPath("res/xml/locales_config.xml"), R"(
793 <locale-config xmlns:android="http://schemas.android.com/apk/res/android">
794 <locale android:name="en-US"/>
795 <locale android:name="pt"/>
796 <locale android:name="es-419"/>
797 <locale android:name="zh-Hans-SG"/>
798 </locale-config>)",
799 compiled_files_dir, &diag));
800
801 const std::string localeconfig_manifest = GetTestPath("localeconfig_manifest.xml");
802 WriteFile(localeconfig_manifest, android::base::StringPrintf(R"(
803 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
804 package="com.aapt2.app">
805
806 <application
807 android:localeConfig="@xml/locales_config">
808 </application>
809 </manifest>)"));
810
811 const std::string out_apk = GetTestPath("out.apk");
812
813 auto link_args = LinkCommandBuilder(this)
814 .SetManifestFile(localeconfig_manifest)
815 .AddCompiledResDir(compiled_files_dir, &diag)
816 .Build(out_apk);
817 ASSERT_TRUE(Link(link_args, &diag));
818
819 // Empty locale list
820 ASSERT_TRUE(CompileFile(GetTestPath("res/xml/empty_locales_config.xml"), R"(
821 <locale-config xmlns:android="http://schemas.android.com/apk/res/android">
822 </locale-config>)",
823 compiled_files_dir, &diag));
824
825 const std::string empty_localeconfig_manifest = GetTestPath("empty_localeconfig_manifest.xml");
826 WriteFile(empty_localeconfig_manifest, android::base::StringPrintf(R"(
827 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
828 package="com.aapt2.app">
829
830 <application
831 android:localeConfig="@xml/empty_locales_config">
832 </application>
833 </manifest>)"));
834
835 auto link1_args = LinkCommandBuilder(this)
836 .SetManifestFile(empty_localeconfig_manifest)
837 .AddCompiledResDir(compiled_files_dir, &diag)
838 .Build(out_apk);
839 ASSERT_TRUE(Link(link1_args, &diag));
840 }
841
842 TEST_F(LinkTest, LocaleConfigVerificationExternalSymbol) {
843 StdErrDiagnostics diag;
844 const std::string base_files_dir = GetTestPath("base");
845 ASSERT_TRUE(CompileFile(GetTestPath("res/xml/locales_config.xml"), R"(
846 <locale-config xmlns:android="http://schemas.android.com/apk/res/android">
847 <locale android:name="en-US"/>
848 <locale android:name="pt"/>
849 <locale android:name="es-419"/>
850 <locale android:name="zh-Hans-SG"/>
851 </locale-config>)",
852 base_files_dir, &diag));
853 const std::string base_apk = GetTestPath("base.apk");
854 std::vector<std::string> link_args = {
855 "--manifest",
856 GetDefaultManifest("com.aapt2.app"),
857 "-o",
858 base_apk,
859 };
860 ASSERT_TRUE(Link(link_args, base_files_dir, &diag));
861
862 const std::string localeconfig_manifest = GetTestPath("localeconfig_manifest.xml");
863 const std::string out_apk = GetTestPath("out.apk");
864 WriteFile(localeconfig_manifest, android::base::StringPrintf(R"(
865 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
866 package="com.aapt2.app">
867
868 <application
869 android:localeConfig="@xml/locales_config">
870 </application>
871 </manifest>)"));
872 link_args = LinkCommandBuilder(this)
873 .SetManifestFile(localeconfig_manifest)
874 .AddParameter("-I", base_apk)
875 .Build(out_apk);
876 ASSERT_TRUE(Link(link_args, &diag));
877 }
878
879 TEST_F(LinkTest, LocaleConfigWrongTag) {
880 StdErrDiagnostics diag;
881 const std::string compiled_files_dir = GetTestPath("compiled");
882
883 // Invalid element: locale1-config
884 ASSERT_TRUE(CompileFile(GetTestPath("res/xml/wrong_locale_config.xml"), R"(
885 <locale1-config xmlns:android="http://schemas.android.com/apk/res/android">
886 <locale android:name="en-US"/>
887 <locale android:name="pt"/>
888 <locale android:name="es-419"/>
889 <locale android:name="zh-Hans-SG"/>
890 </locale1-config>)",
891 compiled_files_dir, &diag));
892
893 const std::string locale1config_manifest = GetTestPath("locale1config_manifest.xml");
894 WriteFile(locale1config_manifest, android::base::StringPrintf(R"(
895 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
896 package="com.aapt2.app">
897
898 <application
899 android:localeConfig="@xml/wrong_locale_config">
900 </application>
901 </manifest>)"));
902
903 const std::string out_apk = GetTestPath("out.apk");
904 auto link_args = LinkCommandBuilder(this)
905 .SetManifestFile(locale1config_manifest)
906 .AddCompiledResDir(compiled_files_dir, &diag)
907 .Build(out_apk);
908 ASSERT_FALSE(Link(link_args, &diag));
909
910 // Invalid element: locale1
911 ASSERT_TRUE(CompileFile(GetTestPath("res/xml/wrong_locale.xml"), R"(
912 <locale-config xmlns:android="http://schemas.android.com/apk/res/android">
913 <locale1 android:name="en-US"/>
914 <locale android:name="pt"/>
915 <locale android:name="es-419"/>
916 <locale android:name="zh-Hans-SG"/>
917 </locale-config>)",
918 compiled_files_dir, &diag));
919
920 const std::string locale1_manifest = GetTestPath("locale1_manifest.xml");
921 WriteFile(locale1_manifest, android::base::StringPrintf(R"(
922 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
923 package="com.aapt2.app">
924
925 <application
926 android:localeConfig="@xml/wrong_locale">
927 </application>
928 </manifest>)"));
929
930 auto link1_args = LinkCommandBuilder(this)
931 .SetManifestFile(locale1_manifest)
932 .AddCompiledResDir(compiled_files_dir, &diag)
933 .Build(out_apk);
934 ASSERT_FALSE(Link(link1_args, &diag));
935
936 // Invalid attribute: android:name1
937 ASSERT_TRUE(CompileFile(GetTestPath("res/xml/wrong_attribute.xml"), R"(
938 <locale-config xmlns:android="http://schemas.android.com/apk/res/android">
939 <locale android:name1="en-US"/>
940 <locale android:name="pt"/>
941 <locale android:name="es-419"/>
942 <locale android:name="zh-Hans-SG"/>
943 </locale-config>)",
944 compiled_files_dir, &diag));
945
946 const std::string wrong_attribute_manifest = GetTestPath("wrong_attribute_manifest.xml");
947 WriteFile(wrong_attribute_manifest, android::base::StringPrintf(R"(
948 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
949 package="com.aapt2.app">
950
951 <application
952 android:localeConfig="@xml/wrong_attribute">
953 </application>
954 </manifest>)"));
955
956 auto link2_args = LinkCommandBuilder(this)
957 .SetManifestFile(wrong_attribute_manifest)
958 .AddCompiledResDir(compiled_files_dir, &diag)
959 .Build(out_apk);
960 ASSERT_FALSE(Link(link2_args, &diag));
961 }
962
963 TEST_F(LinkTest, LocaleConfigWrongLocaleFormat) {
964 StdErrDiagnostics diag;
965 const std::string compiled_files_dir = GetTestPath("compiled");
966
967 // Invalid locale: en-U
968 ASSERT_TRUE(CompileFile(GetTestPath("res/xml/wrong_locale.xml"), R"(
969 <locale-config xmlns:android="http://schemas.android.com/apk/res/android">
970 <locale android:name="en-U"/>
971 <locale android:name="pt"/>
972 <locale android:name="es-419"/>
973 <locale android:name="zh-Hans-SG"/>
974 </locale-config>)",
975 compiled_files_dir, &diag));
976
977 const std::string wrong_locale_manifest = GetTestPath("wrong_locale_manifest.xml");
978 WriteFile(wrong_locale_manifest, android::base::StringPrintf(R"(
979 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
980 package="com.aapt2.app">
981
982 <application
983 android:localeConfig="@xml/wrong_locale">
984 </application>
985 </manifest>)"));
986
987 const std::string out_apk = GetTestPath("out.apk");
988 auto link_args = LinkCommandBuilder(this)
989 .SetManifestFile(wrong_locale_manifest)
990 .AddCompiledResDir(compiled_files_dir, &diag)
991 .Build(out_apk);
992 ASSERT_FALSE(Link(link_args, &diag));
993 }
994
995 static void BuildSDKWithFeatureFlagAttr(const std::string& apk_path, const std::string& java_path,
996 CommandTestFixture* fixture, android::IDiagnostics* diag) {
997 const std::string android_values =
998 R"(<resources>
999 <staging-public-group type="attr" first-id="0x01fe0063">
1000 <public name="featureFlag" />
1001 </staging-public-group>
1002 <attr name="featureFlag" format="string" />
1003 </resources>)";
1004
1005 SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values};
1006 BuildSDK({source_xml}, apk_path, java_path, fixture, diag);
1007 }
1008
1009 TEST_F(LinkTest, FeatureFlagDisabled_SdkAtMostUDC) {
1010 StdErrDiagnostics diag;
1011 const std::string android_apk = GetTestPath("android.apk");
1012 const std::string android_java = GetTestPath("android-java");
1013 BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag);
1014
1015 const std::string manifest_contents = android::base::StringPrintf(
1016 R"(<uses-sdk android:minSdkVersion="%d" />"
1017 <permission android:name="FOO" android:featureFlag="flag" />)",
1018 SDK_UPSIDE_DOWN_CAKE);
1019 auto app_manifest = ManifestBuilder(this)
1020 .SetPackageName("com.example.app")
1021 .AddContents(manifest_contents)
1022 .Build();
1023
1024 const std::string app_java = GetTestPath("app-java");
1025 auto app_link_args = LinkCommandBuilder(this)
1026 .SetManifestFile(app_manifest)
1027 .AddParameter("-I", android_apk)
1028 .AddParameter("--java", app_java)
1029 .AddParameter("--feature-flags", "flag=false");
1030
1031 const std::string app_apk = GetTestPath("app.apk");
1032 BuildApk({}, app_apk, std::move(app_link_args), this, &diag);
1033
1034 // Permission element should be removed if flag is disabled
1035 auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag);
1036 ASSERT_THAT(apk, NotNull());
1037 auto apk_manifest = apk->GetManifest();
1038 ASSERT_THAT(apk_manifest, NotNull());
1039 auto root = apk_manifest->root.get();
1040 ASSERT_THAT(root, NotNull());
1041 auto maybe_removed = root->FindChild({}, "permission");
1042 ASSERT_THAT(maybe_removed, IsNull());
1043
1044 // Code for the permission should be generated even if the element is removed
1045 const std::string manifest_java = app_java + "/com/example/app/Manifest.java";
1046 std::string manifest_java_contents;
1047 ASSERT_TRUE(android::base::ReadFileToString(manifest_java, &manifest_java_contents));
1048 EXPECT_THAT(manifest_java_contents, HasSubstr(" public static final String FOO=\"FOO\";"));
1049 }
1050
TEST_F(LinkTest,FeatureFlagEnabled_SdkAtMostUDC)1051 TEST_F(LinkTest, FeatureFlagEnabled_SdkAtMostUDC) {
1052 StdErrDiagnostics diag;
1053 const std::string android_apk = GetTestPath("android.apk");
1054 const std::string android_java = GetTestPath("android-java");
1055 BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag);
1056
1057 const std::string manifest_contents = android::base::StringPrintf(
1058 R"(<uses-sdk android:minSdkVersion="%d" />"
1059 <permission android:name="FOO" android:featureFlag="flag" />)",
1060 SDK_UPSIDE_DOWN_CAKE);
1061 auto app_manifest = ManifestBuilder(this)
1062 .SetPackageName("com.example.app")
1063 .AddContents(manifest_contents)
1064 .Build();
1065
1066 auto app_link_args = LinkCommandBuilder(this)
1067 .SetManifestFile(app_manifest)
1068 .AddParameter("-I", android_apk)
1069 .AddParameter("--feature-flags", "flag=true");
1070
1071 const std::string app_apk = GetTestPath("app.apk");
1072 BuildApk({}, app_apk, std::move(app_link_args), this, &diag);
1073
1074 // Permission element should be kept if flag is enabled
1075 auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag);
1076 ASSERT_THAT(apk, NotNull());
1077 auto apk_manifest = apk->GetManifest();
1078 ASSERT_THAT(apk_manifest, NotNull());
1079 auto root = apk_manifest->root.get();
1080 ASSERT_THAT(root, NotNull());
1081 auto maybe_removed = root->FindChild({}, "permission");
1082 ASSERT_THAT(maybe_removed, NotNull());
1083 }
1084
1085 TEST_F(LinkTest, FeatureFlagWithNoValue_SdkAtMostUDC) {
1086 StdErrDiagnostics diag;
1087 const std::string android_apk = GetTestPath("android.apk");
1088 const std::string android_java = GetTestPath("android-java");
1089 BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag);
1090
1091 const std::string manifest_contents = android::base::StringPrintf(
1092 R"(<uses-sdk android:minSdkVersion="%d" />"
1093 <permission android:name="FOO" android:featureFlag="flag" />)",
1094 SDK_UPSIDE_DOWN_CAKE);
1095 auto app_manifest = ManifestBuilder(this)
1096 .SetPackageName("com.example.app")
1097 .AddContents(manifest_contents)
1098 .Build();
1099
1100 auto app_link_args = LinkCommandBuilder(this)
1101 .SetManifestFile(app_manifest)
1102 .AddParameter("-I", android_apk)
1103 .AddParameter("--feature-flags", "flag=");
1104
1105 // Flags must have values if <= UDC
1106 const std::string app_apk = GetTestPath("app.apk");
1107 ASSERT_FALSE(Link(app_link_args.Build(app_apk), &diag));
1108 }
1109
TEST_F(LinkTest,FeatureFlagDisabled_SdkAfterUDC)1110 TEST_F(LinkTest, FeatureFlagDisabled_SdkAfterUDC) {
1111 StdErrDiagnostics diag;
1112 const std::string android_apk = GetTestPath("android.apk");
1113 const std::string android_java = GetTestPath("android-java");
1114 BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag);
1115
1116 const std::string manifest_contents = android::base::StringPrintf(
1117 R"(<uses-sdk android:minSdkVersion="%d" />"
1118 <permission android:name="FOO" android:featureFlag="flag" />)",
1119 SDK_CUR_DEVELOPMENT);
1120 auto app_manifest = ManifestBuilder(this)
1121 .SetPackageName("com.example.app")
1122 .AddContents(manifest_contents)
1123 .Build();
1124
1125 auto app_link_args = LinkCommandBuilder(this)
1126 .SetManifestFile(app_manifest)
1127 .AddParameter("-I", android_apk)
1128 .AddParameter("--feature-flags", "flag=false");
1129
1130 const std::string app_apk = GetTestPath("app.apk");
1131 BuildApk({}, app_apk, std::move(app_link_args), this, &diag);
1132
1133 // Permission element should be kept if > UDC, regardless of flag value
1134 auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag);
1135 ASSERT_THAT(apk, NotNull());
1136 auto apk_manifest = apk->GetManifest();
1137 ASSERT_THAT(apk_manifest, NotNull());
1138 auto root = apk_manifest->root.get();
1139 ASSERT_THAT(root, NotNull());
1140 auto maybe_removed = root->FindChild({}, "permission");
1141 ASSERT_THAT(maybe_removed, NotNull());
1142 }
1143
1144 TEST_F(LinkTest, FeatureFlagEnabled_SdkAfterUDC) {
1145 StdErrDiagnostics diag;
1146 const std::string android_apk = GetTestPath("android.apk");
1147 const std::string android_java = GetTestPath("android-java");
1148 BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag);
1149
1150 const std::string manifest_contents = android::base::StringPrintf(
1151 R"(<uses-sdk android:minSdkVersion="%d" />"
1152 <permission android:name="FOO" android:featureFlag="flag" />)",
1153 SDK_CUR_DEVELOPMENT);
1154 auto app_manifest = ManifestBuilder(this)
1155 .SetPackageName("com.example.app")
1156 .AddContents(manifest_contents)
1157 .Build();
1158
1159 auto app_link_args = LinkCommandBuilder(this)
1160 .SetManifestFile(app_manifest)
1161 .AddParameter("-I", android_apk)
1162 .AddParameter("--feature-flags", "flag=true");
1163
1164 const std::string app_apk = GetTestPath("app.apk");
1165 BuildApk({}, app_apk, std::move(app_link_args), this, &diag);
1166
1167 // Permission element should be kept if > UDC, regardless of flag value
1168 auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag);
1169 ASSERT_THAT(apk, NotNull());
1170 auto apk_manifest = apk->GetManifest();
1171 ASSERT_THAT(apk_manifest, NotNull());
1172 auto root = apk_manifest->root.get();
1173 ASSERT_THAT(root, NotNull());
1174 auto maybe_removed = root->FindChild({}, "permission");
1175 ASSERT_THAT(maybe_removed, NotNull());
1176 }
1177
TEST_F(LinkTest,FeatureFlagWithNoValue_SdkAfterUDC)1178 TEST_F(LinkTest, FeatureFlagWithNoValue_SdkAfterUDC) {
1179 StdErrDiagnostics diag;
1180 const std::string android_apk = GetTestPath("android.apk");
1181 const std::string android_java = GetTestPath("android-java");
1182 BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag);
1183
1184 const std::string manifest_contents = android::base::StringPrintf(
1185 R"(<uses-sdk android:minSdkVersion="%d" />"
1186 <permission android:name="FOO" android:featureFlag="flag" />)",
1187 SDK_CUR_DEVELOPMENT);
1188 auto app_manifest = ManifestBuilder(this)
1189 .SetPackageName("com.example.app")
1190 .AddContents(manifest_contents)
1191 .Build();
1192
1193 auto app_link_args = LinkCommandBuilder(this)
1194 .SetManifestFile(app_manifest)
1195 .AddParameter("-I", android_apk)
1196 .AddParameter("--feature-flags", "flag=");
1197
1198 const std::string app_apk = GetTestPath("app.apk");
1199 BuildApk({}, app_apk, std::move(app_link_args), this, &diag);
1200
1201 // Permission element should be kept if > UDC, regardless of flag value
1202 auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag);
1203 ASSERT_THAT(apk, NotNull());
1204 auto apk_manifest = apk->GetManifest();
1205 ASSERT_THAT(apk_manifest, NotNull());
1206 auto root = apk_manifest->root.get();
1207 ASSERT_THAT(root, NotNull());
1208 auto maybe_removed = root->FindChild({}, "permission");
1209 ASSERT_THAT(maybe_removed, NotNull());
1210 }
1211
1212 } // namespace aapt
1213