blob: 7b1236ab4a5ec9d0defe7bfffb003440716ed25c [file] [log] [blame]
Ryan Mitchell479fa392019-01-02 17:15:39 -08001/*
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
Ryan Mitchell5855de72021-02-24 14:39:13 -080019#include <android-base/file.h>
20
21#include "AppInfo.h"
Ryan Mitchell479fa392019-01-02 17:15:39 -080022#include "LoadedApk.h"
23#include "test/Test.h"
24
Josh Houd3bd4522022-02-14 10:24:42 +080025using android::ConfigDescription;
Ryan Mitchell479fa392019-01-02 17:15:39 -080026using testing::Eq;
Ryan Mitchell5855de72021-02-24 14:39:13 -080027using testing::HasSubstr;
Ryan Mitchell326e35ff2021-04-12 07:50:42 -070028using testing::IsNull;
Ryan Mitchell479fa392019-01-02 17:15:39 -080029using testing::Ne;
Ryan Mitchell2e9bec12021-03-22 09:31:00 -070030using testing::NotNull;
Ryan Mitchell479fa392019-01-02 17:15:39 -080031
32namespace aapt {
33
34using LinkTest = CommandTestFixture;
35
36TEST_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);
Ryan Mitchell1d008d12021-03-19 14:54:17 -070053 ASSERT_THAT(apk, Ne(nullptr));
54
Winsonb7be7932019-01-23 11:10:52 -080055 std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
56 ASSERT_THAT(data, Ne(nullptr));
Winsonb7be7932019-01-23 11:10:52 -080057 AssertLoadXml(apk.get(), data.get(), &tree);
Ryan Mitchell479fa392019-01-02 17:15:39 -080058
59 // Check that the raw string index has not been assigned
60 EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1));
61}
62
63TEST_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);
Ryan Mitchell1d008d12021-03-19 14:54:17 -070081 ASSERT_THAT(apk, Ne(nullptr));
82
Winsonb7be7932019-01-23 11:10:52 -080083 std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
84 ASSERT_THAT(data, Ne(nullptr));
Winsonb7be7932019-01-23 11:10:52 -080085 AssertLoadXml(apk.get(), data.get(), &tree);
Ryan Mitchell479fa392019-01-02 17:15:39 -080086
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(util::GetString(tree.getStrings(), static_cast<size_t>(raw_index)), Eq("007"));
91}
92
Ryan Mitchell81dfca02019-06-07 10:20:27 -070093TEST_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
141TEST_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
Donald Chai121c6e82019-06-12 12:51:57 -0700185TEST_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);
Ryan Mitchell1d008d12021-03-19 14:54:17 -0700218 ASSERT_THAT(apk, Ne(nullptr));
219
Donald Chai121c6e82019-06-12 12:51:57 -0700220 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
228TEST_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);
Ryan Mitchell1d008d12021-03-19 14:54:17 -0700262 ASSERT_THAT(apk, Ne(nullptr));
263
Donald Chai121c6e82019-06-12 12:51:57 -0700264 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
Udam Sainib228df32019-06-18 16:50:34 -0700271TEST_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
Ryan Mitchell5855de72021-02-24 14:39:13 -0800334TEST_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
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700406struct SourceXML {
407 std::string res_file_path;
408 std::string file_contents;
409};
410
411static void BuildApk(const std::vector<SourceXML>& source_files, const std::string& apk_path,
412 LinkCommandBuilder&& link_args, CommandTestFixture* fixture,
413 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
424static void BuildSDK(const std::vector<SourceXML>& source_files, const std::string& apk_path,
425 const std::string& java_root_path, CommandTestFixture* fixture,
426 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
437static void BuildNonFinalizedSDK(const std::string& apk_path, const std::string& java_path,
438 CommandTestFixture* fixture, IDiagnostics* diag) {
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700439 const std::string android_values =
440 R"(<resources>
441 <public type="attr" name="finalized_res" id="0x01010001"/>
442
443 <!-- S staged attributes (support staged resources in the same type id) -->
444 <staging-public-group type="attr" first-id="0x01010050">
445 <public name="staged_s_res" />
446 </staging-public-group>
447
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700448 <staging-public-group type="string" first-id="0x01fd0080">
449 <public name="staged_s_string" />
450 </staging-public-group>
451
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700452 <!-- 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
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700462 <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
473static void BuildFinalizedSDK(const std::string& apk_path, const std::string& java_path,
474 CommandTestFixture* fixture, 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 (support staged resources in the same type id) -->
483 <staging-public-group-final type="attr" first-id="0x01010050">
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" />
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700499 </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" />
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700505 <string name="staged_s_string">Hello</string>
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700506 </resources>)";
507
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700508 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
512static void BuildAppAgainstSDK(const std::string& apk_path, const std::string& java_path,
513 const std::string& sdk_path, CommandTestFixture* fixture,
514 IDiagnostics* diag) {
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700515 const std::string app_values =
516 R"(<resources xmlns:android="http://schemas.android.com/apk/res/android">
517 <attr name="bar" />
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700518 <style name="MyStyle">
519 <item name="android:staged_s_res">@android:string/staged_s_string</item>
520 </style>
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700521 <declare-styleable name="ClientStyleable">
522 <attr name="android:finalized_res" />
523 <attr name="android:staged_s_res" />
524 <attr name="bar" />
525 </declare-styleable>
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700526 <public name="MyStyle" type="style" id="0x7f020000" />
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700527 </resources>)";
528
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700529 SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = app_values};
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700530
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700531 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
541TEST_F(LinkTest, StagedAndroidApi) {
542 StdErrDiagnostics diag;
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700543 const std::string android_apk = GetTestPath("android.apk");
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700544 const std::string android_java = GetTestPath("android-java");
545 BuildNonFinalizedSDK(android_apk, android_java, this, &diag);
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700546
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));
Ryan Mitchellca3b4f72021-03-29 14:47:02 -0700550 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=0x01010050; }"));
554 EXPECT_THAT(
555 android_r_contents,
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700556 HasSubstr("public static final int staged_s_string; static { staged_s_string=0x01fd0080; }"));
557 EXPECT_THAT(
558 android_r_contents,
Ryan Mitchellca3b4f72021-03-29 14:47:02 -0700559 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; }"));
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700563
564 const std::string app_apk = GetTestPath("app.apk");
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700565 const std::string app_java = GetTestPath("app-java");
566 BuildAppAgainstSDK(app_apk, app_java, android_apk, this, &diag);
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700567
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.get()}));
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(0x01010050));
586
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700587 result = am.GetResourceId("android:string/staged_s_string");
588 ASSERT_TRUE(result.has_value());
589 EXPECT_THAT(*result, Eq(0x01fd0080));
590
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700591 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));
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700598}
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700599
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700600TEST_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.get(), app_against_non_final.get()}));
634
635 auto result = am.GetResourceId("android:attr/finalized_res");
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700636 ASSERT_TRUE(result.has_value());
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700637 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.get(), app_against_final.get()}));
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 }
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700680}
681
Ryan Mitchell326e35ff2021-04-12 07:50:42 -0700682TEST_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
Josh Houd3bd4522022-02-14 10:24:42 +0800787TEST_F(LinkTest, ParseLocaleConfig) {
788 StdErrDiagnostics diag;
789 const std::string xml_values =
790 R"(<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
791 <locale android:name="pt"/>
792 <locale android:name="chr"/>
793 <locale android:name="chr-US"/>
794 <locale android:name="zh-Hant"/>
795 <locale android:name="es-419"/>
796 <locale android:name="en-US"/>
797 <locale android:name="zh-Hans-SG"/>
798 </locale-config>)";
799
800 const std::string res = GetTestPath("test-res");
801 ASSERT_TRUE(CompileFile(GetTestPath("res/xml/locale_config.xml"), xml_values, res, &diag));
802
803 const std::string out_apk = GetTestPath("out.apk");
804 auto link_args = LinkCommandBuilder(this)
805 .SetManifestFile(ManifestBuilder(this).SetPackageName("com.test").Build())
806 .AddCompiledResDir(res, &diag)
807 .AddFlag("--no-auto-version")
808 .Build(out_apk);
809 ASSERT_TRUE(Link(link_args, &diag));
810
811 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
812 ASSERT_THAT(apk, Ne(nullptr));
813
814 auto xml = apk->LoadXml("res/xml/locale_config.xml", &diag);
815 ASSERT_THAT(xml, NotNull());
816 EXPECT_THAT(xml->root->name, Eq("locale-config"));
817 ASSERT_THAT(xml->root->children.size(), Eq(7));
818 for (auto& node : xml->root->children) {
819 const xml::Element* child_el = xml::NodeCast<xml::Element>(node.get());
820 ASSERT_THAT(child_el, NotNull());
821 EXPECT_THAT(child_el->name, Eq("locale"));
822
823 auto& xml_attrs = child_el->attributes;
824 for (auto& attr : xml_attrs) {
825 std::string locale = "b+";
826 locale += attr.value;
827 std::replace(locale.begin(), locale.end(), '-', '+');
828 ConfigDescription config;
829 ASSERT_TRUE(ConfigDescription::Parse(locale, &config));
830 }
831 }
832}
833
Donald Chai121c6e82019-06-12 12:51:57 -0700834} // namespace aapt