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 "LoadedApk.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(util::GetString(tree.getStrings(), static_cast<size_t>(raw_index)), Eq("007"));
90 }
91
TEST_F(LinkTest,NoCompressAssets)92 TEST_F(LinkTest, NoCompressAssets) {
93 StdErrDiagnostics diag;
94 std::string content(500, 'a');
95 WriteFile(GetTestPath("assets/testtxt"), content);
96 WriteFile(GetTestPath("assets/testtxt2"), content);
97 WriteFile(GetTestPath("assets/test.txt"), content);
98 WriteFile(GetTestPath("assets/test.hello.txt"), content);
99 WriteFile(GetTestPath("assets/test.hello.xml"), content);
100
101 const std::string out_apk = GetTestPath("out.apk");
102 std::vector<std::string> link_args = {
103 "--manifest", GetDefaultManifest(),
104 "-o", out_apk,
105 "-0", ".txt",
106 "-0", "txt2",
107 "-0", ".hello.txt",
108 "-0", "hello.xml",
109 "-A", GetTestPath("assets")
110 };
111
112 ASSERT_TRUE(Link(link_args, &diag));
113
114 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
115 ASSERT_THAT(apk, Ne(nullptr));
116 io::IFileCollection* zip = apk->GetFileCollection();
117 ASSERT_THAT(zip, Ne(nullptr));
118
119 auto file = zip->FindFile("assets/testtxt");
120 ASSERT_THAT(file, Ne(nullptr));
121 EXPECT_TRUE(file->WasCompressed());
122
123 file = zip->FindFile("assets/testtxt2");
124 ASSERT_THAT(file, Ne(nullptr));
125 EXPECT_FALSE(file->WasCompressed());
126
127 file = zip->FindFile("assets/test.txt");
128 ASSERT_THAT(file, Ne(nullptr));
129 EXPECT_FALSE(file->WasCompressed());
130
131 file = zip->FindFile("assets/test.hello.txt");
132 ASSERT_THAT(file, Ne(nullptr));
133 EXPECT_FALSE(file->WasCompressed());
134
135 file = zip->FindFile("assets/test.hello.xml");
136 ASSERT_THAT(file, Ne(nullptr));
137 EXPECT_FALSE(file->WasCompressed());
138 }
139
TEST_F(LinkTest,NoCompressResources)140 TEST_F(LinkTest, NoCompressResources) {
141 StdErrDiagnostics diag;
142 std::string content(500, 'a');
143 const std::string compiled_files_dir = GetTestPath("compiled");
144 ASSERT_TRUE(CompileFile(GetTestPath("res/raw/testtxt"), content, compiled_files_dir, &diag));
145 ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test.txt"), content, compiled_files_dir, &diag));
146 ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test1.hello.txt"), content, compiled_files_dir,
147 &diag));
148 ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test2.goodbye.xml"), content, compiled_files_dir,
149 &diag));
150
151 const std::string out_apk = GetTestPath("out.apk");
152 std::vector<std::string> link_args = {
153 "--manifest", GetDefaultManifest(),
154 "-o", out_apk,
155 "-0", ".txt",
156 "-0", ".hello.txt",
157 "-0", "goodbye.xml",
158 };
159
160 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
161
162 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
163 ASSERT_THAT(apk, Ne(nullptr));
164 io::IFileCollection* zip = apk->GetFileCollection();
165 ASSERT_THAT(zip, Ne(nullptr));
166
167 auto file = zip->FindFile("res/raw/testtxt");
168 ASSERT_THAT(file, Ne(nullptr));
169 EXPECT_TRUE(file->WasCompressed());
170
171 file = zip->FindFile("res/raw/test.txt");
172 ASSERT_THAT(file, Ne(nullptr));
173 EXPECT_FALSE(file->WasCompressed());
174
175 file = zip->FindFile("res/raw/test1.hello.hello.txt");
176 ASSERT_THAT(file, Ne(nullptr));
177 EXPECT_FALSE(file->WasCompressed());
178
179 file = zip->FindFile("res/raw/test2.goodbye.goodbye.xml");
180 ASSERT_THAT(file, Ne(nullptr));
181 EXPECT_FALSE(file->WasCompressed());
182 }
183
TEST_F(LinkTest,OverlayStyles)184 TEST_F(LinkTest, OverlayStyles) {
185 StdErrDiagnostics diag;
186 const std::string compiled_files_dir = GetTestPath("compiled");
187 const std::string override_files_dir = GetTestPath("compiled-override");
188 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
189 R"(<resources>
190 <style name="MyStyle">
191 <item name="android:textColor">#123</item>
192 </style>
193 </resources>)",
194 compiled_files_dir, &diag));
195 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values-override.xml"),
196 R"(<resources>
197 <style name="MyStyle">
198 <item name="android:background">#456</item>
199 </style>
200 </resources>)",
201 override_files_dir, &diag));
202
203
204 const std::string out_apk = GetTestPath("out.apk");
205 std::vector<std::string> link_args = {
206 "--manifest", GetDefaultManifest(kDefaultPackageName),
207 "-o", out_apk,
208 };
209 const auto override_files = file::FindFiles(override_files_dir, &diag);
210 for (const auto &override_file : override_files.value()) {
211 link_args.push_back("-R");
212 link_args.push_back(file::BuildPath({override_files_dir, override_file}));
213 }
214 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
215
216 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
217 ASSERT_THAT(apk, Ne(nullptr));
218
219 const Style* actual_style = test::GetValue<Style>(
220 apk->GetResourceTable(), std::string(kDefaultPackageName) + ":style/MyStyle");
221 ASSERT_NE(actual_style, nullptr);
222 ASSERT_EQ(actual_style->entries.size(), 2);
223 EXPECT_EQ(actual_style->entries[0].key.id, 0x01010098); // android:textColor
224 EXPECT_EQ(actual_style->entries[1].key.id, 0x010100d4); // android:background
225 }
226
TEST_F(LinkTest,OverrideStylesInsteadOfOverlaying)227 TEST_F(LinkTest, OverrideStylesInsteadOfOverlaying) {
228 StdErrDiagnostics diag;
229 const std::string compiled_files_dir = GetTestPath("compiled");
230 const std::string override_files_dir = GetTestPath("compiled-override");
231 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
232 R"(<resources>
233 <style name="MyStyle">
234 <item name="android:textColor">#123</item>
235 </style>
236 </resources>)",
237 compiled_files_dir, &diag));
238 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values-override.xml"),
239 R"(<resources>
240 <style name="MyStyle">
241 <item name="android:background">#456</item>
242 </style>
243 </resources>)",
244 override_files_dir, &diag));
245
246
247 const std::string out_apk = GetTestPath("out.apk");
248 std::vector<std::string> link_args = {
249 "--manifest", GetDefaultManifest(kDefaultPackageName),
250 "--override-styles-instead-of-overlaying",
251 "-o", out_apk,
252 };
253 const auto override_files = file::FindFiles(override_files_dir, &diag);
254 for (const auto &override_file : override_files.value()) {
255 link_args.push_back("-R");
256 link_args.push_back(file::BuildPath({override_files_dir, override_file}));
257 }
258 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
259
260 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
261 ASSERT_THAT(apk, Ne(nullptr));
262
263 const Style* actual_style = test::GetValue<Style>(
264 apk->GetResourceTable(), std::string(kDefaultPackageName) + ":style/MyStyle");
265 ASSERT_NE(actual_style, nullptr);
266 ASSERT_EQ(actual_style->entries.size(), 1);
267 EXPECT_EQ(actual_style->entries[0].key.id, 0x010100d4); // android:background
268 }
269
TEST_F(LinkTest,AppInfoWithUsesSplit)270 TEST_F(LinkTest, AppInfoWithUsesSplit) {
271 StdErrDiagnostics diag;
272 const std::string base_files_dir = GetTestPath("base");
273 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
274 R"(<resources>
275 <string name="bar">bar</string>
276 </resources>)",
277 base_files_dir, &diag));
278 const std::string base_apk = GetTestPath("base.apk");
279 std::vector<std::string> link_args = {
280 "--manifest", GetDefaultManifest("com.aapt2.app"),
281 "-o", base_apk,
282 };
283 ASSERT_TRUE(Link(link_args, base_files_dir, &diag));
284
285 const std::string feature_manifest = GetTestPath("feature_manifest.xml");
286 WriteFile(feature_manifest, android::base::StringPrintf(R"(
287 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
288 package="com.aapt2.app" split="feature1">
289 </manifest>)"));
290 const std::string feature_files_dir = GetTestPath("feature");
291 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
292 R"(<resources>
293 <string name="foo">foo</string>
294 </resources>)",
295 feature_files_dir, &diag));
296 const std::string feature_apk = GetTestPath("feature.apk");
297 const std::string feature_package_id = "0x80";
298 link_args = {
299 "--manifest", feature_manifest,
300 "-I", base_apk,
301 "--package-id", feature_package_id,
302 "-o", feature_apk,
303 };
304 ASSERT_TRUE(Link(link_args, feature_files_dir, &diag));
305
306 const std::string feature2_manifest = GetTestPath("feature2_manifest.xml");
307 WriteFile(feature2_manifest, android::base::StringPrintf(R"(
308 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
309 package="com.aapt2.app" split="feature2">
310 <uses-split android:name="feature1"/>
311 </manifest>)"));
312 const std::string feature2_files_dir = GetTestPath("feature2");
313 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
314 R"(<resources>
315 <string-array name="string_array">
316 <item>@string/bar</item>
317 <item>@string/foo</item>
318 </string-array>
319 </resources>)",
320 feature2_files_dir, &diag));
321 const std::string feature2_apk = GetTestPath("feature2.apk");
322 const std::string feature2_package_id = "0x81";
323 link_args = {
324 "--manifest", feature2_manifest,
325 "-I", base_apk,
326 "-I", feature_apk,
327 "--package-id", feature2_package_id,
328 "-o", feature2_apk,
329 };
330 ASSERT_TRUE(Link(link_args, feature2_files_dir, &diag));
331 }
332
333 TEST_F(LinkTest, SharedLibraryAttributeRJava) {
334 StdErrDiagnostics diag;
335 const std::string lib_values =
336 R"(<resources>
337 <attr name="foo"/>
338 <public type="attr" name="foo" id="0x00010001"/>
339 <declare-styleable name="LibraryStyleable">
340 <attr name="foo" />
341 </declare-styleable>
342 </resources>)";
343
344 const std::string client_values =
345 R"(<resources>
346 <attr name="bar" />
347 <declare-styleable name="ClientStyleable">
348 <attr name="com.example.lib:foo" />
349 <attr name="bar" />
350 </declare-styleable>
351 </resources>)";
352
353 // Build a library with a public attribute
354 const std::string lib_res = GetTestPath("library-res");
355 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), lib_values, lib_res, &diag));
356
357 const std::string lib_apk = GetTestPath("library.apk");
358 const std::string lib_java = GetTestPath("library_java");
359 // clang-format off
360 auto lib_manifest = ManifestBuilder(this)
361 .SetPackageName("com.example.lib")
362 .Build();
363
364 auto lib_link_args = LinkCommandBuilder(this)
365 .SetManifestFile(lib_manifest)
366 .AddFlag("--shared-lib")
367 .AddParameter("--java", lib_java)
368 .AddCompiledResDir(lib_res, &diag)
369 .Build(lib_apk);
370 // clang-format on
371 ASSERT_TRUE(Link(lib_link_args, &diag));
372
373 const std::string lib_r_java = lib_java + "/com/example/lib/R.java";
374 std::string lib_r_contents;
375 ASSERT_TRUE(android::base::ReadFileToString(lib_r_java, &lib_r_contents));
376 EXPECT_THAT(lib_r_contents, HasSubstr(" public static int foo=0x00010001;"));
377 EXPECT_THAT(lib_r_contents, HasSubstr(" com.example.lib.R.attr.foo"));
378
379 // Build a client that uses the library attribute in a declare-styleable
380 const std::string client_res = GetTestPath("client-res");
381 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), client_values, client_res, &diag));
382
383 const std::string client_apk = GetTestPath("client.apk");
384 const std::string client_java = GetTestPath("client_java");
385 // clang-format off
386 auto client_manifest = ManifestBuilder(this)
387 .SetPackageName("com.example.client")
388 .Build();
389
390 auto client_link_args = LinkCommandBuilder(this)
391 .SetManifestFile(client_manifest)
392 .AddParameter("--java", client_java)
393 .AddParameter("-I", lib_apk)
394 .AddCompiledResDir(client_res, &diag)
395 .Build(client_apk);
396 // clang-format on
397 ASSERT_TRUE(Link(client_link_args, &diag));
398
399 const std::string client_r_java = client_java + "/com/example/client/R.java";
400 std::string client_r_contents;
401 ASSERT_TRUE(android::base::ReadFileToString(client_r_java, &client_r_contents));
402 EXPECT_THAT(client_r_contents, HasSubstr(" com.example.lib.R.attr.foo, 0x7f010000"));
403 }
404
405 struct SourceXML {
406 std::string res_file_path;
407 std::string file_contents;
408 };
409
410 static void BuildApk(const std::vector<SourceXML>& source_files, const std::string& apk_path,
411 LinkCommandBuilder&& link_args, CommandTestFixture* fixture,
412 IDiagnostics* diag) {
413 TemporaryDir res_dir;
414 TemporaryDir compiled_res_dir;
415 for (auto& source_file : source_files) {
416 ASSERT_TRUE(fixture->CompileFile(res_dir.path + source_file.res_file_path,
417 source_file.file_contents, compiled_res_dir.path, diag));
418 }
419 ASSERT_TRUE(fixture->Link(
420 link_args.AddCompiledResDir(compiled_res_dir.path, diag).Build(apk_path), diag));
421 }
422
423 static void BuildSDK(const std::vector<SourceXML>& source_files, const std::string& apk_path,
424 const std::string& java_root_path, CommandTestFixture* fixture,
425 IDiagnostics* diag) {
426 auto android_manifest = ManifestBuilder(fixture).SetPackageName("android").Build();
427
428 auto android_link_args = LinkCommandBuilder(fixture)
429 .SetManifestFile(android_manifest)
430 .AddParameter("--private-symbols", "com.android.internal")
431 .AddParameter("--java", java_root_path);
432
433 BuildApk(source_files, apk_path, std::move(android_link_args), fixture, diag);
434 }
435
436 static void BuildNonFinalizedSDK(const std::string& apk_path, const std::string& java_path,
437 CommandTestFixture* fixture, IDiagnostics* diag) {
438 const std::string android_values =
439 R"(<resources>
440 <public type="attr" name="finalized_res" id="0x01010001"/>
441
442 <!-- S staged attributes (support staged resources in the same type id) -->
443 <staging-public-group type="attr" first-id="0x01010050">
444 <public name="staged_s_res" />
445 </staging-public-group>
446
447 <staging-public-group type="string" first-id="0x01fd0080">
448 <public name="staged_s_string" />
449 </staging-public-group>
450
451 <!-- SV2 staged attributes (support staged resources in a separate type id) -->
452 <staging-public-group type="attr" first-id="0x01ff0049">
453 <public name="staged_s2_res" />
454 </staging-public-group>
455
456 <!-- T staged attributes (support staged resources in multiple separate type ids) -->
457 <staging-public-group type="attr" first-id="0x01fe0063">
458 <public name="staged_t_res" />
459 </staging-public-group>
460
461 <attr name="finalized_res" />
462 <attr name="staged_s_res" />
463 <attr name="staged_s2_res" />
464 <attr name="staged_t_res" />
465 <string name="staged_s_string">Hello</string>
466 </resources>)";
467
468 SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values};
469 BuildSDK({source_xml}, apk_path, java_path, fixture, diag);
470 }
471
472 static void BuildFinalizedSDK(const std::string& apk_path, const std::string& java_path,
473 CommandTestFixture* fixture, IDiagnostics* diag) {
474 const std::string android_values =
475 R"(<resources>
476 <public type="attr" name="finalized_res" id="0x01010001"/>
477 <public type="attr" name="staged_s_res" id="0x01010002"/>
478 <public type="attr" name="staged_s2_res" id="0x01010003"/>
479 <public type="string" name="staged_s_string" id="0x01020000"/>
480
481 <!-- S staged attributes (support staged resources in the same type id) -->
482 <staging-public-group-final type="attr" first-id="0x01010050">
483 <public name="staged_s_res" />
484 </staging-public-group-final>
485
486 <staging-public-group-final type="string" first-id="0x01fd0080">
487 <public name="staged_s_string" />
488 </staging-public-group-final>
489
490 <!-- SV2 staged attributes (support staged resources in a separate type id) -->
491 <staging-public-group-final type="attr" first-id="0x01ff0049">
492 <public name="staged_s2_res" />
493 </staging-public-group-final>
494
495 <!-- T staged attributes (support staged resources in multiple separate type ids) -->
496 <staging-public-group type="attr" first-id="0x01fe0063">
497 <public name="staged_t_res" />
498 </staging-public-group>
499
500 <attr name="finalized_res" />
501 <attr name="staged_s_res" />
502 <attr name="staged_s2_res" />
503 <attr name="staged_t_res" />
504 <string name="staged_s_string">Hello</string>
505 </resources>)";
506
507 SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values};
508 BuildSDK({source_xml}, apk_path, java_path, fixture, diag);
509 }
510
511 static void BuildAppAgainstSDK(const std::string& apk_path, const std::string& java_path,
512 const std::string& sdk_path, CommandTestFixture* fixture,
513 IDiagnostics* diag) {
514 const std::string app_values =
515 R"(<resources xmlns:android="http://schemas.android.com/apk/res/android">
516 <attr name="bar" />
517 <style name="MyStyle">
518 <item name="android:staged_s_res">@android:string/staged_s_string</item>
519 </style>
520 <declare-styleable name="ClientStyleable">
521 <attr name="android:finalized_res" />
522 <attr name="android:staged_s_res" />
523 <attr name="bar" />
524 </declare-styleable>
525 <public name="MyStyle" type="style" id="0x7f020000" />
526 </resources>)";
527
528 SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = app_values};
529
530 auto app_manifest = ManifestBuilder(fixture).SetPackageName("com.example.app").Build();
531
532 auto app_link_args = LinkCommandBuilder(fixture)
533 .SetManifestFile(app_manifest)
534 .AddParameter("--java", java_path)
535 .AddParameter("-I", sdk_path);
536
537 BuildApk({source_xml}, apk_path, std::move(app_link_args), fixture, diag);
538 }
539
540 TEST_F(LinkTest, StagedAndroidApi) {
541 StdErrDiagnostics diag;
542 const std::string android_apk = GetTestPath("android.apk");
543 const std::string android_java = GetTestPath("android-java");
544 BuildNonFinalizedSDK(android_apk, android_java, this, &diag);
545
546 const std::string android_r_java = android_java + "/android/R.java";
547 std::string android_r_contents;
548 ASSERT_TRUE(android::base::ReadFileToString(android_r_java, &android_r_contents));
549 EXPECT_THAT(android_r_contents, HasSubstr("public static final int finalized_res=0x01010001;"));
550 EXPECT_THAT(
551 android_r_contents,
552 HasSubstr("public static final int staged_s_res; static { staged_s_res=0x01010050; }"));
553 EXPECT_THAT(
554 android_r_contents,
555 HasSubstr("public static final int staged_s_string; static { staged_s_string=0x01fd0080; }"));
556 EXPECT_THAT(
557 android_r_contents,
558 HasSubstr("public static final int staged_s2_res; static { staged_s2_res=0x01ff0049; }"));
559 EXPECT_THAT(
560 android_r_contents,
561 HasSubstr("public static final int staged_t_res; static { staged_t_res=0x01fe0063; }"));
562
563 const std::string app_apk = GetTestPath("app.apk");
564 const std::string app_java = GetTestPath("app-java");
565 BuildAppAgainstSDK(app_apk, app_java, android_apk, this, &diag);
566
567 const std::string client_r_java = app_java + "/com/example/app/R.java";
568 std::string client_r_contents;
569 ASSERT_TRUE(android::base::ReadFileToString(client_r_java, &client_r_contents));
570 EXPECT_THAT(client_r_contents, HasSubstr(" 0x01010001, android.R.attr.staged_s_res, 0x7f010000"));
571
572 // Test that the resource ids of staged and non-staged resource can be retrieved
573 android::AssetManager2 am;
574 auto android_asset = android::ApkAssets::Load(android_apk);
575 ASSERT_THAT(android_asset, NotNull());
576 ASSERT_TRUE(am.SetApkAssets({android_asset.get()}));
577
578 auto result = am.GetResourceId("android:attr/finalized_res");
579 ASSERT_TRUE(result.has_value());
580 EXPECT_THAT(*result, Eq(0x01010001));
581
582 result = am.GetResourceId("android:attr/staged_s_res");
583 ASSERT_TRUE(result.has_value());
584 EXPECT_THAT(*result, Eq(0x01010050));
585
586 result = am.GetResourceId("android:string/staged_s_string");
587 ASSERT_TRUE(result.has_value());
588 EXPECT_THAT(*result, Eq(0x01fd0080));
589
590 result = am.GetResourceId("android:attr/staged_s2_res");
591 ASSERT_TRUE(result.has_value());
592 EXPECT_THAT(*result, Eq(0x01ff0049));
593
594 result = am.GetResourceId("android:attr/staged_t_res");
595 ASSERT_TRUE(result.has_value());
596 EXPECT_THAT(*result, Eq(0x01fe0063));
597 }
598
599 TEST_F(LinkTest, FinalizedAndroidApi) {
600 StdErrDiagnostics diag;
601 const std::string android_apk = GetTestPath("android.apk");
602 const std::string android_java = GetTestPath("android-java");
603 BuildFinalizedSDK(android_apk, android_java, this, &diag);
604
605 const std::string android_r_java = android_java + "/android/R.java";
606 std::string android_r_contents;
607 ASSERT_TRUE(android::base::ReadFileToString(android_r_java, &android_r_contents));
608 EXPECT_THAT(android_r_contents, HasSubstr("public static final int finalized_res=0x01010001;"));
609 EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s_res=0x01010002;"));
610 EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s_string=0x01020000;"));
611 EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s2_res=0x01010003;"));
612 EXPECT_THAT(
613 android_r_contents,
614 HasSubstr("public static final int staged_t_res; static { staged_t_res=0x01fe0063; }"));
615 ;
616
617 // Build an application against the non-finalized SDK and then load it into an AssetManager with
618 // the finalized SDK.
619 const std::string non_finalized_android_apk = GetTestPath("non-finalized-android.apk");
620 const std::string non_finalized_android_java = GetTestPath("non-finalized-android-java");
621 BuildNonFinalizedSDK(non_finalized_android_apk, non_finalized_android_java, this, &diag);
622
623 const std::string app_apk = GetTestPath("app.apk");
624 const std::string app_java = GetTestPath("app-java");
625 BuildAppAgainstSDK(app_apk, app_java, non_finalized_android_apk, this, &diag);
626
627 android::AssetManager2 am;
628 auto android_asset = android::ApkAssets::Load(android_apk);
629 auto app_against_non_final = android::ApkAssets::Load(app_apk);
630 ASSERT_THAT(android_asset, NotNull());
631 ASSERT_THAT(app_against_non_final, NotNull());
632 ASSERT_TRUE(am.SetApkAssets({android_asset.get(), app_against_non_final.get()}));
633
634 auto result = am.GetResourceId("android:attr/finalized_res");
635 ASSERT_TRUE(result.has_value());
636 EXPECT_THAT(*result, Eq(0x01010001));
637
638 result = am.GetResourceId("android:attr/staged_s_res");
639 ASSERT_TRUE(result.has_value());
640 EXPECT_THAT(*result, Eq(0x01010002));
641
642 result = am.GetResourceId("android:string/staged_s_string");
643 ASSERT_TRUE(result.has_value());
644 EXPECT_THAT(*result, Eq(0x01020000));
645
646 result = am.GetResourceId("android:attr/staged_s2_res");
647 ASSERT_TRUE(result.has_value());
648 EXPECT_THAT(*result, Eq(0x01010003));
649
650 {
651 auto style = am.GetBag(0x7f020000);
652 ASSERT_TRUE(style.has_value());
653
654 auto& entry = (*style)->entries[0];
655 EXPECT_THAT(entry.key, Eq(0x01010002));
656 EXPECT_THAT(entry.value.dataType, Eq(android::Res_value::TYPE_REFERENCE));
657 EXPECT_THAT(entry.value.data, Eq(0x01020000));
658 }
659
660 // Re-compile the application against the finalized SDK and then load it into an AssetManager with
661 // the finalized SDK.
662 const std::string app_apk_respin = GetTestPath("app-respin.apk");
663 const std::string app_java_respin = GetTestPath("app-respin-java");
664 BuildAppAgainstSDK(app_apk_respin, app_java_respin, android_apk, this, &diag);
665
666 auto app_against_final = android::ApkAssets::Load(app_apk_respin);
667 ASSERT_THAT(app_against_final, NotNull());
668 ASSERT_TRUE(am.SetApkAssets({android_asset.get(), app_against_final.get()}));
669
670 {
671 auto style = am.GetBag(0x7f020000);
672 ASSERT_TRUE(style.has_value());
673
674 auto& entry = (*style)->entries[0];
675 EXPECT_THAT(entry.key, Eq(0x01010002));
676 EXPECT_THAT(entry.value.dataType, Eq(android::Res_value::TYPE_REFERENCE));
677 EXPECT_THAT(entry.value.data, Eq(0x01020000));
678 }
679 }
680
681 TEST_F(LinkTest, MacroSubstitution) {
682 StdErrDiagnostics diag;
683 const std::string values =
684 R"(<resources xmlns:an="http://schemas.android.com/apk/res/android">
685 <macro name="is_enabled">true</macro>
686 <macro name="deep_is_enabled">@macro/is_enabled</macro>
687 <macro name="attr_ref">?is_enabled_attr</macro>
688 <macro name="raw_string">Hello World!</macro>
689 <macro name="android_ref">@an:color/primary_text_dark</macro>
690
691 <attr name="is_enabled_attr" />
692 <public type="attr" name="is_enabled_attr" id="0x7f010000"/>
693
694 <string name="is_enabled_str">@macro/is_enabled</string>
695 <bool name="is_enabled_bool">@macro/deep_is_enabled</bool>
696
697 <array name="my_array">
698 <item>@macro/is_enabled</item>
699 </array>
700
701 <style name="MyStyle">
702 <item name="android:background">@macro/attr_ref</item>
703 <item name="android:fontFamily">@macro/raw_string</item>
704 </style>
705 </resources>)";
706
707 const std::string xml_values =
708 R"(<SomeLayout xmlns:android="http://schemas.android.com/apk/res/android"
709 android:background="@macro/android_ref"
710 android:fontFamily="@macro/raw_string">
711 </SomeLayout>)";
712
713 // Build a library with a public attribute
714 const std::string lib_res = GetTestPath("test-res");
715 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), values, lib_res, &diag));
716 ASSERT_TRUE(CompileFile(GetTestPath("res/layout/layout.xml"), xml_values, lib_res, &diag));
717
718 const std::string lib_apk = GetTestPath("test.apk");
719 // clang-format off
720 auto lib_link_args = LinkCommandBuilder(this)
721 .SetManifestFile(ManifestBuilder(this).SetPackageName("com.test").Build())
722 .AddCompiledResDir(lib_res, &diag)
723 .AddFlag("--no-auto-version")
724 .Build(lib_apk);
725 // clang-format on
726 ASSERT_TRUE(Link(lib_link_args, &diag));
727
728 auto apk = LoadedApk::LoadApkFromPath(lib_apk, &diag);
729 ASSERT_THAT(apk, NotNull());
730
731 // Test that the type flags determines the value type
732 auto actual_bool =
733 test::GetValue<BinaryPrimitive>(apk->GetResourceTable(), "com.test:bool/is_enabled_bool");
734 ASSERT_THAT(actual_bool, NotNull());
735 EXPECT_EQ(android::Res_value::TYPE_INT_BOOLEAN, actual_bool->value.dataType);
736 EXPECT_EQ(0xffffffffu, actual_bool->value.data);
737
738 auto actual_str =
739 test::GetValue<String>(apk->GetResourceTable(), "com.test:string/is_enabled_str");
740 ASSERT_THAT(actual_str, NotNull());
741 EXPECT_EQ(*actual_str->value, "true");
742
743 // Test nested data structures
744 auto actual_array = test::GetValue<Array>(apk->GetResourceTable(), "com.test:array/my_array");
745 ASSERT_THAT(actual_array, NotNull());
746 EXPECT_THAT(actual_array->elements.size(), Eq(1));
747
748 auto array_el_ref = ValueCast<BinaryPrimitive>(actual_array->elements[0].get());
749 ASSERT_THAT(array_el_ref, NotNull());
750 EXPECT_THAT(array_el_ref->value.dataType, Eq(android::Res_value::TYPE_INT_BOOLEAN));
751 EXPECT_THAT(array_el_ref->value.data, Eq(0xffffffffu));
752
753 auto actual_style = test::GetValue<Style>(apk->GetResourceTable(), "com.test:style/MyStyle");
754 ASSERT_THAT(actual_style, NotNull());
755 EXPECT_THAT(actual_style->entries.size(), Eq(2));
756
757 {
758 auto style_el = ValueCast<Reference>(actual_style->entries[0].value.get());
759 ASSERT_THAT(style_el, NotNull());
760 EXPECT_THAT(style_el->reference_type, Eq(Reference::Type::kAttribute));
761 EXPECT_THAT(style_el->id, Eq(0x7f010000));
762 }
763
764 {
765 auto style_el = ValueCast<String>(actual_style->entries[1].value.get());
766 ASSERT_THAT(style_el, NotNull());
767 EXPECT_THAT(*style_el->value, Eq("Hello World!"));
768 }
769
770 // Test substitution in compiled xml files
771 auto xml = apk->LoadXml("res/layout/layout.xml", &diag);
772 ASSERT_THAT(xml, NotNull());
773
774 auto& xml_attrs = xml->root->attributes;
775 ASSERT_THAT(xml_attrs.size(), Eq(2));
776
777 auto attr_value = ValueCast<Reference>(xml_attrs[0].compiled_value.get());
778 ASSERT_THAT(attr_value, NotNull());
779 EXPECT_THAT(attr_value->reference_type, Eq(Reference::Type::kResource));
780 EXPECT_THAT(attr_value->id, Eq(0x01060001));
781
782 EXPECT_THAT(xml_attrs[1].compiled_value.get(), IsNull());
783 EXPECT_THAT(xml_attrs[1].value, Eq("Hello World!"));
784 }
785
786 } // namespace aapt
787