blob: 683ccad2fdf28d261cda5d2b6b843772dd81924c [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"
Jeremy Meyer56f36e82022-05-20 20:35:42 +000022#include "Diagnostics.h"
Ryan Mitchell479fa392019-01-02 17:15:39 -080023#include "LoadedApk.h"
24#include "test/Test.h"
25
Josh Houd3bd4522022-02-14 10:24:42 +080026using android::ConfigDescription;
Ryan Mitchell479fa392019-01-02 17:15:39 -080027using testing::Eq;
Ryan Mitchell5855de72021-02-24 14:39:13 -080028using testing::HasSubstr;
Ryan Mitchell326e35ff2021-04-12 07:50:42 -070029using testing::IsNull;
Ryan Mitchell479fa392019-01-02 17:15:39 -080030using testing::Ne;
Ryan Mitchell2e9bec12021-03-22 09:31:00 -070031using testing::NotNull;
Ryan Mitchell479fa392019-01-02 17:15:39 -080032
33namespace aapt {
34
35using LinkTest = CommandTestFixture;
36
37TEST_F(LinkTest, RemoveRawXmlStrings) {
38 StdErrDiagnostics diag;
39 const std::string compiled_files_dir = GetTestPath("compiled");
40 ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)",
41 compiled_files_dir, &diag));
42
43 const std::string out_apk = GetTestPath("out.apk");
44 std::vector<std::string> link_args = {
45 "--manifest", GetDefaultManifest(),
46 "-o", out_apk,
47 };
48
49 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
50
51 // Load the binary xml tree
52 android::ResXMLTree tree;
53 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
Ryan Mitchell1d008d12021-03-19 14:54:17 -070054 ASSERT_THAT(apk, Ne(nullptr));
55
Winsonb7be7932019-01-23 11:10:52 -080056 std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
57 ASSERT_THAT(data, Ne(nullptr));
Winsonb7be7932019-01-23 11:10:52 -080058 AssertLoadXml(apk.get(), data.get(), &tree);
Ryan Mitchell479fa392019-01-02 17:15:39 -080059
60 // Check that the raw string index has not been assigned
61 EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1));
62}
63
64TEST_F(LinkTest, KeepRawXmlStrings) {
65 StdErrDiagnostics diag;
66 const std::string compiled_files_dir = GetTestPath("compiled");
67 ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)",
68 compiled_files_dir, &diag));
69
70 const std::string out_apk = GetTestPath("out.apk");
71 std::vector<std::string> link_args = {
72 "--manifest", GetDefaultManifest(),
73 "-o", out_apk,
74 "--keep-raw-values"
75 };
76
77 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
78
79 // Load the binary xml tree
80 android::ResXMLTree tree;
81 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
Ryan Mitchell1d008d12021-03-19 14:54:17 -070082 ASSERT_THAT(apk, Ne(nullptr));
83
Winsonb7be7932019-01-23 11:10:52 -080084 std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
85 ASSERT_THAT(data, Ne(nullptr));
Winsonb7be7932019-01-23 11:10:52 -080086 AssertLoadXml(apk.get(), data.get(), &tree);
Ryan Mitchell479fa392019-01-02 17:15:39 -080087
88 // Check that the raw string index has been set to the correct string pool entry
89 int32_t raw_index = tree.getAttributeValueStringID(0);
90 ASSERT_THAT(raw_index, Ne(-1));
Jeremy Meyer56f36e82022-05-20 20:35:42 +000091 EXPECT_THAT(android::util::GetString(tree.getStrings(), static_cast<size_t>(raw_index)),
92 Eq("007"));
Ryan Mitchell479fa392019-01-02 17:15:39 -080093}
94
Ryan Mitchell81dfca02019-06-07 10:20:27 -070095TEST_F(LinkTest, NoCompressAssets) {
96 StdErrDiagnostics diag;
97 std::string content(500, 'a');
98 WriteFile(GetTestPath("assets/testtxt"), content);
99 WriteFile(GetTestPath("assets/testtxt2"), content);
100 WriteFile(GetTestPath("assets/test.txt"), content);
101 WriteFile(GetTestPath("assets/test.hello.txt"), content);
102 WriteFile(GetTestPath("assets/test.hello.xml"), content);
103
104 const std::string out_apk = GetTestPath("out.apk");
105 std::vector<std::string> link_args = {
106 "--manifest", GetDefaultManifest(),
107 "-o", out_apk,
108 "-0", ".txt",
109 "-0", "txt2",
110 "-0", ".hello.txt",
111 "-0", "hello.xml",
112 "-A", GetTestPath("assets")
113 };
114
115 ASSERT_TRUE(Link(link_args, &diag));
116
117 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
118 ASSERT_THAT(apk, Ne(nullptr));
119 io::IFileCollection* zip = apk->GetFileCollection();
120 ASSERT_THAT(zip, Ne(nullptr));
121
122 auto file = zip->FindFile("assets/testtxt");
123 ASSERT_THAT(file, Ne(nullptr));
124 EXPECT_TRUE(file->WasCompressed());
125
126 file = zip->FindFile("assets/testtxt2");
127 ASSERT_THAT(file, Ne(nullptr));
128 EXPECT_FALSE(file->WasCompressed());
129
130 file = zip->FindFile("assets/test.txt");
131 ASSERT_THAT(file, Ne(nullptr));
132 EXPECT_FALSE(file->WasCompressed());
133
134 file = zip->FindFile("assets/test.hello.txt");
135 ASSERT_THAT(file, Ne(nullptr));
136 EXPECT_FALSE(file->WasCompressed());
137
138 file = zip->FindFile("assets/test.hello.xml");
139 ASSERT_THAT(file, Ne(nullptr));
140 EXPECT_FALSE(file->WasCompressed());
141}
142
143TEST_F(LinkTest, NoCompressResources) {
144 StdErrDiagnostics diag;
145 std::string content(500, 'a');
146 const std::string compiled_files_dir = GetTestPath("compiled");
147 ASSERT_TRUE(CompileFile(GetTestPath("res/raw/testtxt"), content, compiled_files_dir, &diag));
148 ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test.txt"), content, compiled_files_dir, &diag));
149 ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test1.hello.txt"), content, compiled_files_dir,
150 &diag));
151 ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test2.goodbye.xml"), content, compiled_files_dir,
152 &diag));
153
154 const std::string out_apk = GetTestPath("out.apk");
155 std::vector<std::string> link_args = {
156 "--manifest", GetDefaultManifest(),
157 "-o", out_apk,
158 "-0", ".txt",
159 "-0", ".hello.txt",
160 "-0", "goodbye.xml",
161 };
162
163 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
164
165 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
166 ASSERT_THAT(apk, Ne(nullptr));
167 io::IFileCollection* zip = apk->GetFileCollection();
168 ASSERT_THAT(zip, Ne(nullptr));
169
170 auto file = zip->FindFile("res/raw/testtxt");
171 ASSERT_THAT(file, Ne(nullptr));
172 EXPECT_TRUE(file->WasCompressed());
173
174 file = zip->FindFile("res/raw/test.txt");
175 ASSERT_THAT(file, Ne(nullptr));
176 EXPECT_FALSE(file->WasCompressed());
177
178 file = zip->FindFile("res/raw/test1.hello.hello.txt");
179 ASSERT_THAT(file, Ne(nullptr));
180 EXPECT_FALSE(file->WasCompressed());
181
182 file = zip->FindFile("res/raw/test2.goodbye.goodbye.xml");
183 ASSERT_THAT(file, Ne(nullptr));
184 EXPECT_FALSE(file->WasCompressed());
185}
186
Donald Chai121c6e82019-06-12 12:51:57 -0700187TEST_F(LinkTest, OverlayStyles) {
188 StdErrDiagnostics diag;
189 const std::string compiled_files_dir = GetTestPath("compiled");
190 const std::string override_files_dir = GetTestPath("compiled-override");
191 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
192 R"(<resources>
193 <style name="MyStyle">
194 <item name="android:textColor">#123</item>
195 </style>
196 </resources>)",
197 compiled_files_dir, &diag));
198 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values-override.xml"),
199 R"(<resources>
200 <style name="MyStyle">
201 <item name="android:background">#456</item>
202 </style>
203 </resources>)",
204 override_files_dir, &diag));
205
206
207 const std::string out_apk = GetTestPath("out.apk");
208 std::vector<std::string> link_args = {
209 "--manifest", GetDefaultManifest(kDefaultPackageName),
210 "-o", out_apk,
211 };
212 const auto override_files = file::FindFiles(override_files_dir, &diag);
213 for (const auto &override_file : override_files.value()) {
214 link_args.push_back("-R");
215 link_args.push_back(file::BuildPath({override_files_dir, override_file}));
216 }
217 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
218
219 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
Ryan Mitchell1d008d12021-03-19 14:54:17 -0700220 ASSERT_THAT(apk, Ne(nullptr));
221
Donald Chai121c6e82019-06-12 12:51:57 -0700222 const Style* actual_style = test::GetValue<Style>(
223 apk->GetResourceTable(), std::string(kDefaultPackageName) + ":style/MyStyle");
224 ASSERT_NE(actual_style, nullptr);
225 ASSERT_EQ(actual_style->entries.size(), 2);
226 EXPECT_EQ(actual_style->entries[0].key.id, 0x01010098); // android:textColor
227 EXPECT_EQ(actual_style->entries[1].key.id, 0x010100d4); // android:background
228}
229
230TEST_F(LinkTest, OverrideStylesInsteadOfOverlaying) {
231 StdErrDiagnostics diag;
232 const std::string compiled_files_dir = GetTestPath("compiled");
233 const std::string override_files_dir = GetTestPath("compiled-override");
234 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
235 R"(<resources>
236 <style name="MyStyle">
237 <item name="android:textColor">#123</item>
238 </style>
239 </resources>)",
240 compiled_files_dir, &diag));
241 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values-override.xml"),
242 R"(<resources>
243 <style name="MyStyle">
244 <item name="android:background">#456</item>
245 </style>
246 </resources>)",
247 override_files_dir, &diag));
248
249
250 const std::string out_apk = GetTestPath("out.apk");
251 std::vector<std::string> link_args = {
252 "--manifest", GetDefaultManifest(kDefaultPackageName),
253 "--override-styles-instead-of-overlaying",
254 "-o", out_apk,
255 };
256 const auto override_files = file::FindFiles(override_files_dir, &diag);
257 for (const auto &override_file : override_files.value()) {
258 link_args.push_back("-R");
259 link_args.push_back(file::BuildPath({override_files_dir, override_file}));
260 }
261 ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
262
263 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
Ryan Mitchell1d008d12021-03-19 14:54:17 -0700264 ASSERT_THAT(apk, Ne(nullptr));
265
Donald Chai121c6e82019-06-12 12:51:57 -0700266 const Style* actual_style = test::GetValue<Style>(
267 apk->GetResourceTable(), std::string(kDefaultPackageName) + ":style/MyStyle");
268 ASSERT_NE(actual_style, nullptr);
269 ASSERT_EQ(actual_style->entries.size(), 1);
270 EXPECT_EQ(actual_style->entries[0].key.id, 0x010100d4); // android:background
271}
272
Udam Sainib228df32019-06-18 16:50:34 -0700273TEST_F(LinkTest, AppInfoWithUsesSplit) {
274 StdErrDiagnostics diag;
275 const std::string base_files_dir = GetTestPath("base");
276 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
277 R"(<resources>
278 <string name="bar">bar</string>
279 </resources>)",
280 base_files_dir, &diag));
281 const std::string base_apk = GetTestPath("base.apk");
282 std::vector<std::string> link_args = {
283 "--manifest", GetDefaultManifest("com.aapt2.app"),
284 "-o", base_apk,
285 };
286 ASSERT_TRUE(Link(link_args, base_files_dir, &diag));
287
288 const std::string feature_manifest = GetTestPath("feature_manifest.xml");
289 WriteFile(feature_manifest, android::base::StringPrintf(R"(
290 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
291 package="com.aapt2.app" split="feature1">
292 </manifest>)"));
293 const std::string feature_files_dir = GetTestPath("feature");
294 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
295 R"(<resources>
296 <string name="foo">foo</string>
297 </resources>)",
298 feature_files_dir, &diag));
299 const std::string feature_apk = GetTestPath("feature.apk");
300 const std::string feature_package_id = "0x80";
301 link_args = {
302 "--manifest", feature_manifest,
303 "-I", base_apk,
304 "--package-id", feature_package_id,
305 "-o", feature_apk,
306 };
307 ASSERT_TRUE(Link(link_args, feature_files_dir, &diag));
308
309 const std::string feature2_manifest = GetTestPath("feature2_manifest.xml");
310 WriteFile(feature2_manifest, android::base::StringPrintf(R"(
311 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
312 package="com.aapt2.app" split="feature2">
313 <uses-split android:name="feature1"/>
314 </manifest>)"));
315 const std::string feature2_files_dir = GetTestPath("feature2");
316 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
317 R"(<resources>
318 <string-array name="string_array">
319 <item>@string/bar</item>
320 <item>@string/foo</item>
321 </string-array>
322 </resources>)",
323 feature2_files_dir, &diag));
324 const std::string feature2_apk = GetTestPath("feature2.apk");
325 const std::string feature2_package_id = "0x81";
326 link_args = {
327 "--manifest", feature2_manifest,
328 "-I", base_apk,
329 "-I", feature_apk,
330 "--package-id", feature2_package_id,
331 "-o", feature2_apk,
332 };
333 ASSERT_TRUE(Link(link_args, feature2_files_dir, &diag));
334}
335
Ryan Mitchell5855de72021-02-24 14:39:13 -0800336TEST_F(LinkTest, SharedLibraryAttributeRJava) {
337 StdErrDiagnostics diag;
338 const std::string lib_values =
339 R"(<resources>
340 <attr name="foo"/>
341 <public type="attr" name="foo" id="0x00010001"/>
342 <declare-styleable name="LibraryStyleable">
343 <attr name="foo" />
344 </declare-styleable>
345 </resources>)";
346
347 const std::string client_values =
348 R"(<resources>
349 <attr name="bar" />
350 <declare-styleable name="ClientStyleable">
351 <attr name="com.example.lib:foo" />
352 <attr name="bar" />
353 </declare-styleable>
354 </resources>)";
355
356 // Build a library with a public attribute
357 const std::string lib_res = GetTestPath("library-res");
358 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), lib_values, lib_res, &diag));
359
360 const std::string lib_apk = GetTestPath("library.apk");
361 const std::string lib_java = GetTestPath("library_java");
362 // clang-format off
363 auto lib_manifest = ManifestBuilder(this)
364 .SetPackageName("com.example.lib")
365 .Build();
366
367 auto lib_link_args = LinkCommandBuilder(this)
368 .SetManifestFile(lib_manifest)
369 .AddFlag("--shared-lib")
370 .AddParameter("--java", lib_java)
371 .AddCompiledResDir(lib_res, &diag)
372 .Build(lib_apk);
373 // clang-format on
374 ASSERT_TRUE(Link(lib_link_args, &diag));
375
376 const std::string lib_r_java = lib_java + "/com/example/lib/R.java";
377 std::string lib_r_contents;
378 ASSERT_TRUE(android::base::ReadFileToString(lib_r_java, &lib_r_contents));
379 EXPECT_THAT(lib_r_contents, HasSubstr(" public static int foo=0x00010001;"));
380 EXPECT_THAT(lib_r_contents, HasSubstr(" com.example.lib.R.attr.foo"));
381
382 // Build a client that uses the library attribute in a declare-styleable
383 const std::string client_res = GetTestPath("client-res");
384 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), client_values, client_res, &diag));
385
386 const std::string client_apk = GetTestPath("client.apk");
387 const std::string client_java = GetTestPath("client_java");
388 // clang-format off
389 auto client_manifest = ManifestBuilder(this)
390 .SetPackageName("com.example.client")
391 .Build();
392
393 auto client_link_args = LinkCommandBuilder(this)
394 .SetManifestFile(client_manifest)
395 .AddParameter("--java", client_java)
396 .AddParameter("-I", lib_apk)
397 .AddCompiledResDir(client_res, &diag)
398 .Build(client_apk);
399 // clang-format on
400 ASSERT_TRUE(Link(client_link_args, &diag));
401
402 const std::string client_r_java = client_java + "/com/example/client/R.java";
403 std::string client_r_contents;
404 ASSERT_TRUE(android::base::ReadFileToString(client_r_java, &client_r_contents));
405 EXPECT_THAT(client_r_contents, HasSubstr(" com.example.lib.R.attr.foo, 0x7f010000"));
406}
407
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700408struct SourceXML {
409 std::string res_file_path;
410 std::string file_contents;
411};
412
413static void BuildApk(const std::vector<SourceXML>& source_files, const std::string& apk_path,
414 LinkCommandBuilder&& link_args, CommandTestFixture* fixture,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000415 android::IDiagnostics* diag) {
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700416 TemporaryDir res_dir;
417 TemporaryDir compiled_res_dir;
418 for (auto& source_file : source_files) {
419 ASSERT_TRUE(fixture->CompileFile(res_dir.path + source_file.res_file_path,
420 source_file.file_contents, compiled_res_dir.path, diag));
421 }
422 ASSERT_TRUE(fixture->Link(
423 link_args.AddCompiledResDir(compiled_res_dir.path, diag).Build(apk_path), diag));
424}
425
426static void BuildSDK(const std::vector<SourceXML>& source_files, const std::string& apk_path,
427 const std::string& java_root_path, CommandTestFixture* fixture,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000428 android::IDiagnostics* diag) {
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700429 auto android_manifest = ManifestBuilder(fixture).SetPackageName("android").Build();
430
431 auto android_link_args = LinkCommandBuilder(fixture)
432 .SetManifestFile(android_manifest)
433 .AddParameter("--private-symbols", "com.android.internal")
434 .AddParameter("--java", java_root_path);
435
436 BuildApk(source_files, apk_path, std::move(android_link_args), fixture, diag);
437}
438
439static void BuildNonFinalizedSDK(const std::string& apk_path, const std::string& java_path,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000440 CommandTestFixture* fixture, android::IDiagnostics* diag) {
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700441 const std::string android_values =
442 R"(<resources>
443 <public type="attr" name="finalized_res" id="0x01010001"/>
444
445 <!-- S staged attributes (support staged resources in the same type id) -->
446 <staging-public-group type="attr" first-id="0x01010050">
447 <public name="staged_s_res" />
448 </staging-public-group>
449
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700450 <staging-public-group type="string" first-id="0x01fd0080">
451 <public name="staged_s_string" />
452 </staging-public-group>
453
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700454 <!-- SV2 staged attributes (support staged resources in a separate type id) -->
455 <staging-public-group type="attr" first-id="0x01ff0049">
456 <public name="staged_s2_res" />
457 </staging-public-group>
458
459 <!-- T staged attributes (support staged resources in multiple separate type ids) -->
460 <staging-public-group type="attr" first-id="0x01fe0063">
461 <public name="staged_t_res" />
462 </staging-public-group>
463
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700464 <attr name="finalized_res" />
465 <attr name="staged_s_res" />
466 <attr name="staged_s2_res" />
467 <attr name="staged_t_res" />
468 <string name="staged_s_string">Hello</string>
469 </resources>)";
470
471 SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values};
472 BuildSDK({source_xml}, apk_path, java_path, fixture, diag);
473}
474
475static void BuildFinalizedSDK(const std::string& apk_path, const std::string& java_path,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000476 CommandTestFixture* fixture, android::IDiagnostics* diag) {
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700477 const std::string android_values =
478 R"(<resources>
479 <public type="attr" name="finalized_res" id="0x01010001"/>
480 <public type="attr" name="staged_s_res" id="0x01010002"/>
481 <public type="attr" name="staged_s2_res" id="0x01010003"/>
482 <public type="string" name="staged_s_string" id="0x01020000"/>
483
484 <!-- S staged attributes (support staged resources in the same type id) -->
485 <staging-public-group-final type="attr" first-id="0x01010050">
486 <public name="staged_s_res" />
487 </staging-public-group-final>
488
489 <staging-public-group-final type="string" first-id="0x01fd0080">
490 <public name="staged_s_string" />
491 </staging-public-group-final>
492
493 <!-- SV2 staged attributes (support staged resources in a separate type id) -->
494 <staging-public-group-final type="attr" first-id="0x01ff0049">
495 <public name="staged_s2_res" />
496 </staging-public-group-final>
497
498 <!-- T staged attributes (support staged resources in multiple separate type ids) -->
499 <staging-public-group type="attr" first-id="0x01fe0063">
500 <public name="staged_t_res" />
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700501 </staging-public-group>
502
503 <attr name="finalized_res" />
504 <attr name="staged_s_res" />
505 <attr name="staged_s2_res" />
506 <attr name="staged_t_res" />
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700507 <string name="staged_s_string">Hello</string>
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700508 </resources>)";
509
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700510 SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values};
511 BuildSDK({source_xml}, apk_path, java_path, fixture, diag);
512}
513
514static void BuildAppAgainstSDK(const std::string& apk_path, const std::string& java_path,
515 const std::string& sdk_path, CommandTestFixture* fixture,
Jeremy Meyer56f36e82022-05-20 20:35:42 +0000516 android::IDiagnostics* diag) {
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700517 const std::string app_values =
518 R"(<resources xmlns:android="http://schemas.android.com/apk/res/android">
519 <attr name="bar" />
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700520 <style name="MyStyle">
521 <item name="android:staged_s_res">@android:string/staged_s_string</item>
522 </style>
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700523 <declare-styleable name="ClientStyleable">
524 <attr name="android:finalized_res" />
525 <attr name="android:staged_s_res" />
526 <attr name="bar" />
527 </declare-styleable>
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700528 <public name="MyStyle" type="style" id="0x7f020000" />
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700529 </resources>)";
530
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700531 SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = app_values};
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700532
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700533 auto app_manifest = ManifestBuilder(fixture).SetPackageName("com.example.app").Build();
534
535 auto app_link_args = LinkCommandBuilder(fixture)
536 .SetManifestFile(app_manifest)
537 .AddParameter("--java", java_path)
538 .AddParameter("-I", sdk_path);
539
540 BuildApk({source_xml}, apk_path, std::move(app_link_args), fixture, diag);
541}
542
543TEST_F(LinkTest, StagedAndroidApi) {
544 StdErrDiagnostics diag;
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700545 const std::string android_apk = GetTestPath("android.apk");
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700546 const std::string android_java = GetTestPath("android-java");
547 BuildNonFinalizedSDK(android_apk, android_java, this, &diag);
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700548
549 const std::string android_r_java = android_java + "/android/R.java";
550 std::string android_r_contents;
551 ASSERT_TRUE(android::base::ReadFileToString(android_r_java, &android_r_contents));
Ryan Mitchellca3b4f72021-03-29 14:47:02 -0700552 EXPECT_THAT(android_r_contents, HasSubstr("public static final int finalized_res=0x01010001;"));
553 EXPECT_THAT(
554 android_r_contents,
555 HasSubstr("public static final int staged_s_res; static { staged_s_res=0x01010050; }"));
556 EXPECT_THAT(
557 android_r_contents,
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700558 HasSubstr("public static final int staged_s_string; static { staged_s_string=0x01fd0080; }"));
559 EXPECT_THAT(
560 android_r_contents,
Ryan Mitchellca3b4f72021-03-29 14:47:02 -0700561 HasSubstr("public static final int staged_s2_res; static { staged_s2_res=0x01ff0049; }"));
562 EXPECT_THAT(
563 android_r_contents,
564 HasSubstr("public static final int staged_t_res; static { staged_t_res=0x01fe0063; }"));
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700565
566 const std::string app_apk = GetTestPath("app.apk");
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700567 const std::string app_java = GetTestPath("app-java");
568 BuildAppAgainstSDK(app_apk, app_java, android_apk, this, &diag);
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700569
570 const std::string client_r_java = app_java + "/com/example/app/R.java";
571 std::string client_r_contents;
572 ASSERT_TRUE(android::base::ReadFileToString(client_r_java, &client_r_contents));
573 EXPECT_THAT(client_r_contents, HasSubstr(" 0x01010001, android.R.attr.staged_s_res, 0x7f010000"));
574
575 // Test that the resource ids of staged and non-staged resource can be retrieved
576 android::AssetManager2 am;
577 auto android_asset = android::ApkAssets::Load(android_apk);
578 ASSERT_THAT(android_asset, NotNull());
579 ASSERT_TRUE(am.SetApkAssets({android_asset.get()}));
580
581 auto result = am.GetResourceId("android:attr/finalized_res");
582 ASSERT_TRUE(result.has_value());
583 EXPECT_THAT(*result, Eq(0x01010001));
584
585 result = am.GetResourceId("android:attr/staged_s_res");
586 ASSERT_TRUE(result.has_value());
587 EXPECT_THAT(*result, Eq(0x01010050));
588
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700589 result = am.GetResourceId("android:string/staged_s_string");
590 ASSERT_TRUE(result.has_value());
591 EXPECT_THAT(*result, Eq(0x01fd0080));
592
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700593 result = am.GetResourceId("android:attr/staged_s2_res");
594 ASSERT_TRUE(result.has_value());
595 EXPECT_THAT(*result, Eq(0x01ff0049));
596
597 result = am.GetResourceId("android:attr/staged_t_res");
598 ASSERT_TRUE(result.has_value());
599 EXPECT_THAT(*result, Eq(0x01fe0063));
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700600}
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700601
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700602TEST_F(LinkTest, FinalizedAndroidApi) {
603 StdErrDiagnostics diag;
604 const std::string android_apk = GetTestPath("android.apk");
605 const std::string android_java = GetTestPath("android-java");
606 BuildFinalizedSDK(android_apk, android_java, this, &diag);
607
608 const std::string android_r_java = android_java + "/android/R.java";
609 std::string android_r_contents;
610 ASSERT_TRUE(android::base::ReadFileToString(android_r_java, &android_r_contents));
611 EXPECT_THAT(android_r_contents, HasSubstr("public static final int finalized_res=0x01010001;"));
612 EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s_res=0x01010002;"));
613 EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s_string=0x01020000;"));
614 EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s2_res=0x01010003;"));
615 EXPECT_THAT(
616 android_r_contents,
617 HasSubstr("public static final int staged_t_res; static { staged_t_res=0x01fe0063; }"));
618 ;
619
620 // Build an application against the non-finalized SDK and then load it into an AssetManager with
621 // the finalized SDK.
622 const std::string non_finalized_android_apk = GetTestPath("non-finalized-android.apk");
623 const std::string non_finalized_android_java = GetTestPath("non-finalized-android-java");
624 BuildNonFinalizedSDK(non_finalized_android_apk, non_finalized_android_java, this, &diag);
625
626 const std::string app_apk = GetTestPath("app.apk");
627 const std::string app_java = GetTestPath("app-java");
628 BuildAppAgainstSDK(app_apk, app_java, non_finalized_android_apk, this, &diag);
629
630 android::AssetManager2 am;
631 auto android_asset = android::ApkAssets::Load(android_apk);
632 auto app_against_non_final = android::ApkAssets::Load(app_apk);
633 ASSERT_THAT(android_asset, NotNull());
634 ASSERT_THAT(app_against_non_final, NotNull());
635 ASSERT_TRUE(am.SetApkAssets({android_asset.get(), app_against_non_final.get()}));
636
637 auto result = am.GetResourceId("android:attr/finalized_res");
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700638 ASSERT_TRUE(result.has_value());
Ryan Mitchell2fedba92021-04-23 07:47:38 -0700639 EXPECT_THAT(*result, Eq(0x01010001));
640
641 result = am.GetResourceId("android:attr/staged_s_res");
642 ASSERT_TRUE(result.has_value());
643 EXPECT_THAT(*result, Eq(0x01010002));
644
645 result = am.GetResourceId("android:string/staged_s_string");
646 ASSERT_TRUE(result.has_value());
647 EXPECT_THAT(*result, Eq(0x01020000));
648
649 result = am.GetResourceId("android:attr/staged_s2_res");
650 ASSERT_TRUE(result.has_value());
651 EXPECT_THAT(*result, Eq(0x01010003));
652
653 {
654 auto style = am.GetBag(0x7f020000);
655 ASSERT_TRUE(style.has_value());
656
657 auto& entry = (*style)->entries[0];
658 EXPECT_THAT(entry.key, Eq(0x01010002));
659 EXPECT_THAT(entry.value.dataType, Eq(android::Res_value::TYPE_REFERENCE));
660 EXPECT_THAT(entry.value.data, Eq(0x01020000));
661 }
662
663 // Re-compile the application against the finalized SDK and then load it into an AssetManager with
664 // the finalized SDK.
665 const std::string app_apk_respin = GetTestPath("app-respin.apk");
666 const std::string app_java_respin = GetTestPath("app-respin-java");
667 BuildAppAgainstSDK(app_apk_respin, app_java_respin, android_apk, this, &diag);
668
669 auto app_against_final = android::ApkAssets::Load(app_apk_respin);
670 ASSERT_THAT(app_against_final, NotNull());
671 ASSERT_TRUE(am.SetApkAssets({android_asset.get(), app_against_final.get()}));
672
673 {
674 auto style = am.GetBag(0x7f020000);
675 ASSERT_TRUE(style.has_value());
676
677 auto& entry = (*style)->entries[0];
678 EXPECT_THAT(entry.key, Eq(0x01010002));
679 EXPECT_THAT(entry.value.dataType, Eq(android::Res_value::TYPE_REFERENCE));
680 EXPECT_THAT(entry.value.data, Eq(0x01020000));
681 }
Ryan Mitchell2e9bec12021-03-22 09:31:00 -0700682}
683
Ryan Mitchell326e35ff2021-04-12 07:50:42 -0700684TEST_F(LinkTest, MacroSubstitution) {
685 StdErrDiagnostics diag;
686 const std::string values =
687 R"(<resources xmlns:an="http://schemas.android.com/apk/res/android">
688 <macro name="is_enabled">true</macro>
689 <macro name="deep_is_enabled">@macro/is_enabled</macro>
690 <macro name="attr_ref">?is_enabled_attr</macro>
691 <macro name="raw_string">Hello World!</macro>
692 <macro name="android_ref">@an:color/primary_text_dark</macro>
693
694 <attr name="is_enabled_attr" />
695 <public type="attr" name="is_enabled_attr" id="0x7f010000"/>
696
697 <string name="is_enabled_str">@macro/is_enabled</string>
698 <bool name="is_enabled_bool">@macro/deep_is_enabled</bool>
699
700 <array name="my_array">
701 <item>@macro/is_enabled</item>
702 </array>
703
704 <style name="MyStyle">
705 <item name="android:background">@macro/attr_ref</item>
706 <item name="android:fontFamily">@macro/raw_string</item>
707 </style>
708 </resources>)";
709
710 const std::string xml_values =
711 R"(<SomeLayout xmlns:android="http://schemas.android.com/apk/res/android"
712 android:background="@macro/android_ref"
713 android:fontFamily="@macro/raw_string">
714 </SomeLayout>)";
715
716 // Build a library with a public attribute
717 const std::string lib_res = GetTestPath("test-res");
718 ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), values, lib_res, &diag));
719 ASSERT_TRUE(CompileFile(GetTestPath("res/layout/layout.xml"), xml_values, lib_res, &diag));
720
721 const std::string lib_apk = GetTestPath("test.apk");
722 // clang-format off
723 auto lib_link_args = LinkCommandBuilder(this)
724 .SetManifestFile(ManifestBuilder(this).SetPackageName("com.test").Build())
725 .AddCompiledResDir(lib_res, &diag)
726 .AddFlag("--no-auto-version")
727 .Build(lib_apk);
728 // clang-format on
729 ASSERT_TRUE(Link(lib_link_args, &diag));
730
731 auto apk = LoadedApk::LoadApkFromPath(lib_apk, &diag);
732 ASSERT_THAT(apk, NotNull());
733
734 // Test that the type flags determines the value type
735 auto actual_bool =
736 test::GetValue<BinaryPrimitive>(apk->GetResourceTable(), "com.test:bool/is_enabled_bool");
737 ASSERT_THAT(actual_bool, NotNull());
738 EXPECT_EQ(android::Res_value::TYPE_INT_BOOLEAN, actual_bool->value.dataType);
739 EXPECT_EQ(0xffffffffu, actual_bool->value.data);
740
741 auto actual_str =
742 test::GetValue<String>(apk->GetResourceTable(), "com.test:string/is_enabled_str");
743 ASSERT_THAT(actual_str, NotNull());
744 EXPECT_EQ(*actual_str->value, "true");
745
746 // Test nested data structures
747 auto actual_array = test::GetValue<Array>(apk->GetResourceTable(), "com.test:array/my_array");
748 ASSERT_THAT(actual_array, NotNull());
749 EXPECT_THAT(actual_array->elements.size(), Eq(1));
750
751 auto array_el_ref = ValueCast<BinaryPrimitive>(actual_array->elements[0].get());
752 ASSERT_THAT(array_el_ref, NotNull());
753 EXPECT_THAT(array_el_ref->value.dataType, Eq(android::Res_value::TYPE_INT_BOOLEAN));
754 EXPECT_THAT(array_el_ref->value.data, Eq(0xffffffffu));
755
756 auto actual_style = test::GetValue<Style>(apk->GetResourceTable(), "com.test:style/MyStyle");
757 ASSERT_THAT(actual_style, NotNull());
758 EXPECT_THAT(actual_style->entries.size(), Eq(2));
759
760 {
761 auto style_el = ValueCast<Reference>(actual_style->entries[0].value.get());
762 ASSERT_THAT(style_el, NotNull());
763 EXPECT_THAT(style_el->reference_type, Eq(Reference::Type::kAttribute));
764 EXPECT_THAT(style_el->id, Eq(0x7f010000));
765 }
766
767 {
768 auto style_el = ValueCast<String>(actual_style->entries[1].value.get());
769 ASSERT_THAT(style_el, NotNull());
770 EXPECT_THAT(*style_el->value, Eq("Hello World!"));
771 }
772
773 // Test substitution in compiled xml files
774 auto xml = apk->LoadXml("res/layout/layout.xml", &diag);
775 ASSERT_THAT(xml, NotNull());
776
777 auto& xml_attrs = xml->root->attributes;
778 ASSERT_THAT(xml_attrs.size(), Eq(2));
779
780 auto attr_value = ValueCast<Reference>(xml_attrs[0].compiled_value.get());
781 ASSERT_THAT(attr_value, NotNull());
782 EXPECT_THAT(attr_value->reference_type, Eq(Reference::Type::kResource));
783 EXPECT_THAT(attr_value->id, Eq(0x01060001));
784
785 EXPECT_THAT(xml_attrs[1].compiled_value.get(), IsNull());
786 EXPECT_THAT(xml_attrs[1].value, Eq("Hello World!"));
787}
788
Josh Houd3bd4522022-02-14 10:24:42 +0800789TEST_F(LinkTest, ParseLocaleConfig) {
790 StdErrDiagnostics diag;
791 const std::string xml_values =
792 R"(<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
793 <locale android:name="pt"/>
794 <locale android:name="chr"/>
795 <locale android:name="chr-US"/>
796 <locale android:name="zh-Hant"/>
797 <locale android:name="es-419"/>
798 <locale android:name="en-US"/>
799 <locale android:name="zh-Hans-SG"/>
800 </locale-config>)";
801
802 const std::string res = GetTestPath("test-res");
803 ASSERT_TRUE(CompileFile(GetTestPath("res/xml/locale_config.xml"), xml_values, res, &diag));
804
805 const std::string out_apk = GetTestPath("out.apk");
806 auto link_args = LinkCommandBuilder(this)
807 .SetManifestFile(ManifestBuilder(this).SetPackageName("com.test").Build())
808 .AddCompiledResDir(res, &diag)
809 .AddFlag("--no-auto-version")
810 .Build(out_apk);
811 ASSERT_TRUE(Link(link_args, &diag));
812
813 std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
814 ASSERT_THAT(apk, Ne(nullptr));
815
816 auto xml = apk->LoadXml("res/xml/locale_config.xml", &diag);
817 ASSERT_THAT(xml, NotNull());
818 EXPECT_THAT(xml->root->name, Eq("locale-config"));
819 ASSERT_THAT(xml->root->children.size(), Eq(7));
820 for (auto& node : xml->root->children) {
821 const xml::Element* child_el = xml::NodeCast<xml::Element>(node.get());
822 ASSERT_THAT(child_el, NotNull());
823 EXPECT_THAT(child_el->name, Eq("locale"));
824
825 auto& xml_attrs = child_el->attributes;
826 for (auto& attr : xml_attrs) {
827 std::string locale = "b+";
828 locale += attr.value;
829 std::replace(locale.begin(), locale.end(), '-', '+');
830 ConfigDescription config;
831 ASSERT_TRUE(ConfigDescription::Parse(locale, &config));
832 }
833 }
834}
835
Donald Chai121c6e82019-06-12 12:51:57 -0700836} // namespace aapt