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