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