• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "Diagnostics.h"
20 #include "LoadedApk.h"
21 #include "android-base/file.h"
22 #include "android-base/stringprintf.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(android::util::GetString(tree.getStrings(), static_cast<size_t>(raw_index)),
90               Eq("007"));
91 }
92 
TEST_F(LinkTest,NoCompressAssets)93 TEST_F(LinkTest, NoCompressAssets) {
94   StdErrDiagnostics diag;
95   std::string content(500, 'a');
96   WriteFile(GetTestPath("assets/testtxt"), content);
97   WriteFile(GetTestPath("assets/testtxt2"), content);
98   WriteFile(GetTestPath("assets/test.txt"), content);
99   WriteFile(GetTestPath("assets/test.hello.txt"), content);
100   WriteFile(GetTestPath("assets/test.hello.xml"), content);
101 
102   const std::string out_apk = GetTestPath("out.apk");
103   std::vector<std::string> link_args = {
104       "--manifest", GetDefaultManifest(),
105       "-o", out_apk,
106       "-0", ".txt",
107       "-0", "txt2",
108       "-0", ".hello.txt",
109       "-0", "hello.xml",
110       "-A", GetTestPath("assets")
111   };
112 
113   ASSERT_TRUE(Link(link_args, &diag));
114 
115   std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
116   ASSERT_THAT(apk, Ne(nullptr));
117   io::IFileCollection* zip = apk->GetFileCollection();
118   ASSERT_THAT(zip, Ne(nullptr));
119 
120   auto file = zip->FindFile("assets/testtxt");
121   ASSERT_THAT(file, Ne(nullptr));
122   EXPECT_TRUE(file->WasCompressed());
123 
124   file = zip->FindFile("assets/testtxt2");
125   ASSERT_THAT(file, Ne(nullptr));
126   EXPECT_FALSE(file->WasCompressed());
127 
128   file = zip->FindFile("assets/test.txt");
129   ASSERT_THAT(file, Ne(nullptr));
130   EXPECT_FALSE(file->WasCompressed());
131 
132   file = zip->FindFile("assets/test.hello.txt");
133   ASSERT_THAT(file, Ne(nullptr));
134   EXPECT_FALSE(file->WasCompressed());
135 
136   file = zip->FindFile("assets/test.hello.xml");
137   ASSERT_THAT(file, Ne(nullptr));
138   EXPECT_FALSE(file->WasCompressed());
139 }
140 
TEST_F(LinkTest,NoCompressResources)141 TEST_F(LinkTest, NoCompressResources) {
142   StdErrDiagnostics diag;
143   std::string content(500, 'a');
144   const std::string compiled_files_dir = GetTestPath("compiled");
145   ASSERT_TRUE(CompileFile(GetTestPath("res/raw/testtxt"), content, compiled_files_dir, &diag));
146   ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test.txt"), content, compiled_files_dir, &diag));
147   ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test1.hello.txt"), content, compiled_files_dir,
148               &diag));
149   ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test2.goodbye.xml"), content, compiled_files_dir,
150               &diag));
151 
152   const std::string out_apk = GetTestPath("out.apk");
153   std::vector<std::string> link_args = {
154       "--manifest", GetDefaultManifest(),
155       "-o", out_apk,
156       "-0", ".txt",
157       "-0", ".hello.txt",
158       "-0", "goodbye.xml",
159   };
160 
161   ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
162 
163   std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
164   ASSERT_THAT(apk, Ne(nullptr));
165   io::IFileCollection* zip = apk->GetFileCollection();
166   ASSERT_THAT(zip, Ne(nullptr));
167 
168   auto file = zip->FindFile("res/raw/testtxt");
169   ASSERT_THAT(file, Ne(nullptr));
170   EXPECT_TRUE(file->WasCompressed());
171 
172   file = zip->FindFile("res/raw/test.txt");
173   ASSERT_THAT(file, Ne(nullptr));
174   EXPECT_FALSE(file->WasCompressed());
175 
176   file = zip->FindFile("res/raw/test1.hello.hello.txt");
177   ASSERT_THAT(file, Ne(nullptr));
178   EXPECT_FALSE(file->WasCompressed());
179 
180   file = zip->FindFile("res/raw/test2.goodbye.goodbye.xml");
181   ASSERT_THAT(file, Ne(nullptr));
182   EXPECT_FALSE(file->WasCompressed());
183 }
184 
TEST_F(LinkTest,OverlayStyles)185 TEST_F(LinkTest, OverlayStyles) {
186   StdErrDiagnostics diag;
187   const std::string compiled_files_dir = GetTestPath("compiled");
188   const std::string override_files_dir = GetTestPath("compiled-override");
189   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
190                           R"(<resources>
191                                <style name="MyStyle">
192                                  <item name="android:textColor">#123</item>
193                                </style>
194                              </resources>)",
195                           compiled_files_dir, &diag));
196   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values-override.xml"),
197                           R"(<resources>
198                                <style name="MyStyle">
199                                  <item name="android:background">#456</item>
200                                </style>
201                              </resources>)",
202                           override_files_dir, &diag));
203 
204 
205   const std::string out_apk = GetTestPath("out.apk");
206   std::vector<std::string> link_args = {
207       "--manifest", GetDefaultManifest(kDefaultPackageName),
208       "-o", out_apk,
209   };
210   const auto override_files = file::FindFiles(override_files_dir, &diag);
211   for (const auto &override_file : override_files.value()) {
212       link_args.push_back("-R");
213       link_args.push_back(file::BuildPath({override_files_dir, override_file}));
214   }
215   ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
216 
217   std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
218   ASSERT_THAT(apk, Ne(nullptr));
219 
220   const Style* actual_style = test::GetValue<Style>(
221       apk->GetResourceTable(), std::string(kDefaultPackageName) + ":style/MyStyle");
222   ASSERT_NE(actual_style, nullptr);
223   ASSERT_EQ(actual_style->entries.size(), 2);
224   EXPECT_EQ(actual_style->entries[0].key.id, 0x01010098);  // android:textColor
225   EXPECT_EQ(actual_style->entries[1].key.id, 0x010100d4);  // android:background
226 }
227 
TEST_F(LinkTest,OverrideStylesInsteadOfOverlaying)228 TEST_F(LinkTest, OverrideStylesInsteadOfOverlaying) {
229   StdErrDiagnostics diag;
230   const std::string compiled_files_dir = GetTestPath("compiled");
231   const std::string override_files_dir = GetTestPath("compiled-override");
232   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
233                           R"(<resources>
234                                <style name="MyStyle">
235                                  <item name="android:textColor">#123</item>
236                                </style>
237                              </resources>)",
238                           compiled_files_dir, &diag));
239   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values-override.xml"),
240                           R"(<resources>
241                                <style name="MyStyle">
242                                  <item name="android:background">#456</item>
243                                </style>
244                              </resources>)",
245                           override_files_dir, &diag));
246 
247 
248   const std::string out_apk = GetTestPath("out.apk");
249   std::vector<std::string> link_args = {
250       "--manifest", GetDefaultManifest(kDefaultPackageName),
251       "--override-styles-instead-of-overlaying",
252       "-o", out_apk,
253   };
254   const auto override_files = file::FindFiles(override_files_dir, &diag);
255   for (const auto &override_file : override_files.value()) {
256       link_args.push_back("-R");
257       link_args.push_back(file::BuildPath({override_files_dir, override_file}));
258   }
259   ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
260 
261   std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
262   ASSERT_THAT(apk, Ne(nullptr));
263 
264   const Style* actual_style = test::GetValue<Style>(
265       apk->GetResourceTable(), std::string(kDefaultPackageName) + ":style/MyStyle");
266   ASSERT_NE(actual_style, nullptr);
267   ASSERT_EQ(actual_style->entries.size(), 1);
268   EXPECT_EQ(actual_style->entries[0].key.id, 0x010100d4);  // android:background
269 }
270 
TEST_F(LinkTest,AppInfoWithUsesSplit)271 TEST_F(LinkTest, AppInfoWithUsesSplit) {
272   StdErrDiagnostics diag;
273   const std::string base_files_dir = GetTestPath("base");
274   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
275                           R"(<resources>
276                                <string name="bar">bar</string>
277                              </resources>)",
278                           base_files_dir, &diag));
279   const std::string base_apk = GetTestPath("base.apk");
280   std::vector<std::string> link_args = {
281       "--manifest", GetDefaultManifest("com.aapt2.app"),
282       "-o", base_apk,
283   };
284   ASSERT_TRUE(Link(link_args, base_files_dir, &diag));
285 
286   const std::string feature_manifest = GetTestPath("feature_manifest.xml");
287   WriteFile(feature_manifest, android::base::StringPrintf(R"(
288       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
289           package="com.aapt2.app" split="feature1">
290       </manifest>)"));
291   const std::string feature_files_dir = GetTestPath("feature");
292   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
293                           R"(<resources>
294                                <string name="foo">foo</string>
295                              </resources>)",
296                           feature_files_dir, &diag));
297   const std::string feature_apk = GetTestPath("feature.apk");
298   const std::string feature_package_id = "0x80";
299   link_args = {
300       "--manifest", feature_manifest,
301       "-I", base_apk,
302       "--package-id", feature_package_id,
303       "-o", feature_apk,
304   };
305   ASSERT_TRUE(Link(link_args, feature_files_dir, &diag));
306 
307   const std::string feature2_manifest = GetTestPath("feature2_manifest.xml");
308   WriteFile(feature2_manifest, android::base::StringPrintf(R"(
309         <manifest xmlns:android="http://schemas.android.com/apk/res/android"
310             package="com.aapt2.app" split="feature2">
311           <uses-split android:name="feature1"/>
312         </manifest>)"));
313   const std::string feature2_files_dir = GetTestPath("feature2");
314   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
315                           R"(<resources>
316                                <string-array name="string_array">
317                                  <item>@string/bar</item>
318                                  <item>@string/foo</item>
319                                </string-array>
320                              </resources>)",
321                           feature2_files_dir, &diag));
322   const std::string feature2_apk = GetTestPath("feature2.apk");
323   const std::string feature2_package_id = "0x81";
324   link_args = {
325       "--manifest", feature2_manifest,
326       "-I", base_apk,
327       "-I", feature_apk,
328       "--package-id", feature2_package_id,
329       "-o", feature2_apk,
330   };
331   ASSERT_TRUE(Link(link_args, feature2_files_dir, &diag));
332 }
333 
334 TEST_F(LinkTest, SharedLibraryAttributeRJava) {
335   StdErrDiagnostics diag;
336   const std::string lib_values =
337       R"(<resources>
338            <attr name="foo"/>
339            <public type="attr" name="foo" id="0x00010001"/>
340            <declare-styleable name="LibraryStyleable">
341              <attr name="foo" />
342            </declare-styleable>
343          </resources>)";
344 
345   const std::string client_values =
346       R"(<resources>
347            <attr name="bar" />
348            <declare-styleable name="ClientStyleable">
349              <attr name="com.example.lib:foo" />
350              <attr name="bar" />
351            </declare-styleable>
352          </resources>)";
353 
354   // Build a library with a public attribute
355   const std::string lib_res = GetTestPath("library-res");
356   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), lib_values, lib_res, &diag));
357 
358   const std::string lib_apk = GetTestPath("library.apk");
359   const std::string lib_java = GetTestPath("library_java");
360   // clang-format off
361   auto lib_manifest = ManifestBuilder(this)
362       .SetPackageName("com.example.lib")
363       .Build();
364 
365   auto lib_link_args = LinkCommandBuilder(this)
366       .SetManifestFile(lib_manifest)
367       .AddFlag("--shared-lib")
368       .AddParameter("--java", lib_java)
369       .AddCompiledResDir(lib_res, &diag)
370       .Build(lib_apk);
371   // clang-format on
372   ASSERT_TRUE(Link(lib_link_args, &diag));
373 
374   const std::string lib_r_java = lib_java + "/com/example/lib/R.java";
375   std::string lib_r_contents;
376   ASSERT_TRUE(android::base::ReadFileToString(lib_r_java, &lib_r_contents));
377   EXPECT_THAT(lib_r_contents, HasSubstr(" public static int foo=0x00010001;"));
378   EXPECT_THAT(lib_r_contents, HasSubstr(" com.example.lib.R.attr.foo"));
379 
380   // Build a client that uses the library attribute in a declare-styleable
381   const std::string client_res = GetTestPath("client-res");
382   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), client_values, client_res, &diag));
383 
384   const std::string client_apk = GetTestPath("client.apk");
385   const std::string client_java = GetTestPath("client_java");
386   // clang-format off
387   auto client_manifest = ManifestBuilder(this)
388       .SetPackageName("com.example.client")
389       .Build();
390 
391   auto client_link_args = LinkCommandBuilder(this)
392       .SetManifestFile(client_manifest)
393       .AddParameter("--java", client_java)
394       .AddParameter("-I", lib_apk)
395       .AddCompiledResDir(client_res, &diag)
396       .Build(client_apk);
397   // clang-format on
398   ASSERT_TRUE(Link(client_link_args, &diag));
399 
400   const std::string client_r_java = client_java + "/com/example/client/R.java";
401   std::string client_r_contents;
402   ASSERT_TRUE(android::base::ReadFileToString(client_r_java, &client_r_contents));
403   EXPECT_THAT(client_r_contents, HasSubstr(" com.example.lib.R.attr.foo, 0x7f010000"));
404 }
405 
406 struct SourceXML {
407   std::string res_file_path;
408   std::string file_contents;
409 };
410 
411 static void BuildApk(const std::vector<SourceXML>& source_files, const std::string& apk_path,
412                      LinkCommandBuilder&& link_args, CommandTestFixture* fixture,
413                      android::IDiagnostics* diag) {
414   TemporaryDir res_dir;
415   TemporaryDir compiled_res_dir;
416   for (auto& source_file : source_files) {
417     ASSERT_TRUE(fixture->CompileFile(res_dir.path + source_file.res_file_path,
418                                      source_file.file_contents, compiled_res_dir.path, diag));
419   }
420   ASSERT_TRUE(fixture->Link(
421       link_args.AddCompiledResDir(compiled_res_dir.path, diag).Build(apk_path), diag));
422 }
423 
424 static void BuildSDK(const std::vector<SourceXML>& source_files, const std::string& apk_path,
425                      const std::string& java_root_path, CommandTestFixture* fixture,
426                      android::IDiagnostics* diag) {
427   auto android_manifest = ManifestBuilder(fixture).SetPackageName("android").Build();
428 
429   auto android_link_args = LinkCommandBuilder(fixture)
430                                .SetManifestFile(android_manifest)
431                                .AddParameter("--private-symbols", "com.android.internal")
432                                .AddParameter("--java", java_root_path);
433 
434   BuildApk(source_files, apk_path, std::move(android_link_args), fixture, diag);
435 }
436 
437 static void BuildNonFinalizedSDK(const std::string& apk_path, const std::string& java_path,
438                                  CommandTestFixture* fixture, android::IDiagnostics* diag) {
439   const std::string android_values =
440       R"(<resources>
441           <public type="attr" name="finalized_res" id="0x01010001"/>
442 
443           <!-- S staged attributes (Not support staged resources in the same type id) -->
444           <staging-public-group type="attr" first-id="0x01fc0050">
445             <public name="staged_s_res" />
446           </staging-public-group>
447 
448           <staging-public-group type="string" first-id="0x01fd0080">
449             <public name="staged_s_string" />
450           </staging-public-group>
451 
452           <!-- SV2 staged attributes (support staged resources in a separate type id) -->
453           <staging-public-group type="attr" first-id="0x01ff0049">
454             <public name="staged_s2_res" />
455           </staging-public-group>
456 
457           <!-- T staged attributes (support staged resources in multiple separate type ids) -->
458           <staging-public-group type="attr" first-id="0x01fe0063">
459             <public name="staged_t_res" />
460           </staging-public-group>
461 
462           <attr name="finalized_res" />
463           <attr name="staged_s_res" />
464           <attr name="staged_s2_res" />
465           <attr name="staged_t_res" />
466           <string name="staged_s_string">Hello</string>
467          </resources>)";
468 
469   SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values};
470   BuildSDK({source_xml}, apk_path, java_path, fixture, diag);
471 }
472 
473 static void BuildFinalizedSDK(const std::string& apk_path, const std::string& java_path,
474                               CommandTestFixture* fixture, android::IDiagnostics* diag) {
475   const std::string android_values =
476       R"(<resources>
477           <public type="attr" name="finalized_res" id="0x01010001"/>
478           <public type="attr" name="staged_s_res" id="0x01010002"/>
479           <public type="attr" name="staged_s2_res" id="0x01010003"/>
480           <public type="string" name="staged_s_string" id="0x01020000"/>
481 
482           <!-- S staged attributes (Not support staged resources in the same type id) -->
483           <staging-public-group-final type="attr" first-id="0x01fc0050">
484             <public name="staged_s_res" />
485           </staging-public-group-final>
486 
487           <staging-public-group-final type="string" first-id="0x01fd0080">
488             <public name="staged_s_string" />
489           </staging-public-group-final>
490 
491           <!-- SV2 staged attributes (support staged resources in a separate type id) -->
492           <staging-public-group-final type="attr" first-id="0x01ff0049">
493             <public name="staged_s2_res" />
494           </staging-public-group-final>
495 
496           <!-- T staged attributes (support staged resources in multiple separate type ids) -->
497           <staging-public-group type="attr" first-id="0x01fe0063">
498             <public name="staged_t_res" />
499           </staging-public-group>
500 
501           <attr name="finalized_res" />
502           <attr name="staged_s_res" />
503           <attr name="staged_s2_res" />
504           <attr name="staged_t_res" />
505           <string name="staged_s_string">Hello</string>
506          </resources>)";
507 
508   SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values};
509   BuildSDK({source_xml}, apk_path, java_path, fixture, diag);
510 }
511 
512 static void BuildAppAgainstSDK(const std::string& apk_path, const std::string& java_path,
513                                const std::string& sdk_path, CommandTestFixture* fixture,
514                                android::IDiagnostics* diag) {
515   const std::string app_values =
516       R"(<resources xmlns:android="http://schemas.android.com/apk/res/android">
517            <attr name="bar" />
518            <style name="MyStyle">
519              <item name="android:staged_s_res">@android:string/staged_s_string</item>
520            </style>
521            <declare-styleable name="ClientStyleable">
522              <attr name="android:finalized_res" />
523              <attr name="android:staged_s_res" />
524              <attr name="bar" />
525            </declare-styleable>
526            <public name="MyStyle" type="style" id="0x7f020000" />
527          </resources>)";
528 
529   SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = app_values};
530 
531   auto app_manifest = ManifestBuilder(fixture).SetPackageName("com.example.app").Build();
532 
533   auto app_link_args = LinkCommandBuilder(fixture)
534                            .SetManifestFile(app_manifest)
535                            .AddParameter("--java", java_path)
536                            .AddParameter("-I", sdk_path);
537 
538   BuildApk({source_xml}, apk_path, std::move(app_link_args), fixture, diag);
539 }
540 
541 TEST_F(LinkTest, StagedAndroidApi) {
542   StdErrDiagnostics diag;
543   const std::string android_apk = GetTestPath("android.apk");
544   const std::string android_java = GetTestPath("android-java");
545   BuildNonFinalizedSDK(android_apk, android_java, this, &diag);
546 
547   const std::string android_r_java = android_java + "/android/R.java";
548   std::string android_r_contents;
549   ASSERT_TRUE(android::base::ReadFileToString(android_r_java, &android_r_contents));
550   EXPECT_THAT(android_r_contents, HasSubstr("public static final int finalized_res=0x01010001;"));
551   EXPECT_THAT(
552       android_r_contents,
553       HasSubstr("public static final int staged_s_res; static { staged_s_res=0x01fc0050; }"));
554   EXPECT_THAT(
555       android_r_contents,
556       HasSubstr("public static final int staged_s_string; static { staged_s_string=0x01fd0080; }"));
557   EXPECT_THAT(
558       android_r_contents,
559       HasSubstr("public static final int staged_s2_res; static { staged_s2_res=0x01ff0049; }"));
560   EXPECT_THAT(
561       android_r_contents,
562       HasSubstr("public static final int staged_t_res; static { staged_t_res=0x01fe0063; }"));
563 
564   const std::string app_apk = GetTestPath("app.apk");
565   const std::string app_java = GetTestPath("app-java");
566   BuildAppAgainstSDK(app_apk, app_java, android_apk, this, &diag);
567 
568   const std::string client_r_java = app_java + "/com/example/app/R.java";
569   std::string client_r_contents;
570   ASSERT_TRUE(android::base::ReadFileToString(client_r_java, &client_r_contents));
571   EXPECT_THAT(client_r_contents, HasSubstr(" 0x01010001, android.R.attr.staged_s_res, 0x7f010000"));
572 
573   // Test that the resource ids of staged and non-staged resource can be retrieved
574   android::AssetManager2 am;
575   auto android_asset = android::ApkAssets::Load(android_apk);
576   ASSERT_THAT(android_asset, NotNull());
577   ASSERT_TRUE(am.SetApkAssets({android_asset}));
578 
579   auto result = am.GetResourceId("android:attr/finalized_res");
580   ASSERT_TRUE(result.has_value());
581   EXPECT_THAT(*result, Eq(0x01010001));
582 
583   result = am.GetResourceId("android:attr/staged_s_res");
584   ASSERT_TRUE(result.has_value());
585   EXPECT_THAT(*result, Eq(0x01fc0050));
586 
587   result = am.GetResourceId("android:string/staged_s_string");
588   ASSERT_TRUE(result.has_value());
589   EXPECT_THAT(*result, Eq(0x01fd0080));
590 
591   result = am.GetResourceId("android:attr/staged_s2_res");
592   ASSERT_TRUE(result.has_value());
593   EXPECT_THAT(*result, Eq(0x01ff0049));
594 
595   result = am.GetResourceId("android:attr/staged_t_res");
596   ASSERT_TRUE(result.has_value());
597   EXPECT_THAT(*result, Eq(0x01fe0063));
598 }
599 
600 TEST_F(LinkTest, FinalizedAndroidApi) {
601   StdErrDiagnostics diag;
602   const std::string android_apk = GetTestPath("android.apk");
603   const std::string android_java = GetTestPath("android-java");
604   BuildFinalizedSDK(android_apk, android_java, this, &diag);
605 
606   const std::string android_r_java = android_java + "/android/R.java";
607   std::string android_r_contents;
608   ASSERT_TRUE(android::base::ReadFileToString(android_r_java, &android_r_contents));
609   EXPECT_THAT(android_r_contents, HasSubstr("public static final int finalized_res=0x01010001;"));
610   EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s_res=0x01010002;"));
611   EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s_string=0x01020000;"));
612   EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s2_res=0x01010003;"));
613   EXPECT_THAT(
614       android_r_contents,
615       HasSubstr("public static final int staged_t_res; static { staged_t_res=0x01fe0063; }"));
616   ;
617 
618   // Build an application against the non-finalized SDK and then load it into an AssetManager with
619   // the finalized SDK.
620   const std::string non_finalized_android_apk = GetTestPath("non-finalized-android.apk");
621   const std::string non_finalized_android_java = GetTestPath("non-finalized-android-java");
622   BuildNonFinalizedSDK(non_finalized_android_apk, non_finalized_android_java, this, &diag);
623 
624   const std::string app_apk = GetTestPath("app.apk");
625   const std::string app_java = GetTestPath("app-java");
626   BuildAppAgainstSDK(app_apk, app_java, non_finalized_android_apk, this, &diag);
627 
628   android::AssetManager2 am;
629   auto android_asset = android::ApkAssets::Load(android_apk);
630   auto app_against_non_final = android::ApkAssets::Load(app_apk);
631   ASSERT_THAT(android_asset, NotNull());
632   ASSERT_THAT(app_against_non_final, NotNull());
633   ASSERT_TRUE(am.SetApkAssets({android_asset, app_against_non_final}));
634 
635   auto result = am.GetResourceId("android:attr/finalized_res");
636   ASSERT_TRUE(result.has_value());
637   EXPECT_THAT(*result, Eq(0x01010001));
638 
639   result = am.GetResourceId("android:attr/staged_s_res");
640   ASSERT_TRUE(result.has_value());
641   EXPECT_THAT(*result, Eq(0x01010002));
642 
643   result = am.GetResourceId("android:string/staged_s_string");
644   ASSERT_TRUE(result.has_value());
645   EXPECT_THAT(*result, Eq(0x01020000));
646 
647   result = am.GetResourceId("android:attr/staged_s2_res");
648   ASSERT_TRUE(result.has_value());
649   EXPECT_THAT(*result, Eq(0x01010003));
650 
651   {
652     auto style = am.GetBag(0x7f020000);
653     ASSERT_TRUE(style.has_value());
654 
655     auto& entry = (*style)->entries[0];
656     EXPECT_THAT(entry.key, Eq(0x01010002));
657     EXPECT_THAT(entry.value.dataType, Eq(android::Res_value::TYPE_REFERENCE));
658     EXPECT_THAT(entry.value.data, Eq(0x01020000));
659   }
660 
661   // Re-compile the application against the finalized SDK and then load it into an AssetManager with
662   // the finalized SDK.
663   const std::string app_apk_respin = GetTestPath("app-respin.apk");
664   const std::string app_java_respin = GetTestPath("app-respin-java");
665   BuildAppAgainstSDK(app_apk_respin, app_java_respin, android_apk, this, &diag);
666 
667   auto app_against_final = android::ApkAssets::Load(app_apk_respin);
668   ASSERT_THAT(app_against_final, NotNull());
669   ASSERT_TRUE(am.SetApkAssets({android_asset, app_against_final}));
670 
671   {
672     auto style = am.GetBag(0x7f020000);
673     ASSERT_TRUE(style.has_value());
674 
675     auto& entry = (*style)->entries[0];
676     EXPECT_THAT(entry.key, Eq(0x01010002));
677     EXPECT_THAT(entry.value.dataType, Eq(android::Res_value::TYPE_REFERENCE));
678     EXPECT_THAT(entry.value.data, Eq(0x01020000));
679   }
680 }
681 
682 TEST_F(LinkTest, MacroSubstitution) {
683   StdErrDiagnostics diag;
684   const std::string values =
685       R"(<resources xmlns:an="http://schemas.android.com/apk/res/android">
686            <macro name="is_enabled">true</macro>
687            <macro name="deep_is_enabled">@macro/is_enabled</macro>
688            <macro name="attr_ref">?is_enabled_attr</macro>
689            <macro name="raw_string">Hello World!</macro>
690            <macro name="android_ref">@an:color/primary_text_dark</macro>
691 
692            <attr name="is_enabled_attr" />
693            <public type="attr" name="is_enabled_attr" id="0x7f010000"/>
694 
695            <string name="is_enabled_str">@macro/is_enabled</string>
696            <bool name="is_enabled_bool">@macro/deep_is_enabled</bool>
697 
698            <array name="my_array">
699              <item>@macro/is_enabled</item>
700            </array>
701 
702            <style name="MyStyle">
703               <item name="android:background">@macro/attr_ref</item>
704               <item name="android:fontFamily">@macro/raw_string</item>
705            </style>
706          </resources>)";
707 
708   const std::string xml_values =
709       R"(<SomeLayout xmlns:android="http://schemas.android.com/apk/res/android"
710                      android:background="@macro/android_ref"
711                      android:fontFamily="@macro/raw_string">
712          </SomeLayout>)";
713 
714   // Build a library with a public attribute
715   const std::string lib_res = GetTestPath("test-res");
716   ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), values, lib_res, &diag));
717   ASSERT_TRUE(CompileFile(GetTestPath("res/layout/layout.xml"), xml_values, lib_res, &diag));
718 
719   const std::string lib_apk = GetTestPath("test.apk");
720   // clang-format off
721   auto lib_link_args = LinkCommandBuilder(this)
722       .SetManifestFile(ManifestBuilder(this).SetPackageName("com.test").Build())
723       .AddCompiledResDir(lib_res, &diag)
724       .AddFlag("--no-auto-version")
725       .Build(lib_apk);
726   // clang-format on
727   ASSERT_TRUE(Link(lib_link_args, &diag));
728 
729   auto apk = LoadedApk::LoadApkFromPath(lib_apk, &diag);
730   ASSERT_THAT(apk, NotNull());
731 
732   // Test that the type flags determines the value type
733   auto actual_bool =
734       test::GetValue<BinaryPrimitive>(apk->GetResourceTable(), "com.test:bool/is_enabled_bool");
735   ASSERT_THAT(actual_bool, NotNull());
736   EXPECT_EQ(android::Res_value::TYPE_INT_BOOLEAN, actual_bool->value.dataType);
737   EXPECT_EQ(0xffffffffu, actual_bool->value.data);
738 
739   auto actual_str =
740       test::GetValue<String>(apk->GetResourceTable(), "com.test:string/is_enabled_str");
741   ASSERT_THAT(actual_str, NotNull());
742   EXPECT_EQ(*actual_str->value, "true");
743 
744   // Test nested data structures
745   auto actual_array = test::GetValue<Array>(apk->GetResourceTable(), "com.test:array/my_array");
746   ASSERT_THAT(actual_array, NotNull());
747   EXPECT_THAT(actual_array->elements.size(), Eq(1));
748 
749   auto array_el_ref = ValueCast<BinaryPrimitive>(actual_array->elements[0].get());
750   ASSERT_THAT(array_el_ref, NotNull());
751   EXPECT_THAT(array_el_ref->value.dataType, Eq(android::Res_value::TYPE_INT_BOOLEAN));
752   EXPECT_THAT(array_el_ref->value.data, Eq(0xffffffffu));
753 
754   auto actual_style = test::GetValue<Style>(apk->GetResourceTable(), "com.test:style/MyStyle");
755   ASSERT_THAT(actual_style, NotNull());
756   EXPECT_THAT(actual_style->entries.size(), Eq(2));
757 
758   {
759     auto style_el = ValueCast<Reference>(actual_style->entries[0].value.get());
760     ASSERT_THAT(style_el, NotNull());
761     EXPECT_THAT(style_el->reference_type, Eq(Reference::Type::kAttribute));
762     EXPECT_THAT(style_el->id, Eq(0x7f010000));
763   }
764 
765   {
766     auto style_el = ValueCast<String>(actual_style->entries[1].value.get());
767     ASSERT_THAT(style_el, NotNull());
768     EXPECT_THAT(*style_el->value, Eq("Hello World!"));
769   }
770 
771   // Test substitution in compiled xml files
772   auto xml = apk->LoadXml("res/layout/layout.xml", &diag);
773   ASSERT_THAT(xml, NotNull());
774 
775   auto& xml_attrs = xml->root->attributes;
776   ASSERT_THAT(xml_attrs.size(), Eq(2));
777 
778   auto attr_value = ValueCast<Reference>(xml_attrs[0].compiled_value.get());
779   ASSERT_THAT(attr_value, NotNull());
780   EXPECT_THAT(attr_value->reference_type, Eq(Reference::Type::kResource));
781   EXPECT_THAT(attr_value->id, Eq(0x01060001));
782 
783   EXPECT_THAT(xml_attrs[1].compiled_value.get(), IsNull());
784   EXPECT_THAT(xml_attrs[1].value, Eq("Hello World!"));
785 }
786 
787 TEST_F(LinkTest, LocaleConfigVerification) {
788   StdErrDiagnostics diag;
789   const std::string compiled_files_dir = GetTestPath("compiled");
790 
791   // Normal case
792   ASSERT_TRUE(CompileFile(GetTestPath("res/xml/locales_config.xml"), R"(
793     <locale-config xmlns:android="http://schemas.android.com/apk/res/android">
794       <locale android:name="en-US"/>
795       <locale android:name="pt"/>
796       <locale android:name="es-419"/>
797       <locale android:name="zh-Hans-SG"/>
798     </locale-config>)",
799                           compiled_files_dir, &diag));
800 
801   const std::string localeconfig_manifest = GetTestPath("localeconfig_manifest.xml");
802   WriteFile(localeconfig_manifest, android::base::StringPrintf(R"(
803     <manifest xmlns:android="http://schemas.android.com/apk/res/android"
804       package="com.aapt2.app">
805 
806       <application
807         android:localeConfig="@xml/locales_config">
808       </application>
809     </manifest>)"));
810 
811   const std::string out_apk = GetTestPath("out.apk");
812 
813   auto link_args = LinkCommandBuilder(this)
814                        .SetManifestFile(localeconfig_manifest)
815                        .AddCompiledResDir(compiled_files_dir, &diag)
816                        .Build(out_apk);
817   ASSERT_TRUE(Link(link_args, &diag));
818 
819   // Empty locale list
820   ASSERT_TRUE(CompileFile(GetTestPath("res/xml/empty_locales_config.xml"), R"(
821     <locale-config xmlns:android="http://schemas.android.com/apk/res/android">
822     </locale-config>)",
823                           compiled_files_dir, &diag));
824 
825   const std::string empty_localeconfig_manifest = GetTestPath("empty_localeconfig_manifest.xml");
826   WriteFile(empty_localeconfig_manifest, android::base::StringPrintf(R"(
827     <manifest xmlns:android="http://schemas.android.com/apk/res/android"
828       package="com.aapt2.app">
829 
830       <application
831         android:localeConfig="@xml/empty_locales_config">
832       </application>
833     </manifest>)"));
834 
835   auto link1_args = LinkCommandBuilder(this)
836                         .SetManifestFile(empty_localeconfig_manifest)
837                         .AddCompiledResDir(compiled_files_dir, &diag)
838                         .Build(out_apk);
839   ASSERT_TRUE(Link(link1_args, &diag));
840 }
841 
842 TEST_F(LinkTest, LocaleConfigVerificationExternalSymbol) {
843   StdErrDiagnostics diag;
844   const std::string base_files_dir = GetTestPath("base");
845   ASSERT_TRUE(CompileFile(GetTestPath("res/xml/locales_config.xml"), R"(
846     <locale-config xmlns:android="http://schemas.android.com/apk/res/android">
847       <locale android:name="en-US"/>
848       <locale android:name="pt"/>
849       <locale android:name="es-419"/>
850       <locale android:name="zh-Hans-SG"/>
851     </locale-config>)",
852                           base_files_dir, &diag));
853   const std::string base_apk = GetTestPath("base.apk");
854   std::vector<std::string> link_args = {
855       "--manifest",
856       GetDefaultManifest("com.aapt2.app"),
857       "-o",
858       base_apk,
859   };
860   ASSERT_TRUE(Link(link_args, base_files_dir, &diag));
861 
862   const std::string localeconfig_manifest = GetTestPath("localeconfig_manifest.xml");
863   const std::string out_apk = GetTestPath("out.apk");
864   WriteFile(localeconfig_manifest, android::base::StringPrintf(R"(
865     <manifest xmlns:android="http://schemas.android.com/apk/res/android"
866       package="com.aapt2.app">
867 
868       <application
869         android:localeConfig="@xml/locales_config">
870       </application>
871     </manifest>)"));
872   link_args = LinkCommandBuilder(this)
873                   .SetManifestFile(localeconfig_manifest)
874                   .AddParameter("-I", base_apk)
875                   .Build(out_apk);
876   ASSERT_TRUE(Link(link_args, &diag));
877 }
878 
879 TEST_F(LinkTest, LocaleConfigWrongTag) {
880   StdErrDiagnostics diag;
881   const std::string compiled_files_dir = GetTestPath("compiled");
882 
883   // Invalid element: locale1-config
884   ASSERT_TRUE(CompileFile(GetTestPath("res/xml/wrong_locale_config.xml"), R"(
885     <locale1-config xmlns:android="http://schemas.android.com/apk/res/android">
886       <locale android:name="en-US"/>
887       <locale android:name="pt"/>
888       <locale android:name="es-419"/>
889       <locale android:name="zh-Hans-SG"/>
890     </locale1-config>)",
891                           compiled_files_dir, &diag));
892 
893   const std::string locale1config_manifest = GetTestPath("locale1config_manifest.xml");
894   WriteFile(locale1config_manifest, android::base::StringPrintf(R"(
895     <manifest xmlns:android="http://schemas.android.com/apk/res/android"
896       package="com.aapt2.app">
897 
898       <application
899         android:localeConfig="@xml/wrong_locale_config">
900       </application>
901     </manifest>)"));
902 
903   const std::string out_apk = GetTestPath("out.apk");
904   auto link_args = LinkCommandBuilder(this)
905                        .SetManifestFile(locale1config_manifest)
906                        .AddCompiledResDir(compiled_files_dir, &diag)
907                        .Build(out_apk);
908   ASSERT_FALSE(Link(link_args, &diag));
909 
910   // Invalid element: locale1
911   ASSERT_TRUE(CompileFile(GetTestPath("res/xml/wrong_locale.xml"), R"(
912     <locale-config xmlns:android="http://schemas.android.com/apk/res/android">
913       <locale1 android:name="en-US"/>
914       <locale android:name="pt"/>
915       <locale android:name="es-419"/>
916       <locale android:name="zh-Hans-SG"/>
917     </locale-config>)",
918                           compiled_files_dir, &diag));
919 
920   const std::string locale1_manifest = GetTestPath("locale1_manifest.xml");
921   WriteFile(locale1_manifest, android::base::StringPrintf(R"(
922     <manifest xmlns:android="http://schemas.android.com/apk/res/android"
923       package="com.aapt2.app">
924 
925       <application
926         android:localeConfig="@xml/wrong_locale">
927       </application>
928     </manifest>)"));
929 
930   auto link1_args = LinkCommandBuilder(this)
931                         .SetManifestFile(locale1_manifest)
932                         .AddCompiledResDir(compiled_files_dir, &diag)
933                         .Build(out_apk);
934   ASSERT_FALSE(Link(link1_args, &diag));
935 
936   // Invalid attribute: android:name1
937   ASSERT_TRUE(CompileFile(GetTestPath("res/xml/wrong_attribute.xml"), R"(
938     <locale-config xmlns:android="http://schemas.android.com/apk/res/android">
939       <locale android:name1="en-US"/>
940       <locale android:name="pt"/>
941       <locale android:name="es-419"/>
942       <locale android:name="zh-Hans-SG"/>
943     </locale-config>)",
944                           compiled_files_dir, &diag));
945 
946   const std::string wrong_attribute_manifest = GetTestPath("wrong_attribute_manifest.xml");
947   WriteFile(wrong_attribute_manifest, android::base::StringPrintf(R"(
948     <manifest xmlns:android="http://schemas.android.com/apk/res/android"
949       package="com.aapt2.app">
950 
951       <application
952         android:localeConfig="@xml/wrong_attribute">
953       </application>
954     </manifest>)"));
955 
956   auto link2_args = LinkCommandBuilder(this)
957                         .SetManifestFile(wrong_attribute_manifest)
958                         .AddCompiledResDir(compiled_files_dir, &diag)
959                         .Build(out_apk);
960   ASSERT_FALSE(Link(link2_args, &diag));
961 }
962 
963 TEST_F(LinkTest, LocaleConfigWrongLocaleFormat) {
964   StdErrDiagnostics diag;
965   const std::string compiled_files_dir = GetTestPath("compiled");
966 
967   // Invalid locale: en-U
968   ASSERT_TRUE(CompileFile(GetTestPath("res/xml/wrong_locale.xml"), R"(
969     <locale-config xmlns:android="http://schemas.android.com/apk/res/android">
970       <locale android:name="en-U"/>
971       <locale android:name="pt"/>
972       <locale android:name="es-419"/>
973       <locale android:name="zh-Hans-SG"/>
974     </locale-config>)",
975                           compiled_files_dir, &diag));
976 
977   const std::string wrong_locale_manifest = GetTestPath("wrong_locale_manifest.xml");
978   WriteFile(wrong_locale_manifest, android::base::StringPrintf(R"(
979     <manifest xmlns:android="http://schemas.android.com/apk/res/android"
980       package="com.aapt2.app">
981 
982       <application
983         android:localeConfig="@xml/wrong_locale">
984       </application>
985     </manifest>)"));
986 
987   const std::string out_apk = GetTestPath("out.apk");
988   auto link_args = LinkCommandBuilder(this)
989                        .SetManifestFile(wrong_locale_manifest)
990                        .AddCompiledResDir(compiled_files_dir, &diag)
991                        .Build(out_apk);
992   ASSERT_FALSE(Link(link_args, &diag));
993 }
994 
995 static void BuildSDKWithFeatureFlagAttr(const std::string& apk_path, const std::string& java_path,
996                                         CommandTestFixture* fixture, android::IDiagnostics* diag) {
997   const std::string android_values =
998       R"(<resources>
999           <staging-public-group type="attr" first-id="0x01fe0063">
1000             <public name="featureFlag" />
1001           </staging-public-group>
1002           <attr name="featureFlag" format="string" />
1003          </resources>)";
1004 
1005   SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values};
1006   BuildSDK({source_xml}, apk_path, java_path, fixture, diag);
1007 }
1008 
1009 TEST_F(LinkTest, FeatureFlagDisabled_SdkAtMostUDC) {
1010   StdErrDiagnostics diag;
1011   const std::string android_apk = GetTestPath("android.apk");
1012   const std::string android_java = GetTestPath("android-java");
1013   BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag);
1014 
1015   const std::string manifest_contents = android::base::StringPrintf(
1016       R"(<uses-sdk android:minSdkVersion="%d" />"
1017           <permission android:name="FOO" android:featureFlag="flag" />)",
1018       SDK_UPSIDE_DOWN_CAKE);
1019   auto app_manifest = ManifestBuilder(this)
1020                           .SetPackageName("com.example.app")
1021                           .AddContents(manifest_contents)
1022                           .Build();
1023 
1024   const std::string app_java = GetTestPath("app-java");
1025   auto app_link_args = LinkCommandBuilder(this)
1026                            .SetManifestFile(app_manifest)
1027                            .AddParameter("-I", android_apk)
1028                            .AddParameter("--java", app_java)
1029                            .AddParameter("--feature-flags", "flag=false");
1030 
1031   const std::string app_apk = GetTestPath("app.apk");
1032   BuildApk({}, app_apk, std::move(app_link_args), this, &diag);
1033 
1034   // Permission element should be removed if flag is disabled
1035   auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag);
1036   ASSERT_THAT(apk, NotNull());
1037   auto apk_manifest = apk->GetManifest();
1038   ASSERT_THAT(apk_manifest, NotNull());
1039   auto root = apk_manifest->root.get();
1040   ASSERT_THAT(root, NotNull());
1041   auto maybe_removed = root->FindChild({}, "permission");
1042   ASSERT_THAT(maybe_removed, IsNull());
1043 
1044   // Code for the permission should be generated even if the element is removed
1045   const std::string manifest_java = app_java + "/com/example/app/Manifest.java";
1046   std::string manifest_java_contents;
1047   ASSERT_TRUE(android::base::ReadFileToString(manifest_java, &manifest_java_contents));
1048   EXPECT_THAT(manifest_java_contents, HasSubstr(" public static final String FOO=\"FOO\";"));
1049 }
1050 
TEST_F(LinkTest,FeatureFlagEnabled_SdkAtMostUDC)1051 TEST_F(LinkTest, FeatureFlagEnabled_SdkAtMostUDC) {
1052   StdErrDiagnostics diag;
1053   const std::string android_apk = GetTestPath("android.apk");
1054   const std::string android_java = GetTestPath("android-java");
1055   BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag);
1056 
1057   const std::string manifest_contents = android::base::StringPrintf(
1058       R"(<uses-sdk android:minSdkVersion="%d" />"
1059           <permission android:name="FOO" android:featureFlag="flag" />)",
1060       SDK_UPSIDE_DOWN_CAKE);
1061   auto app_manifest = ManifestBuilder(this)
1062                           .SetPackageName("com.example.app")
1063                           .AddContents(manifest_contents)
1064                           .Build();
1065 
1066   auto app_link_args = LinkCommandBuilder(this)
1067                            .SetManifestFile(app_manifest)
1068                            .AddParameter("-I", android_apk)
1069                            .AddParameter("--feature-flags", "flag=true");
1070 
1071   const std::string app_apk = GetTestPath("app.apk");
1072   BuildApk({}, app_apk, std::move(app_link_args), this, &diag);
1073 
1074   // Permission element should be kept if flag is enabled
1075   auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag);
1076   ASSERT_THAT(apk, NotNull());
1077   auto apk_manifest = apk->GetManifest();
1078   ASSERT_THAT(apk_manifest, NotNull());
1079   auto root = apk_manifest->root.get();
1080   ASSERT_THAT(root, NotNull());
1081   auto maybe_removed = root->FindChild({}, "permission");
1082   ASSERT_THAT(maybe_removed, NotNull());
1083 }
1084 
1085 TEST_F(LinkTest, FeatureFlagWithNoValue_SdkAtMostUDC) {
1086   StdErrDiagnostics diag;
1087   const std::string android_apk = GetTestPath("android.apk");
1088   const std::string android_java = GetTestPath("android-java");
1089   BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag);
1090 
1091   const std::string manifest_contents = android::base::StringPrintf(
1092       R"(<uses-sdk android:minSdkVersion="%d" />"
1093           <permission android:name="FOO" android:featureFlag="flag" />)",
1094       SDK_UPSIDE_DOWN_CAKE);
1095   auto app_manifest = ManifestBuilder(this)
1096                           .SetPackageName("com.example.app")
1097                           .AddContents(manifest_contents)
1098                           .Build();
1099 
1100   auto app_link_args = LinkCommandBuilder(this)
1101                            .SetManifestFile(app_manifest)
1102                            .AddParameter("-I", android_apk)
1103                            .AddParameter("--feature-flags", "flag=");
1104 
1105   // Flags must have values if <= UDC
1106   const std::string app_apk = GetTestPath("app.apk");
1107   ASSERT_FALSE(Link(app_link_args.Build(app_apk), &diag));
1108 }
1109 
TEST_F(LinkTest,FeatureFlagDisabled_SdkAfterUDC)1110 TEST_F(LinkTest, FeatureFlagDisabled_SdkAfterUDC) {
1111   StdErrDiagnostics diag;
1112   const std::string android_apk = GetTestPath("android.apk");
1113   const std::string android_java = GetTestPath("android-java");
1114   BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag);
1115 
1116   const std::string manifest_contents = android::base::StringPrintf(
1117       R"(<uses-sdk android:minSdkVersion="%d" />"
1118           <permission android:name="FOO" android:featureFlag="flag" />)",
1119       SDK_CUR_DEVELOPMENT);
1120   auto app_manifest = ManifestBuilder(this)
1121                           .SetPackageName("com.example.app")
1122                           .AddContents(manifest_contents)
1123                           .Build();
1124 
1125   auto app_link_args = LinkCommandBuilder(this)
1126                            .SetManifestFile(app_manifest)
1127                            .AddParameter("-I", android_apk)
1128                            .AddParameter("--feature-flags", "flag=false");
1129 
1130   const std::string app_apk = GetTestPath("app.apk");
1131   BuildApk({}, app_apk, std::move(app_link_args), this, &diag);
1132 
1133   // Permission element should be kept if > UDC, regardless of flag value
1134   auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag);
1135   ASSERT_THAT(apk, NotNull());
1136   auto apk_manifest = apk->GetManifest();
1137   ASSERT_THAT(apk_manifest, NotNull());
1138   auto root = apk_manifest->root.get();
1139   ASSERT_THAT(root, NotNull());
1140   auto maybe_removed = root->FindChild({}, "permission");
1141   ASSERT_THAT(maybe_removed, NotNull());
1142 }
1143 
1144 TEST_F(LinkTest, FeatureFlagEnabled_SdkAfterUDC) {
1145   StdErrDiagnostics diag;
1146   const std::string android_apk = GetTestPath("android.apk");
1147   const std::string android_java = GetTestPath("android-java");
1148   BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag);
1149 
1150   const std::string manifest_contents = android::base::StringPrintf(
1151       R"(<uses-sdk android:minSdkVersion="%d" />"
1152           <permission android:name="FOO" android:featureFlag="flag" />)",
1153       SDK_CUR_DEVELOPMENT);
1154   auto app_manifest = ManifestBuilder(this)
1155                           .SetPackageName("com.example.app")
1156                           .AddContents(manifest_contents)
1157                           .Build();
1158 
1159   auto app_link_args = LinkCommandBuilder(this)
1160                            .SetManifestFile(app_manifest)
1161                            .AddParameter("-I", android_apk)
1162                            .AddParameter("--feature-flags", "flag=true");
1163 
1164   const std::string app_apk = GetTestPath("app.apk");
1165   BuildApk({}, app_apk, std::move(app_link_args), this, &diag);
1166 
1167   // Permission element should be kept if > UDC, regardless of flag value
1168   auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag);
1169   ASSERT_THAT(apk, NotNull());
1170   auto apk_manifest = apk->GetManifest();
1171   ASSERT_THAT(apk_manifest, NotNull());
1172   auto root = apk_manifest->root.get();
1173   ASSERT_THAT(root, NotNull());
1174   auto maybe_removed = root->FindChild({}, "permission");
1175   ASSERT_THAT(maybe_removed, NotNull());
1176 }
1177 
TEST_F(LinkTest,FeatureFlagWithNoValue_SdkAfterUDC)1178 TEST_F(LinkTest, FeatureFlagWithNoValue_SdkAfterUDC) {
1179   StdErrDiagnostics diag;
1180   const std::string android_apk = GetTestPath("android.apk");
1181   const std::string android_java = GetTestPath("android-java");
1182   BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag);
1183 
1184   const std::string manifest_contents = android::base::StringPrintf(
1185       R"(<uses-sdk android:minSdkVersion="%d" />"
1186           <permission android:name="FOO" android:featureFlag="flag" />)",
1187       SDK_CUR_DEVELOPMENT);
1188   auto app_manifest = ManifestBuilder(this)
1189                           .SetPackageName("com.example.app")
1190                           .AddContents(manifest_contents)
1191                           .Build();
1192 
1193   auto app_link_args = LinkCommandBuilder(this)
1194                            .SetManifestFile(app_manifest)
1195                            .AddParameter("-I", android_apk)
1196                            .AddParameter("--feature-flags", "flag=");
1197 
1198   const std::string app_apk = GetTestPath("app.apk");
1199   BuildApk({}, app_apk, std::move(app_link_args), this, &diag);
1200 
1201   // Permission element should be kept if > UDC, regardless of flag value
1202   auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag);
1203   ASSERT_THAT(apk, NotNull());
1204   auto apk_manifest = apk->GetManifest();
1205   ASSERT_THAT(apk_manifest, NotNull());
1206   auto root = apk_manifest->root.get();
1207   ASSERT_THAT(root, NotNull());
1208   auto maybe_removed = root->FindChild({}, "permission");
1209   ASSERT_THAT(maybe_removed, NotNull());
1210 }
1211 
1212 }  // namespace aapt
1213