• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 "format/binary/TableFlattener.h"
18 #include <string>
19 
20 #include "android-base/stringprintf.h"
21 #include "androidfw/TypeWrappers.h"
22 
23 #include "ResChunkPullParser.h"
24 #include "ResourceUtils.h"
25 #include "SdkConstants.h"
26 #include "format/binary/BinaryResourceParser.h"
27 #include "test/Test.h"
28 #include "util/Util.h"
29 
30 using namespace android;
31 
32 using ::testing::Gt;
33 using ::testing::IsNull;
34 using ::testing::NotNull;
35 
36 using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
37 
38 namespace aapt {
39 
40 class TableFlattenerTest : public ::testing::Test {
41  public:
SetUp()42   void SetUp() override {
43     context_ =
44         test::ContextBuilder().SetCompilationPackage("com.app.test").SetPackageId(0x7f).Build();
45   }
46 
Flatten(IAaptContext * context,const TableFlattenerOptions & options,ResourceTable * table,std::string * out_content)47   ::testing::AssertionResult Flatten(IAaptContext* context, const TableFlattenerOptions& options,
48                                      ResourceTable* table, std::string* out_content) {
49     android::BigBuffer buffer(1024);
50     TableFlattener flattener(options, &buffer);
51     if (!flattener.Consume(context, table)) {
52       return ::testing::AssertionFailure() << "failed to flatten ResourceTable";
53     }
54     *out_content = buffer.to_string();
55     return ::testing::AssertionSuccess();
56   }
57 
Flatten(IAaptContext * context,const TableFlattenerOptions & options,ResourceTable * table,ResTable * out_table)58   ::testing::AssertionResult Flatten(IAaptContext* context, const TableFlattenerOptions& options,
59                                      ResourceTable* table, ResTable* out_table) {
60     std::string content;
61     auto result = Flatten(context, options, table, &content);
62     if (!result) {
63       return result;
64     }
65 
66     if (out_table->add(content.data(), content.size(), 1, true) != NO_ERROR) {
67       return ::testing::AssertionFailure() << "flattened ResTable is corrupt";
68     }
69     return ::testing::AssertionSuccess();
70   }
71 
Flatten(IAaptContext * context,const TableFlattenerOptions & options,ResourceTable * table,ResourceTable * out_table)72   ::testing::AssertionResult Flatten(IAaptContext* context, const TableFlattenerOptions& options,
73                                      ResourceTable* table, ResourceTable* out_table) {
74     std::string content;
75     auto result = Flatten(context, options, table, &content);
76     if (!result) {
77       return result;
78     }
79 
80     BinaryResourceParser parser(context->GetDiagnostics(), out_table, {}, content.data(),
81                                 content.size());
82     if (!parser.Parse()) {
83       return ::testing::AssertionFailure() << "flattened ResTable is corrupt";
84     }
85     return ::testing::AssertionSuccess();
86   }
87 
Exists(ResTable * table,StringPiece expected_name,const ResourceId & expected_id,const ConfigDescription & expected_config,const uint8_t expected_data_type,const uint32_t expected_data,const uint32_t expected_spec_flags)88   ::testing::AssertionResult Exists(ResTable* table, StringPiece expected_name,
89                                     const ResourceId& expected_id,
90                                     const ConfigDescription& expected_config,
91                                     const uint8_t expected_data_type, const uint32_t expected_data,
92                                     const uint32_t expected_spec_flags) {
93     const ResourceName expected_res_name = test::ParseNameOrDie(expected_name);
94 
95     table->setParameters(&expected_config);
96 
97     ResTable_config config;
98     Res_value val;
99     uint32_t spec_flags;
100     if (table->getResource(expected_id.id, &val, false, 0, &spec_flags, &config) < 0) {
101       return ::testing::AssertionFailure() << "could not find resource with";
102     }
103 
104     if (expected_data_type != val.dataType) {
105       return ::testing::AssertionFailure()
106              << "expected data type " << std::hex << (int)expected_data_type
107              << " but got data type " << (int)val.dataType << std::dec << " instead";
108     }
109 
110     if (expected_data != val.data) {
111       return ::testing::AssertionFailure()
112              << "expected data " << std::hex << expected_data << " but got data " << val.data
113              << std::dec << " instead";
114     }
115 
116     if (expected_spec_flags != spec_flags) {
117       return ::testing::AssertionFailure()
118              << "expected specFlags " << std::hex << expected_spec_flags << " but got specFlags "
119              << spec_flags << std::dec << " instead";
120     }
121 
122     ResTable::resource_name actual_name;
123     if (!table->getResourceName(expected_id.id, false, &actual_name)) {
124       return ::testing::AssertionFailure() << "failed to find resource name";
125     }
126 
127     std::optional<ResourceName> resName = ResourceUtils::ToResourceName(actual_name);
128     if (!resName) {
129       return ::testing::AssertionFailure()
130              << "expected name '" << expected_res_name << "' but got '"
131              << StringPiece16(actual_name.package, actual_name.packageLen) << ":"
132              << StringPiece16(actual_name.type, actual_name.typeLen) << "/"
133              << StringPiece16(actual_name.name, actual_name.nameLen) << "'";
134     }
135 
136     ResourceName actual_res_name(resName.value());
137 
138     if (expected_res_name.entry != actual_res_name.entry ||
139         expected_res_name.package != actual_res_name.package ||
140         expected_res_name.type != actual_res_name.type) {
141       return ::testing::AssertionFailure() << "expected resource '" << expected_res_name.to_string()
142                                            << "' but got '" << actual_res_name.to_string() << "'";
143     }
144 
145     if (expected_config != config) {
146       return ::testing::AssertionFailure() << "expected config '" << expected_config
147                                            << "' but got '" << ConfigDescription(config) << "'";
148     }
149     return ::testing::AssertionSuccess();
150   }
151 
152  protected:
153   std::unique_ptr<IAaptContext> context_;
154 };
155 
TEST_F(TableFlattenerTest,FlattenFullyLinkedTable)156 TEST_F(TableFlattenerTest, FlattenFullyLinkedTable) {
157   std::unique_ptr<ResourceTable> table =
158       test::ResourceTableBuilder()
159           .AddSimple("com.app.test:id/one", ResourceId(0x7f020000))
160           .AddSimple("com.app.test:id/two", ResourceId(0x7f020001))
161           .AddValue("com.app.test:id/three", ResourceId(0x7f020002),
162                     test::BuildReference("com.app.test:id/one", ResourceId(0x7f020000)))
163           .AddValue("com.app.test:integer/one", ResourceId(0x7f030000),
164                     util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u))
165           .AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"),
166                     ResourceId(0x7f030000),
167                     util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u))
168           .AddString("com.app.test:string/test", ResourceId(0x7f040000), "foo")
169           .AddString("com.app.test:layout/bar", ResourceId(0x7f050000), "res/layout/bar.xml")
170           .Build();
171 
172   ResTable res_table;
173   ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table));
174 
175   EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020000), {},
176                      Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
177 
178   EXPECT_TRUE(Exists(&res_table, "com.app.test:id/two", ResourceId(0x7f020001), {},
179                      Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
180 
181   EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three", ResourceId(0x7f020002), {},
182                      Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
183 
184   EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one", ResourceId(0x7f030000), {},
185                      Res_value::TYPE_INT_DEC, 1u, ResTable_config::CONFIG_VERSION));
186 
187   EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one", ResourceId(0x7f030000),
188                      test::ParseConfigOrDie("v1"), Res_value::TYPE_INT_DEC, 2u,
189                      ResTable_config::CONFIG_VERSION));
190 
191   std::u16string foo_str = u"foo";
192   auto idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
193   ASSERT_TRUE(idx.has_value());
194   EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test", ResourceId(0x7f040000), {},
195                      Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
196 
197   std::u16string bar_path = u"res/layout/bar.xml";
198   idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
199   ASSERT_TRUE(idx.has_value());
200   EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/bar", ResourceId(0x7f050000), {},
201                      Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
202 }
203 
TEST_F(TableFlattenerTest,FlattenEntriesWithGapsInIds)204 TEST_F(TableFlattenerTest, FlattenEntriesWithGapsInIds) {
205   std::unique_ptr<ResourceTable> table =
206       test::ResourceTableBuilder()
207           .AddSimple("com.app.test:id/one", ResourceId(0x7f020001))
208           .AddSimple("com.app.test:id/three", ResourceId(0x7f020003))
209           .Build();
210 
211   ResTable res_table;
212   ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table));
213 
214   EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020001), {},
215                      Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
216   EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three", ResourceId(0x7f020003), {},
217                      Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
218 }
219 
TEST_F(TableFlattenerTest,FlattenMinMaxAttributes)220 TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) {
221   Attribute attr;
222   attr.type_mask = android::ResTable_map::TYPE_INTEGER;
223   attr.min_int = 10;
224   attr.max_int = 23;
225   std::unique_ptr<ResourceTable> table =
226       test::ResourceTableBuilder()
227           .AddValue("android:attr/foo", ResourceId(0x01010000), util::make_unique<Attribute>(attr))
228           .Build();
229 
230   ResourceTable result;
231   ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &result));
232 
233   Attribute* actual_attr = test::GetValue<Attribute>(&result, "android:attr/foo");
234   ASSERT_THAT(actual_attr, NotNull());
235   EXPECT_EQ(attr.IsWeak(), actual_attr->IsWeak());
236   EXPECT_EQ(attr.type_mask, actual_attr->type_mask);
237   EXPECT_EQ(attr.min_int, actual_attr->min_int);
238   EXPECT_EQ(attr.max_int, actual_attr->max_int);
239 }
240 
TEST_F(TableFlattenerTest,FlattenArray)241 TEST_F(TableFlattenerTest, FlattenArray) {
242   auto array = util::make_unique<Array>();
243   array->elements.push_back(util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC),
244                                                                1u));
245   array->elements.push_back(util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC),
246                                                                2u));
247   std::unique_ptr<ResourceTable> table =
248       test::ResourceTableBuilder()
249           .AddValue("android:array/foo", ResourceId(0x01010000), std::move(array))
250           .Build();
251 
252   std::string result;
253   ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &result));
254 
255   // Parse the flattened resource table
256   ResChunkPullParser parser(result.data(), result.size());
257   ASSERT_TRUE(parser.IsGoodEvent(parser.Next()));
258   ASSERT_EQ(android::util::DeviceToHost16(parser.chunk()->type), RES_TABLE_TYPE);
259 
260   // Retrieve the package of the entry
261   ResChunkPullParser table_parser(GetChunkData(parser.chunk()), GetChunkDataLen(parser.chunk()));
262   const ResChunk_header* package_chunk = nullptr;
263   while (table_parser.IsGoodEvent(table_parser.Next())) {
264     if (android::util::DeviceToHost16(table_parser.chunk()->type) == RES_TABLE_PACKAGE_TYPE) {
265       package_chunk = table_parser.chunk();
266       break;
267     }
268   }
269 
270   // Retrieve the type that proceeds the array entry
271   ASSERT_NE(package_chunk, nullptr);
272   ResChunkPullParser package_parser(GetChunkData(table_parser.chunk()),
273                                     GetChunkDataLen(table_parser.chunk()));
274   const ResChunk_header* type_chunk = nullptr;
275   while (package_parser.IsGoodEvent(package_parser.Next())) {
276     if (android::util::DeviceToHost16(package_parser.chunk()->type) == RES_TABLE_TYPE_TYPE) {
277       type_chunk = package_parser.chunk();
278       break;
279     }
280   }
281 
282   // Retrieve the array entry
283   ASSERT_NE(type_chunk, nullptr);
284   TypeVariant typeVariant((const ResTable_type*) type_chunk);
285   auto entry = (const ResTable_map_entry*)*typeVariant.beginEntries();
286   ASSERT_EQ(android::util::DeviceToHost16(entry->count), 2u);
287 
288   // Check that the value and name of the array entries are correct
289   auto values = (const ResTable_map*)(((const uint8_t *)entry) + entry->size);
290   ASSERT_EQ(values->value.data, 1u);
291   ASSERT_EQ(values->name.ident, android::ResTable_map::ATTR_MIN);
292   ASSERT_EQ((values+1)->value.data, 2u);
293   ASSERT_EQ((values+1)->name.ident, android::ResTable_map::ATTR_MIN + 1);
294 }
295 
BuildTableWithSparseEntries(IAaptContext * context,const ConfigDescription & sparse_config,float load)296 static std::unique_ptr<ResourceTable> BuildTableWithSparseEntries(
297     IAaptContext* context, const ConfigDescription& sparse_config, float load) {
298   std::unique_ptr<ResourceTable> table =
299       test::ResourceTableBuilder()
300           .Build();
301 
302   // Add regular entries.
303   CloningValueTransformer cloner(&table->string_pool);
304   int stride = static_cast<int>(1.0f / load);
305   for (int i = 0; i < 100; i++) {
306     const ResourceName name = test::ParseNameOrDie(
307         base::StringPrintf("%s:string/foo_%d", context->GetCompilationPackage().data(), i));
308     const ResourceId resid(context->GetPackageId(), 0x02, static_cast<uint16_t>(i));
309     const auto value =
310         util::make_unique<BinaryPrimitive>(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(i));
311     CHECK(table->AddResource(NewResourceBuilder(name)
312                                  .SetId(resid)
313                                  .SetValue(std::unique_ptr<Value>(value->Transform(cloner)))
314                                  .Build(),
315                              context->GetDiagnostics()));
316 
317     // Every few entries, write out a sparse_config value. This will give us the desired load.
318     if (i % stride == 0) {
319       CHECK(table->AddResource(
320           NewResourceBuilder(name)
321               .SetId(resid)
322               .SetValue(std::unique_ptr<Value>(value->Transform(cloner)), sparse_config)
323               .Build(),
324           context->GetDiagnostics()));
325     }
326   }
327   return table;
328 }
329 
CheckSparseEntries(IAaptContext * context,const ConfigDescription & sparse_config,const std::string & sparse_contents)330 static void CheckSparseEntries(IAaptContext* context, const ConfigDescription& sparse_config,
331                                const std::string& sparse_contents) {
332   ResourceTable sparse_table;
333   BinaryResourceParser parser(context->GetDiagnostics(), &sparse_table, Source("test.arsc"),
334                               sparse_contents.data(), sparse_contents.size());
335   ASSERT_TRUE(parser.Parse());
336 
337   auto value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_0",
338                                                         sparse_config);
339   ASSERT_THAT(value, NotNull());
340   EXPECT_EQ(0u, value->value.data);
341 
342   ASSERT_THAT(test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_1",
343                                                        sparse_config),
344               IsNull());
345 
346   value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_4",
347                                                    sparse_config);
348   ASSERT_THAT(value, NotNull());
349   EXPECT_EQ(4u, value->value.data);
350 }
351 
TEST_F(TableFlattenerTest,FlattenSparseEntryWithMinSdkSV2)352 TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkSV2) {
353   std::unique_ptr<IAaptContext> context = test::ContextBuilder()
354                                               .SetCompilationPackage("android")
355                                               .SetPackageId(0x01)
356                                               .SetMinSdkVersion(SDK_S_V2)
357                                               .Build();
358 
359   const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB");
360   auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f);
361 
362   TableFlattenerOptions options;
363   options.sparse_entries = SparseEntriesMode::Enabled;
364 
365   std::string no_sparse_contents;
366   ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents));
367 
368   std::string sparse_contents;
369   ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents));
370 
371   EXPECT_GT(no_sparse_contents.size(), sparse_contents.size());
372 
373   CheckSparseEntries(context.get(), sparse_config, sparse_contents);
374 }
375 
TEST_F(TableFlattenerTest,FlattenSparseEntryWithMinSdkSV2AndForced)376 TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkSV2AndForced) {
377   std::unique_ptr<IAaptContext> context = test::ContextBuilder()
378                                               .SetCompilationPackage("android")
379                                               .SetPackageId(0x01)
380                                               .SetMinSdkVersion(SDK_S_V2)
381                                               .Build();
382 
383   const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB");
384   auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f);
385 
386   TableFlattenerOptions options;
387   options.sparse_entries = SparseEntriesMode::Forced;
388 
389   std::string no_sparse_contents;
390   ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents));
391 
392   std::string sparse_contents;
393   ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents));
394 
395   EXPECT_GT(no_sparse_contents.size(), sparse_contents.size());
396 
397   CheckSparseEntries(context.get(), sparse_config, sparse_contents);
398 }
399 
TEST_F(TableFlattenerTest,FlattenSparseEntryWithMinSdkBeforeSV2)400 TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkBeforeSV2) {
401   std::unique_ptr<IAaptContext> context = test::ContextBuilder()
402                                               .SetCompilationPackage("android")
403                                               .SetPackageId(0x01)
404                                               .SetMinSdkVersion(SDK_LOLLIPOP)
405                                               .Build();
406 
407   const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB");
408   auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f);
409 
410   TableFlattenerOptions options;
411   options.sparse_entries = SparseEntriesMode::Enabled;
412 
413   std::string no_sparse_contents;
414   ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents));
415 
416   std::string sparse_contents;
417   ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents));
418 
419   EXPECT_EQ(no_sparse_contents.size(), sparse_contents.size());
420 }
421 
TEST_F(TableFlattenerTest,FlattenSparseEntryWithMinSdkBeforeSV2AndConfigSdkVersionSV2)422 TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkBeforeSV2AndConfigSdkVersionSV2) {
423   std::unique_ptr<IAaptContext> context = test::ContextBuilder()
424                                               .SetCompilationPackage("android")
425                                               .SetPackageId(0x01)
426                                               .SetMinSdkVersion(SDK_LOLLIPOP)
427                                               .Build();
428 
429   const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB-v32");
430   auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f);
431 
432   TableFlattenerOptions options;
433   options.sparse_entries = SparseEntriesMode::Enabled;
434 
435   std::string no_sparse_contents;
436   ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents));
437 
438   std::string sparse_contents;
439   ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents));
440 
441   EXPECT_EQ(no_sparse_contents.size(), sparse_contents.size());
442 }
443 
TEST_F(TableFlattenerTest,FlattenSparseEntryWithMinSdkBeforeSV2AndForced)444 TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkBeforeSV2AndForced) {
445   std::unique_ptr<IAaptContext> context = test::ContextBuilder()
446                                               .SetCompilationPackage("android")
447                                               .SetPackageId(0x01)
448                                               .SetMinSdkVersion(SDK_LOLLIPOP)
449                                               .Build();
450 
451   const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB");
452   auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f);
453 
454   TableFlattenerOptions options;
455   options.sparse_entries = SparseEntriesMode::Forced;
456 
457   std::string no_sparse_contents;
458   ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents));
459 
460   std::string sparse_contents;
461   ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents));
462 
463   EXPECT_EQ(no_sparse_contents.size(), sparse_contents.size());
464 }
465 
TEST_F(TableFlattenerTest,FlattenSparseEntryWithSdkVersionNotSet)466 TEST_F(TableFlattenerTest, FlattenSparseEntryWithSdkVersionNotSet) {
467   std::unique_ptr<IAaptContext> context =
468       test::ContextBuilder().SetCompilationPackage("android").SetPackageId(0x01).Build();
469 
470   const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB");
471   auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f);
472 
473   TableFlattenerOptions options;
474   options.sparse_entries = SparseEntriesMode::Enabled;
475 
476   std::string no_sparse_contents;
477   ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents));
478 
479   std::string sparse_contents;
480   ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents));
481 
482   EXPECT_EQ(no_sparse_contents.size(), sparse_contents.size());
483 }
484 
TEST_F(TableFlattenerTest,FlattenSparseEntryWithSdkVersionNotSetAndForced)485 TEST_F(TableFlattenerTest, FlattenSparseEntryWithSdkVersionNotSetAndForced) {
486   std::unique_ptr<IAaptContext> context =
487       test::ContextBuilder().SetCompilationPackage("android").SetPackageId(0x01).Build();
488 
489   const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB");
490   auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f);
491 
492   TableFlattenerOptions options;
493   options.sparse_entries = SparseEntriesMode::Forced;
494 
495   std::string no_sparse_contents;
496   ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents));
497 
498   std::string sparse_contents;
499   ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents));
500 
501   EXPECT_GT(no_sparse_contents.size(), sparse_contents.size());
502 
503   CheckSparseEntries(context.get(), sparse_config, sparse_contents);
504 }
505 
TEST_F(TableFlattenerTest,DoNotUseSparseEntryForDenseConfig)506 TEST_F(TableFlattenerTest, DoNotUseSparseEntryForDenseConfig) {
507   std::unique_ptr<IAaptContext> context = test::ContextBuilder()
508                                               .SetCompilationPackage("android")
509                                               .SetPackageId(0x01)
510                                               .SetMinSdkVersion(SDK_O)
511                                               .Build();
512 
513   const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB");
514   auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.80f);
515 
516   TableFlattenerOptions options;
517   options.sparse_entries = SparseEntriesMode::Enabled;
518 
519   std::string no_sparse_contents;
520   ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents));
521 
522   std::string sparse_contents;
523   ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents));
524 
525   EXPECT_EQ(no_sparse_contents.size(), sparse_contents.size());
526 }
527 
TEST_F(TableFlattenerTest,FlattenSharedLibrary)528 TEST_F(TableFlattenerTest, FlattenSharedLibrary) {
529   std::unique_ptr<IAaptContext> context =
530       test::ContextBuilder().SetCompilationPackage("lib").SetPackageId(0x00).Build();
531   std::unique_ptr<ResourceTable> table =
532       test::ResourceTableBuilder()
533           .AddValue("lib:id/foo", ResourceId(0x00010000), util::make_unique<Id>())
534           .Build();
535   ResourceTable result;
536   ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result));
537 
538   std::optional<ResourceTable::SearchResult> search_result =
539       result.FindResource(test::ParseNameOrDie("lib:id/foo"));
540   ASSERT_TRUE(search_result);
541   EXPECT_EQ(0x00u, search_result.value().entry->id.value().package_id());
542 
543   auto iter = result.included_packages_.find(0x00);
544   ASSERT_NE(result.included_packages_.end(), iter);
545   EXPECT_EQ("lib", iter->second);
546 }
547 
TEST_F(TableFlattenerTest,FlattenSharedLibraryWithStyle)548 TEST_F(TableFlattenerTest, FlattenSharedLibraryWithStyle) {
549   std::unique_ptr<IAaptContext> context =
550       test::ContextBuilder().SetCompilationPackage("lib").SetPackageId(0x00).Build();
551   std::unique_ptr<ResourceTable> table =
552       test::ResourceTableBuilder()
553           .AddValue("lib:style/Theme",
554                     ResourceId(0x00030001),
555                     test::StyleBuilder()
556                     .AddItem("lib:attr/bar", ResourceId(0x00010002),
557                              ResourceUtils::TryParseInt("2"))
558                     .AddItem("lib:attr/foo", ResourceId(0x00010001),
559                              ResourceUtils::TryParseInt("1"))
560                     .AddItem("android:attr/bar", ResourceId(0x01010002),
561                              ResourceUtils::TryParseInt("4"))
562                     .AddItem("android:attr/foo", ResourceId(0x01010001),
563                              ResourceUtils::TryParseInt("3"))
564                     .Build())
565           .Build();
566   ResourceTable result;
567   ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result));
568 
569   std::optional<ResourceTable::SearchResult> search_result =
570       result.FindResource(test::ParseNameOrDie("lib:style/Theme"));
571   ASSERT_TRUE(search_result);
572   EXPECT_EQ(0x00030001u, search_result.value().entry->id.value());
573   ASSERT_EQ(1u, search_result.value().entry->values.size());
574   Value* value = search_result.value().entry->values[0]->value.get();
575   Style* style = ValueCast<Style>(value);
576   ASSERT_TRUE(style);
577   ASSERT_EQ(4u, style->entries.size());
578   // Ensure the attributes from the shared library come after the items from
579   // android.
580   EXPECT_EQ(0x01010001, style->entries[0].key.id.value());
581   EXPECT_EQ(0x01010002, style->entries[1].key.id.value());
582   EXPECT_EQ(0x00010001, style->entries[2].key.id.value());
583   EXPECT_EQ(0x00010002, style->entries[3].key.id.value());
584 }
585 
TEST_F(TableFlattenerTest,FlattenTableReferencingSharedLibraries)586 TEST_F(TableFlattenerTest, FlattenTableReferencingSharedLibraries) {
587   std::unique_ptr<IAaptContext> context =
588       test::ContextBuilder().SetCompilationPackage("app").SetPackageId(0x7f).Build();
589   std::unique_ptr<ResourceTable> table =
590       test::ResourceTableBuilder()
591           .AddValue("app:id/foo", ResourceId(0x7f010000),
592                     test::BuildReference("lib_one:id/foo", ResourceId(0x02010000)))
593           .AddValue("app:id/bar", ResourceId(0x7f010001),
594                     test::BuildReference("lib_two:id/bar", ResourceId(0x03010000)))
595           .Build();
596   table->included_packages_[0x02] = "lib_one";
597   table->included_packages_[0x03] = "lib_two";
598 
599   ResTable result;
600   ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result));
601 
602   const DynamicRefTable* dynamic_ref_table = result.getDynamicRefTableForCookie(1);
603   ASSERT_THAT(dynamic_ref_table, NotNull());
604 
605   const KeyedVector<String16, uint8_t>& entries = dynamic_ref_table->entries();
606 
607   ssize_t idx = entries.indexOfKey(android::String16("lib_one"));
608   ASSERT_GE(idx, 0);
609   EXPECT_EQ(0x02u, entries.valueAt(idx));
610 
611   idx = entries.indexOfKey(android::String16("lib_two"));
612   ASSERT_GE(idx, 0);
613   EXPECT_EQ(0x03u, entries.valueAt(idx));
614 }
615 
TEST_F(TableFlattenerTest,PackageWithNonStandardIdHasDynamicRefTable)616 TEST_F(TableFlattenerTest, PackageWithNonStandardIdHasDynamicRefTable) {
617   std::unique_ptr<IAaptContext> context =
618       test::ContextBuilder().SetCompilationPackage("app").SetPackageId(0x80).Build();
619   std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
620                                              .AddSimple("app:id/foo", ResourceId(0x80010000))
621                                              .Build();
622 
623   ResTable result;
624   ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result));
625 
626   const DynamicRefTable* dynamic_ref_table = result.getDynamicRefTableForCookie(1);
627   ASSERT_THAT(dynamic_ref_table, NotNull());
628 
629   const KeyedVector<String16, uint8_t>& entries = dynamic_ref_table->entries();
630   ssize_t idx = entries.indexOfKey(android::String16("app"));
631   ASSERT_GE(idx, 0);
632   EXPECT_EQ(0x80u, entries.valueAt(idx));
633 }
634 
TEST_F(TableFlattenerTest,LongPackageNameIsTruncated)635 TEST_F(TableFlattenerTest, LongPackageNameIsTruncated) {
636   std::string kPackageName(256, 'F');
637 
638   std::unique_ptr<IAaptContext> context =
639       test::ContextBuilder().SetCompilationPackage(kPackageName).SetPackageId(0x7f).Build();
640   std::unique_ptr<ResourceTable> table =
641       test::ResourceTableBuilder()
642           .AddSimple(kPackageName + ":id/foo", ResourceId(0x7f010000))
643           .Build();
644 
645   ResTable result;
646   ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result));
647 
648   ASSERT_EQ(1u, result.getBasePackageCount());
649   EXPECT_EQ(127u, result.getBasePackageName(0).size());
650 }
651 
TEST_F(TableFlattenerTest,LongSharedLibraryPackageNameIsIllegal)652 TEST_F(TableFlattenerTest, LongSharedLibraryPackageNameIsIllegal) {
653   std::string kPackageName(256, 'F');
654 
655   std::unique_ptr<IAaptContext> context = test::ContextBuilder()
656                                               .SetCompilationPackage(kPackageName)
657                                               .SetPackageId(0x7f)
658                                               .SetPackageType(PackageType::kSharedLib)
659                                               .Build();
660   std::unique_ptr<ResourceTable> table =
661       test::ResourceTableBuilder()
662           .AddSimple(kPackageName + ":id/foo", ResourceId(0x7f010000))
663           .Build();
664 
665   ResTable result;
666   ASSERT_FALSE(Flatten(context.get(), {}, table.get(), &result));
667 }
668 
TEST_F(TableFlattenerTest,ObfuscatingResourceNamesNoNameCollapseExemptionsSucceeds)669 TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoNameCollapseExemptionsSucceeds) {
670   std::unique_ptr<ResourceTable> table =
671       test::ResourceTableBuilder()
672           .AddSimple("com.app.test:id/one", ResourceId(0x7f020000))
673           .AddSimple("com.app.test:id/two", ResourceId(0x7f020001))
674           .AddValue("com.app.test:id/three", ResourceId(0x7f020002),
675                     test::BuildReference("com.app.test:id/one", ResourceId(0x7f020000)))
676           .AddValue("com.app.test:integer/one", ResourceId(0x7f030000),
677                     util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u))
678           .AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"),
679                     ResourceId(0x7f030000),
680                     util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u))
681           .AddString("com.app.test:string/test", ResourceId(0x7f040000), "foo")
682           .AddString("com.app.test:layout/bar", ResourceId(0x7f050000), "res/layout/bar.xml")
683           .Build();
684 
685   TableFlattenerOptions options;
686   options.collapse_key_stringpool = true;
687 
688   ResTable res_table;
689 
690   ASSERT_TRUE(Flatten(context_.get(), options, table.get(), &res_table));
691 
692   EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
693                      ResourceId(0x7f020000), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
694 
695   EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
696                      ResourceId(0x7f020001), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
697 
698   EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
699                      ResourceId(0x7f020002), {}, Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
700 
701   EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated",
702                      ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u,
703                      ResTable_config::CONFIG_VERSION));
704 
705   EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated",
706                      ResourceId(0x7f030000), test::ParseConfigOrDie("v1"), Res_value::TYPE_INT_DEC,
707                      2u, ResTable_config::CONFIG_VERSION));
708 
709   std::u16string foo_str = u"foo";
710   auto idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
711   ASSERT_TRUE(idx.has_value());
712   EXPECT_TRUE(Exists(&res_table, "com.app.test:string/0_resource_name_obfuscated",
713                      ResourceId(0x7f040000), {}, Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
714 
715   std::u16string bar_path = u"res/layout/bar.xml";
716   idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
717   ASSERT_TRUE(idx.has_value());
718   EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/0_resource_name_obfuscated",
719                      ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
720 }
721 
TEST_F(TableFlattenerTest,ObfuscatingResourceNamesWithDeduplicationSucceeds)722 TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithDeduplicationSucceeds) {
723   std::unique_ptr<ResourceTable> table =
724       test::ResourceTableBuilder()
725           .AddSimple("com.app.test:id/one", ResourceId(0x7f020000))
726           .AddSimple("com.app.test:id/two", ResourceId(0x7f020001))
727           .AddValue("com.app.test:id/three", ResourceId(0x7f020002),
728                     test::BuildReference("com.app.test:id/one", ResourceId(0x7f020000)))
729           .AddValue("com.app.test:integer/one", ResourceId(0x7f030000),
730                     util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u))
731           .AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"),
732                     ResourceId(0x7f030000),
733                     util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u))
734           .AddString("com.app.test:string/test1", ResourceId(0x7f040000), "foo")
735           .AddString("com.app.test:string/test2", ResourceId(0x7f040001), "foo")
736           .AddString("com.app.test:string/test3", ResourceId(0x7f040002), "bar")
737           .AddString("com.app.test:string/test4", ResourceId(0x7f040003), "foo")
738           .AddString("com.app.test:layout/bar1", ResourceId(0x7f050000), "res/layout/bar.xml")
739           .AddString("com.app.test:layout/bar2", ResourceId(0x7f050001), "res/layout/bar.xml")
740           .Build();
741 
742   TableFlattenerOptions options;
743   options.collapse_key_stringpool = true;
744   options.deduplicate_entry_values = true;
745 
746   ResTable res_table;
747 
748   ASSERT_TRUE(Flatten(context_.get(), options, table.get(), &res_table));
749 
750   EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
751                      ResourceId(0x7f020000), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
752 
753   EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
754                      ResourceId(0x7f020001), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
755 
756   EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
757                      ResourceId(0x7f020002), {}, Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
758 
759   EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated",
760                      ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u,
761                      ResTable_config::CONFIG_VERSION));
762 
763   EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated",
764                      ResourceId(0x7f030000), test::ParseConfigOrDie("v1"), Res_value::TYPE_INT_DEC,
765                      2u, ResTable_config::CONFIG_VERSION));
766 
767   std::u16string foo_str = u"foo";
768   std::u16string bar_str = u"bar";
769   auto foo_idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
770   auto bar_idx = res_table.getTableStringBlock(0)->indexOfString(bar_str.data(), bar_str.size());
771   ASSERT_TRUE(foo_idx.has_value());
772   EXPECT_TRUE(Exists(&res_table, "com.app.test:string/0_resource_name_obfuscated",
773                      ResourceId(0x7f040000), {}, Res_value::TYPE_STRING, (uint32_t)*foo_idx, 0u));
774   EXPECT_TRUE(Exists(&res_table, "com.app.test:string/0_resource_name_obfuscated",
775                      ResourceId(0x7f040001), {}, Res_value::TYPE_STRING, (uint32_t)*foo_idx, 0u));
776   EXPECT_TRUE(Exists(&res_table, "com.app.test:string/0_resource_name_obfuscated",
777                      ResourceId(0x7f040002), {}, Res_value::TYPE_STRING, (uint32_t)*bar_idx, 0u));
778   EXPECT_TRUE(Exists(&res_table, "com.app.test:string/0_resource_name_obfuscated",
779                      ResourceId(0x7f040003), {}, Res_value::TYPE_STRING, (uint32_t)*foo_idx, 0u));
780 
781   std::u16string bar_path = u"res/layout/bar.xml";
782   auto bar_path_idx =
783       res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
784   ASSERT_TRUE(bar_path_idx.has_value());
785   EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/0_resource_name_obfuscated",
786                      ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)*bar_path_idx,
787                      0u));
788   EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/0_resource_name_obfuscated",
789                      ResourceId(0x7f050001), {}, Res_value::TYPE_STRING, (uint32_t)*bar_path_idx,
790                      0u));
791 
792   std::string deduplicated_output;
793   std::string sequential_output;
794   Flatten(context_.get(), options, table.get(), &deduplicated_output);
795   options.deduplicate_entry_values = false;
796   Flatten(context_.get(), options, table.get(), &sequential_output);
797 
798   // We have 4 duplicates: 0x7f020001 id, 0x7f040001 string, 0x7f040003 string, 0x7f050001 layout.
799   EXPECT_EQ(sequential_output.size(),
800             deduplicated_output.size() + 4 * (sizeof(ResTable_entry) + sizeof(Res_value)));
801 }
802 
TEST_F(TableFlattenerTest,ObfuscatingResourceNamesWithNameCollapseExemptionsSucceeds)803 TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithNameCollapseExemptionsSucceeds) {
804   std::unique_ptr<ResourceTable> table =
805       test::ResourceTableBuilder()
806           .AddSimple("com.app.test:id/one", ResourceId(0x7f020000))
807           .AddSimple("com.app.test:id/two", ResourceId(0x7f020001))
808           .AddValue("com.app.test:id/three", ResourceId(0x7f020002),
809                     test::BuildReference("com.app.test:id/one", ResourceId(0x7f020000)))
810           .AddValue("com.app.test:integer/one", ResourceId(0x7f030000),
811                     util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u))
812           .AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"),
813                     ResourceId(0x7f030000),
814                     util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u))
815           .AddString("com.app.test:string/test", ResourceId(0x7f040000), "foo")
816           .AddString("com.app.test:layout/bar", ResourceId(0x7f050000), "res/layout/bar.xml")
817           .Build();
818 
819   TableFlattenerOptions options;
820   options.collapse_key_stringpool = true;
821   options.name_collapse_exemptions.insert(ResourceName({}, ResourceType::kId, "one"));
822   options.name_collapse_exemptions.insert(ResourceName({}, ResourceType::kString, "test"));
823   ResTable res_table;
824 
825   ASSERT_TRUE(Flatten(context_.get(), options, table.get(), &res_table));
826 
827   EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one",
828                      ResourceId(0x7f020000), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
829 
830   EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
831                      ResourceId(0x7f020001), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
832 
833   EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
834                      ResourceId(0x7f020002), {}, Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
835 
836   // Note that this resource is also named "one", but it's a different type, so gets obfuscated.
837   EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated",
838                      ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u,
839                      ResTable_config::CONFIG_VERSION));
840 
841   EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated",
842                      ResourceId(0x7f030000), test::ParseConfigOrDie("v1"), Res_value::TYPE_INT_DEC,
843                      2u, ResTable_config::CONFIG_VERSION));
844 
845   std::u16string foo_str = u"foo";
846   auto idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
847   ASSERT_TRUE(idx.has_value());
848   EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test", ResourceId(0x7f040000), {},
849                      Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
850 
851   std::u16string bar_path = u"res/layout/bar.xml";
852   idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
853   ASSERT_TRUE(idx.has_value());
854   EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/0_resource_name_obfuscated",
855                      ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
856 }
857 
TEST_F(TableFlattenerTest,FlattenOverlayable)858 TEST_F(TableFlattenerTest, FlattenOverlayable) {
859   OverlayableItem overlayable_item(std::make_shared<Overlayable>("TestName", "overlay://theme"));
860   overlayable_item.policies |= PolicyFlags::PRODUCT_PARTITION;
861   overlayable_item.policies |= PolicyFlags::SYSTEM_PARTITION;
862   overlayable_item.policies |= PolicyFlags::VENDOR_PARTITION;
863 
864   std::string name = "com.app.test:integer/overlayable";
865   std::unique_ptr<ResourceTable> table =
866       test::ResourceTableBuilder()
867           .AddSimple(name, ResourceId(0x7f020000))
868           .SetOverlayable(name, overlayable_item)
869           .Build();
870 
871   ResourceTable output_table;
872   ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &output_table));
873 
874   auto search_result = output_table.FindResource(test::ParseNameOrDie(name));
875   ASSERT_TRUE(search_result);
876   ASSERT_THAT(search_result.value().entry, NotNull());
877   ASSERT_TRUE(search_result.value().entry->overlayable_item);
878   OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
879   EXPECT_EQ(result_overlayable_item.policies, PolicyFlags::SYSTEM_PARTITION
880                                          | PolicyFlags::VENDOR_PARTITION
881                                          | PolicyFlags::PRODUCT_PARTITION);
882 }
883 
TEST_F(TableFlattenerTest,FlattenMultipleOverlayablePolicies)884 TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
885   auto overlayable = std::make_shared<Overlayable>("TestName", "overlay://theme");
886   std::string name_zero = "com.app.test:integer/overlayable_zero_item";
887   OverlayableItem overlayable_item_zero(overlayable);
888   overlayable_item_zero.policies |= PolicyFlags::PRODUCT_PARTITION;
889   overlayable_item_zero.policies |= PolicyFlags::SYSTEM_PARTITION;
890 
891   std::string name_one = "com.app.test:integer/overlayable_one_item";
892   OverlayableItem overlayable_item_one(overlayable);
893   overlayable_item_one.policies |= PolicyFlags::PUBLIC;
894 
895   std::string name_two = "com.app.test:integer/overlayable_two_item";
896   OverlayableItem overlayable_item_two(overlayable);
897   overlayable_item_two.policies |= PolicyFlags::PRODUCT_PARTITION;
898   overlayable_item_two.policies |= PolicyFlags::SYSTEM_PARTITION;
899   overlayable_item_two.policies |= PolicyFlags::VENDOR_PARTITION;
900 
901   std::unique_ptr<ResourceTable> table =
902       test::ResourceTableBuilder()
903           .AddSimple(name_zero, ResourceId(0x7f020000))
904           .SetOverlayable(name_zero, overlayable_item_zero)
905           .AddSimple(name_one, ResourceId(0x7f020001))
906           .SetOverlayable(name_one, overlayable_item_one)
907           .AddSimple(name_two, ResourceId(0x7f020002))
908           .SetOverlayable(name_two, overlayable_item_two)
909           .Build();
910 
911   ResourceTable output_table;
912   ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &output_table));
913 
914   auto search_result = output_table.FindResource(test::ParseNameOrDie(name_zero));
915   ASSERT_TRUE(search_result);
916   ASSERT_THAT(search_result.value().entry, NotNull());
917   ASSERT_TRUE(search_result.value().entry->overlayable_item);
918   OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value();
919   EXPECT_EQ(overlayable_item.policies, PolicyFlags::SYSTEM_PARTITION
920                                        | PolicyFlags::PRODUCT_PARTITION);
921 
922   search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
923   ASSERT_TRUE(search_result);
924   ASSERT_THAT(search_result.value().entry, NotNull());
925   ASSERT_TRUE(search_result.value().entry->overlayable_item);
926   overlayable_item = search_result.value().entry->overlayable_item.value();
927   EXPECT_EQ(overlayable_item.policies, PolicyFlags::PUBLIC);
928 
929   search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
930   ASSERT_TRUE(search_result);
931   ASSERT_THAT(search_result.value().entry, NotNull());
932   ASSERT_TRUE(search_result.value().entry->overlayable_item);
933   overlayable_item = search_result.value().entry->overlayable_item.value();
934   EXPECT_EQ(overlayable_item.policies, PolicyFlags::SYSTEM_PARTITION
935                                        | PolicyFlags::PRODUCT_PARTITION
936                                        | PolicyFlags::VENDOR_PARTITION);
937 }
938 
TEST_F(TableFlattenerTest,FlattenMultipleOverlayable)939 TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) {
940   auto group = std::make_shared<Overlayable>("TestName", "overlay://theme");
941   std::string name_zero = "com.app.test:integer/overlayable_zero";
942   OverlayableItem overlayable_item_zero(group);
943   overlayable_item_zero.policies |= PolicyFlags::PRODUCT_PARTITION;
944   overlayable_item_zero.policies |= PolicyFlags::SYSTEM_PARTITION;
945 
946   auto group_one = std::make_shared<Overlayable>("OtherName", "overlay://customization");
947   std::string name_one = "com.app.test:integer/overlayable_one";
948   OverlayableItem overlayable_item_one(group_one);
949   overlayable_item_one.policies |= PolicyFlags::PUBLIC;
950 
951   std::string name_two = "com.app.test:integer/overlayable_two";
952   OverlayableItem overlayable_item_two(group);
953   overlayable_item_two.policies |= PolicyFlags::ODM_PARTITION;
954   overlayable_item_two.policies |= PolicyFlags::OEM_PARTITION;
955   overlayable_item_two.policies |= PolicyFlags::VENDOR_PARTITION;
956 
957   std::string name_three = "com.app.test:integer/overlayable_three";
958   OverlayableItem overlayable_item_three(group_one);
959   overlayable_item_three.policies |= PolicyFlags::SIGNATURE;
960   overlayable_item_three.policies |= PolicyFlags::ACTOR_SIGNATURE;
961   overlayable_item_three.policies |= PolicyFlags::CONFIG_SIGNATURE;
962 
963   std::unique_ptr<ResourceTable> table =
964       test::ResourceTableBuilder()
965           .AddSimple(name_zero, ResourceId(0x7f020000))
966           .SetOverlayable(name_zero, overlayable_item_zero)
967           .AddSimple(name_one, ResourceId(0x7f020001))
968           .SetOverlayable(name_one, overlayable_item_one)
969           .AddSimple(name_two, ResourceId(0x7f020002))
970           .SetOverlayable(name_two, overlayable_item_two)
971           .AddSimple(name_three, ResourceId(0x7f020003))
972           .SetOverlayable(name_three, overlayable_item_three)
973           .Build();
974 
975   ResourceTable output_table;
976   ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &output_table));
977   auto search_result = output_table.FindResource(test::ParseNameOrDie(name_zero));
978   ASSERT_TRUE(search_result);
979   ASSERT_THAT(search_result.value().entry, NotNull());
980   ASSERT_TRUE(search_result.value().entry->overlayable_item);
981   OverlayableItem& result_overlayable = search_result.value().entry->overlayable_item.value();
982   EXPECT_EQ(result_overlayable.overlayable->name, "TestName");
983   EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://theme");
984   EXPECT_EQ(result_overlayable.policies, PolicyFlags::SYSTEM_PARTITION
985                                          | PolicyFlags::PRODUCT_PARTITION);
986 
987   search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
988   ASSERT_TRUE(search_result);
989   ASSERT_THAT(search_result.value().entry, NotNull());
990   ASSERT_TRUE(search_result.value().entry->overlayable_item);
991   result_overlayable = search_result.value().entry->overlayable_item.value();
992   EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
993   EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
994   EXPECT_EQ(result_overlayable.policies, PolicyFlags::PUBLIC);
995 
996   search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
997   ASSERT_TRUE(search_result);
998   ASSERT_THAT(search_result.value().entry, NotNull());
999   ASSERT_TRUE(search_result.value().entry->overlayable_item);
1000   result_overlayable = search_result.value().entry->overlayable_item.value();
1001   EXPECT_EQ(result_overlayable.overlayable->name, "TestName");
1002   EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://theme");
1003   EXPECT_EQ(result_overlayable.policies, PolicyFlags::ODM_PARTITION
1004                                          | PolicyFlags::OEM_PARTITION
1005                                          | PolicyFlags::VENDOR_PARTITION);
1006 
1007   search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
1008   ASSERT_TRUE(search_result);
1009   ASSERT_THAT(search_result.value().entry, NotNull());
1010   ASSERT_TRUE(search_result.value().entry->overlayable_item);
1011   result_overlayable = search_result.value().entry->overlayable_item.value();
1012   EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
1013   EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
1014   EXPECT_EQ(result_overlayable.policies, PolicyFlags::SIGNATURE
1015                                            | PolicyFlags::ACTOR_SIGNATURE
1016                                            | PolicyFlags::CONFIG_SIGNATURE);
1017 }
1018 
TEST_F(TableFlattenerTest,FlattenOverlayableNoPolicyFails)1019 TEST_F(TableFlattenerTest, FlattenOverlayableNoPolicyFails) {
1020   auto group = std::make_shared<Overlayable>("TestName", "overlay://theme");
1021   std::string name_zero = "com.app.test:integer/overlayable_zero";
1022   OverlayableItem overlayable_item_zero(group);
1023 
1024   std::unique_ptr<ResourceTable> table =
1025       test::ResourceTableBuilder()
1026           .AddSimple(name_zero, ResourceId(0x7f020000))
1027           .SetOverlayable(name_zero, overlayable_item_zero)
1028           .Build();
1029   ResourceTable output_table;
1030   ASSERT_FALSE(Flatten(context_.get(), {}, table.get(), &output_table));
1031 }
1032 
TEST_F(TableFlattenerTest,FlattenCustomResourceTypes)1033 TEST_F(TableFlattenerTest, FlattenCustomResourceTypes) {
1034   std::unique_ptr<ResourceTable> table =
1035       test::ResourceTableBuilder()
1036           .AddSimple("com.app.test:id/one", ResourceId(0x7f010000))
1037           .AddSimple("com.app.test:id.2/two", ResourceId(0x7f020000))
1038           .AddValue("com.app.test:integer/one", ResourceId(0x7f030000),
1039                     util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 10u))
1040           .AddValue("com.app.test:integer.1/one", ResourceId(0x7f040000),
1041                     util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u))
1042           .AddValue("com.app.test:integer.1/one", test::ParseConfigOrDie("v1"),
1043                     ResourceId(0x7f040000),
1044                     util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u))
1045           .AddString("com.app.test:layout.custom/bar", ResourceId(0x7f050000), "res/layout/bar.xml")
1046           .Build();
1047 
1048   ResTable res_table;
1049   ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table));
1050 
1051   EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f010000), {},
1052                      Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
1053 
1054   EXPECT_TRUE(Exists(&res_table, "com.app.test:id.2/two", ResourceId(0x7f020000), {},
1055                      Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
1056 
1057   EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one", ResourceId(0x7f030000), {},
1058                      Res_value::TYPE_INT_DEC, 10u, 0u));
1059 
1060   EXPECT_TRUE(Exists(&res_table, "com.app.test:integer.1/one", ResourceId(0x7f040000), {},
1061                      Res_value::TYPE_INT_DEC, 1u, ResTable_config::CONFIG_VERSION));
1062 
1063   EXPECT_TRUE(Exists(&res_table, "com.app.test:integer.1/one", ResourceId(0x7f040000),
1064                      test::ParseConfigOrDie("v1"), Res_value::TYPE_INT_DEC, 2u,
1065                      ResTable_config::CONFIG_VERSION));
1066 
1067   std::u16string bar_path = u"res/layout/bar.xml";
1068   auto idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
1069   ASSERT_TRUE(idx.has_value());
1070   EXPECT_TRUE(Exists(&res_table, "com.app.test:layout.custom/bar", ResourceId(0x7f050000), {},
1071                      Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
1072 }
1073 
TEST_F(TableFlattenerTest,FlattenTypeEntryWithNameCollapseNotInExemption)1074 TEST_F(TableFlattenerTest, FlattenTypeEntryWithNameCollapseNotInExemption) {
1075   OverlayableItem overlayable_item(std::make_shared<Overlayable>("TestName", "overlay://theme"));
1076   overlayable_item.policies |= PolicyFlags::PUBLIC;
1077 
1078   std::string name = "com.app.test:color/overlayable_color";
1079   std::unique_ptr<ResourceTable> table =
1080       test::ResourceTableBuilder()
1081           .AddValue("com.app.test:color/overlayable_color", ResourceId(0x7f010000),
1082                     util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_COLOR_ARGB8),
1083                                                        0xffaabbcc))
1084           .SetOverlayable(name, overlayable_item)
1085           .Build();
1086 
1087   TableFlattenerOptions options;
1088   options.collapse_key_stringpool = true;
1089 
1090   ResTable res_table;
1091   EXPECT_THAT(Flatten(context_.get(), options, table.get(), &res_table), testing::IsTrue());
1092   EXPECT_THAT(Exists(&res_table, "com.app.test:color/overlayable_color", ResourceId(0x7f010000), {},
1093                      Res_value::TYPE_INT_COLOR_ARGB8, 0xffaabbcc, 0u),
1094               testing::IsTrue());
1095 }
1096 
TEST_F(TableFlattenerTest,FlattenTypeEntryWithNameCollapseInExemption)1097 TEST_F(TableFlattenerTest, FlattenTypeEntryWithNameCollapseInExemption) {
1098   OverlayableItem overlayable_item(std::make_shared<Overlayable>("TestName", "overlay://theme"));
1099   overlayable_item.policies |= PolicyFlags::PUBLIC;
1100 
1101   std::string name = "com.app.test:color/overlayable_color";
1102   std::unique_ptr<ResourceTable> table =
1103       test::ResourceTableBuilder()
1104           .AddValue("com.app.test:color/overlayable_color", ResourceId(0x7f010000),
1105                     util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_COLOR_ARGB8),
1106                                                        0xffaabbcc))
1107           .SetOverlayable(name, overlayable_item)
1108           .Build();
1109 
1110   TableFlattenerOptions options;
1111   options.collapse_key_stringpool = true;
1112   options.name_collapse_exemptions.insert(
1113       ResourceName({}, ResourceType::kColor, "overlayable_color"));
1114 
1115   ResTable res_table;
1116   EXPECT_THAT(Flatten(context_.get(), options, table.get(), &res_table), testing::IsTrue());
1117   EXPECT_THAT(Exists(&res_table, "com.app.test:color/overlayable_color", ResourceId(0x7f010000), {},
1118                      Res_value::TYPE_INT_COLOR_ARGB8, 0xffaabbcc, 0u),
1119               testing::IsTrue());
1120 }
1121 
TEST_F(TableFlattenerTest,UsesReadWriteFeatureFlagSerializesCorrectly)1122 TEST_F(TableFlattenerTest, UsesReadWriteFeatureFlagSerializesCorrectly) {
1123   std::unique_ptr<ResourceTable> table =
1124       test::ResourceTableBuilder()
1125           .Add(NewResourceBuilder("com.app.a:color/foo")
1126                    .SetValue(util::make_unique<BinaryPrimitive>(
1127                        uint8_t(Res_value::TYPE_INT_COLOR_ARGB8), 0xffaabbcc))
1128                    .SetUsesReadWriteFeatureFlags(true)
1129                    .SetId(0x7f020000)
1130                    .Build())
1131           .Build();
1132   ResTable res_table;
1133   TableFlattenerOptions options;
1134   ASSERT_TRUE(Flatten(context_.get(), options, table.get(), &res_table));
1135 
1136   uint32_t flags;
1137   ASSERT_TRUE(res_table.getResourceEntryFlags(0x7f020000, &flags));
1138   ASSERT_EQ(flags, ResTable_entry::FLAG_USES_FEATURE_FLAGS);
1139 }
1140 
1141 }  // namespace aapt
1142