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