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 19#include "android-base/stringprintf.h" 20 21#include "ResourceUtils.h" 22#include "SdkConstants.h" 23#include "format/binary/BinaryResourceParser.h" 24#include "test/Test.h" 25#include "util/Util.h" 26 27using namespace android; 28 29using ::testing::Gt; 30using ::testing::IsNull; 31using ::testing::NotNull; 32 33namespace aapt { 34 35class TableFlattenerTest : public ::testing::Test { 36 public: 37 void SetUp() override { 38 context_ = 39 test::ContextBuilder().SetCompilationPackage("com.app.test").SetPackageId(0x7f).Build(); 40 } 41 42 ::testing::AssertionResult Flatten(IAaptContext* context, const TableFlattenerOptions& options, 43 ResourceTable* table, std::string* out_content) { 44 BigBuffer buffer(1024); 45 TableFlattener flattener(options, &buffer); 46 if (!flattener.Consume(context, table)) { 47 return ::testing::AssertionFailure() << "failed to flatten ResourceTable"; 48 } 49 *out_content = buffer.to_string(); 50 return ::testing::AssertionSuccess(); 51 } 52 53 ::testing::AssertionResult Flatten(IAaptContext* context, const TableFlattenerOptions& options, 54 ResourceTable* table, ResTable* out_table) { 55 std::string content; 56 auto result = Flatten(context, options, table, &content); 57 if (!result) { 58 return result; 59 } 60 61 if (out_table->add(content.data(), content.size(), 1, true) != NO_ERROR) { 62 return ::testing::AssertionFailure() << "flattened ResTable is corrupt"; 63 } 64 return ::testing::AssertionSuccess(); 65 } 66 67 ::testing::AssertionResult Flatten(IAaptContext* context, const TableFlattenerOptions& options, 68 ResourceTable* table, ResourceTable* out_table) { 69 std::string content; 70 auto result = Flatten(context, options, table, &content); 71 if (!result) { 72 return result; 73 } 74 75 BinaryResourceParser parser(context->GetDiagnostics(), out_table, {}, content.data(), 76 content.size()); 77 if (!parser.Parse()) { 78 return ::testing::AssertionFailure() << "flattened ResTable is corrupt"; 79 } 80 return ::testing::AssertionSuccess(); 81 } 82 83 ::testing::AssertionResult Exists(ResTable* table, const StringPiece& expected_name, 84 const ResourceId& expected_id, 85 const ConfigDescription& expected_config, 86 const uint8_t expected_data_type, const uint32_t expected_data, 87 const uint32_t expected_spec_flags) { 88 const ResourceName expected_res_name = test::ParseNameOrDie(expected_name); 89 90 table->setParameters(&expected_config); 91 92 ResTable_config config; 93 Res_value val; 94 uint32_t spec_flags; 95 if (table->getResource(expected_id.id, &val, false, 0, &spec_flags, &config) < 0) { 96 return ::testing::AssertionFailure() << "could not find resource with"; 97 } 98 99 if (expected_data_type != val.dataType) { 100 return ::testing::AssertionFailure() 101 << "expected data type " << std::hex << (int)expected_data_type 102 << " but got data type " << (int)val.dataType << std::dec << " instead"; 103 } 104 105 if (expected_data != val.data) { 106 return ::testing::AssertionFailure() 107 << "expected data " << std::hex << expected_data << " but got data " << val.data 108 << std::dec << " instead"; 109 } 110 111 if (expected_spec_flags != spec_flags) { 112 return ::testing::AssertionFailure() 113 << "expected specFlags " << std::hex << expected_spec_flags << " but got specFlags " 114 << spec_flags << std::dec << " instead"; 115 } 116 117 ResTable::resource_name actual_name; 118 if (!table->getResourceName(expected_id.id, false, &actual_name)) { 119 return ::testing::AssertionFailure() << "failed to find resource name"; 120 } 121 122 Maybe<ResourceName> resName = ResourceUtils::ToResourceName(actual_name); 123 if (!resName) { 124 return ::testing::AssertionFailure() 125 << "expected name '" << expected_res_name << "' but got '" 126 << StringPiece16(actual_name.package, actual_name.packageLen) << ":" 127 << StringPiece16(actual_name.type, actual_name.typeLen) << "/" 128 << StringPiece16(actual_name.name, actual_name.nameLen) << "'"; 129 } 130 131 ResourceName actual_res_name(resName.value()); 132 133 if (expected_res_name.entry != actual_res_name.entry || 134 expected_res_name.package != actual_res_name.package || 135 expected_res_name.type != actual_res_name.type) { 136 return ::testing::AssertionFailure() << "expected resource '" << expected_res_name.to_string() 137 << "' but got '" << actual_res_name.to_string() << "'"; 138 } 139 140 if (expected_config != config) { 141 return ::testing::AssertionFailure() << "expected config '" << expected_config 142 << "' but got '" << ConfigDescription(config) << "'"; 143 } 144 return ::testing::AssertionSuccess(); 145 } 146 147 protected: 148 std::unique_ptr<IAaptContext> context_; 149}; 150 151TEST_F(TableFlattenerTest, FlattenFullyLinkedTable) { 152 std::unique_ptr<ResourceTable> table = 153 test::ResourceTableBuilder() 154 .SetPackageId("com.app.test", 0x7f) 155 .AddSimple("com.app.test:id/one", ResourceId(0x7f020000)) 156 .AddSimple("com.app.test:id/two", ResourceId(0x7f020001)) 157 .AddValue("com.app.test:id/three", ResourceId(0x7f020002), 158 test::BuildReference("com.app.test:id/one", ResourceId(0x7f020000))) 159 .AddValue("com.app.test:integer/one", ResourceId(0x7f030000), 160 util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u)) 161 .AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"), 162 ResourceId(0x7f030000), 163 util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u)) 164 .AddString("com.app.test:string/test", ResourceId(0x7f040000), "foo") 165 .AddString("com.app.test:layout/bar", ResourceId(0x7f050000), "res/layout/bar.xml") 166 .Build(); 167 168 ResTable res_table; 169 ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table)); 170 171 EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020000), {}, 172 Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); 173 174 EXPECT_TRUE(Exists(&res_table, "com.app.test:id/two", ResourceId(0x7f020001), {}, 175 Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); 176 177 EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three", ResourceId(0x7f020002), {}, 178 Res_value::TYPE_REFERENCE, 0x7f020000u, 0u)); 179 180 EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one", ResourceId(0x7f030000), {}, 181 Res_value::TYPE_INT_DEC, 1u, ResTable_config::CONFIG_VERSION)); 182 183 EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one", ResourceId(0x7f030000), 184 test::ParseConfigOrDie("v1"), Res_value::TYPE_INT_DEC, 2u, 185 ResTable_config::CONFIG_VERSION)); 186 187 std::u16string foo_str = u"foo"; 188 ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size()); 189 ASSERT_GE(idx, 0); 190 EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test", ResourceId(0x7f040000), {}, 191 Res_value::TYPE_STRING, (uint32_t)idx, 0u)); 192 193 std::u16string bar_path = u"res/layout/bar.xml"; 194 idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size()); 195 ASSERT_GE(idx, 0); 196 EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/bar", ResourceId(0x7f050000), {}, 197 Res_value::TYPE_STRING, (uint32_t)idx, 0u)); 198} 199 200TEST_F(TableFlattenerTest, FlattenEntriesWithGapsInIds) { 201 std::unique_ptr<ResourceTable> table = 202 test::ResourceTableBuilder() 203 .SetPackageId("com.app.test", 0x7f) 204 .AddSimple("com.app.test:id/one", ResourceId(0x7f020001)) 205 .AddSimple("com.app.test:id/three", ResourceId(0x7f020003)) 206 .Build(); 207 208 ResTable res_table; 209 ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table)); 210 211 EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020001), {}, 212 Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); 213 EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three", ResourceId(0x7f020003), {}, 214 Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); 215} 216 217TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) { 218 Attribute attr; 219 attr.type_mask = android::ResTable_map::TYPE_INTEGER; 220 attr.min_int = 10; 221 attr.max_int = 23; 222 std::unique_ptr<ResourceTable> table = 223 test::ResourceTableBuilder() 224 .SetPackageId("android", 0x01) 225 .AddValue("android:attr/foo", ResourceId(0x01010000), util::make_unique<Attribute>(attr)) 226 .Build(); 227 228 ResourceTable result; 229 ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &result)); 230 231 Attribute* actual_attr = test::GetValue<Attribute>(&result, "android:attr/foo"); 232 ASSERT_THAT(actual_attr, NotNull()); 233 EXPECT_EQ(attr.IsWeak(), actual_attr->IsWeak()); 234 EXPECT_EQ(attr.type_mask, actual_attr->type_mask); 235 EXPECT_EQ(attr.min_int, actual_attr->min_int); 236 EXPECT_EQ(attr.max_int, actual_attr->max_int); 237} 238 239static std::unique_ptr<ResourceTable> BuildTableWithSparseEntries( 240 IAaptContext* context, const ConfigDescription& sparse_config, float load) { 241 std::unique_ptr<ResourceTable> table = 242 test::ResourceTableBuilder() 243 .SetPackageId(context->GetCompilationPackage(), context->GetPackageId()) 244 .Build(); 245 246 // Add regular entries. 247 int stride = static_cast<int>(1.0f / load); 248 for (int i = 0; i < 100; i++) { 249 const ResourceName name = test::ParseNameOrDie( 250 base::StringPrintf("%s:string/foo_%d", context->GetCompilationPackage().data(), i)); 251 const ResourceId resid(context->GetPackageId(), 0x02, static_cast<uint16_t>(i)); 252 const auto value = 253 util::make_unique<BinaryPrimitive>(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(i)); 254 CHECK(table->AddResourceWithId(name, resid, ConfigDescription::DefaultConfig(), "", 255 std::unique_ptr<Value>(value->Clone(nullptr)), 256 context->GetDiagnostics())); 257 258 // Every few entries, write out a sparse_config value. This will give us the desired load. 259 if (i % stride == 0) { 260 CHECK(table->AddResourceWithId(name, resid, sparse_config, "", 261 std::unique_ptr<Value>(value->Clone(nullptr)), 262 context->GetDiagnostics())); 263 } 264 } 265 return table; 266} 267 268TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkO) { 269 std::unique_ptr<IAaptContext> context = test::ContextBuilder() 270 .SetCompilationPackage("android") 271 .SetPackageId(0x01) 272 .SetMinSdkVersion(SDK_O) 273 .Build(); 274 275 const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB"); 276 auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f); 277 278 TableFlattenerOptions options; 279 options.use_sparse_entries = true; 280 281 std::string no_sparse_contents; 282 ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents)); 283 284 std::string sparse_contents; 285 ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents)); 286 287 EXPECT_GT(no_sparse_contents.size(), sparse_contents.size()); 288 289 // Attempt to parse the sparse contents. 290 291 ResourceTable sparse_table; 292 BinaryResourceParser parser(context->GetDiagnostics(), &sparse_table, Source("test.arsc"), 293 sparse_contents.data(), sparse_contents.size()); 294 ASSERT_TRUE(parser.Parse()); 295 296 auto value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_0", 297 sparse_config); 298 ASSERT_THAT(value, NotNull()); 299 EXPECT_EQ(0u, value->value.data); 300 301 ASSERT_THAT(test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_1", 302 sparse_config), 303 IsNull()); 304 305 value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_4", 306 sparse_config); 307 ASSERT_THAT(value, NotNull()); 308 EXPECT_EQ(4u, value->value.data); 309} 310 311TEST_F(TableFlattenerTest, FlattenSparseEntryWithConfigSdkVersionO) { 312 std::unique_ptr<IAaptContext> context = test::ContextBuilder() 313 .SetCompilationPackage("android") 314 .SetPackageId(0x01) 315 .SetMinSdkVersion(SDK_LOLLIPOP) 316 .Build(); 317 318 const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB-v26"); 319 auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f); 320 321 TableFlattenerOptions options; 322 options.use_sparse_entries = true; 323 324 std::string no_sparse_contents; 325 ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents)); 326 327 std::string sparse_contents; 328 ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents)); 329 330 EXPECT_GT(no_sparse_contents.size(), sparse_contents.size()); 331} 332 333TEST_F(TableFlattenerTest, DoNotUseSparseEntryForDenseConfig) { 334 std::unique_ptr<IAaptContext> context = test::ContextBuilder() 335 .SetCompilationPackage("android") 336 .SetPackageId(0x01) 337 .SetMinSdkVersion(SDK_O) 338 .Build(); 339 340 const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB"); 341 auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.80f); 342 343 TableFlattenerOptions options; 344 options.use_sparse_entries = true; 345 346 std::string no_sparse_contents; 347 ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents)); 348 349 std::string sparse_contents; 350 ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents)); 351 352 EXPECT_EQ(no_sparse_contents.size(), sparse_contents.size()); 353} 354 355TEST_F(TableFlattenerTest, FlattenSharedLibrary) { 356 std::unique_ptr<IAaptContext> context = 357 test::ContextBuilder().SetCompilationPackage("lib").SetPackageId(0x00).Build(); 358 std::unique_ptr<ResourceTable> table = 359 test::ResourceTableBuilder() 360 .SetPackageId("lib", 0x00) 361 .AddValue("lib:id/foo", ResourceId(0x00010000), util::make_unique<Id>()) 362 .Build(); 363 ResourceTable result; 364 ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result)); 365 366 Maybe<ResourceTable::SearchResult> search_result = 367 result.FindResource(test::ParseNameOrDie("lib:id/foo")); 368 ASSERT_TRUE(search_result); 369 EXPECT_EQ(0x00u, search_result.value().package->id.value()); 370 371 auto iter = result.included_packages_.find(0x00); 372 ASSERT_NE(result.included_packages_.end(), iter); 373 EXPECT_EQ("lib", iter->second); 374} 375 376TEST_F(TableFlattenerTest, FlattenTableReferencingSharedLibraries) { 377 std::unique_ptr<IAaptContext> context = 378 test::ContextBuilder().SetCompilationPackage("app").SetPackageId(0x7f).Build(); 379 std::unique_ptr<ResourceTable> table = 380 test::ResourceTableBuilder() 381 .SetPackageId("app", 0x7f) 382 .AddValue("app:id/foo", ResourceId(0x7f010000), 383 test::BuildReference("lib_one:id/foo", ResourceId(0x02010000))) 384 .AddValue("app:id/bar", ResourceId(0x7f010001), 385 test::BuildReference("lib_two:id/bar", ResourceId(0x03010000))) 386 .Build(); 387 table->included_packages_[0x02] = "lib_one"; 388 table->included_packages_[0x03] = "lib_two"; 389 390 ResTable result; 391 ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result)); 392 393 const DynamicRefTable* dynamic_ref_table = result.getDynamicRefTableForCookie(1); 394 ASSERT_THAT(dynamic_ref_table, NotNull()); 395 396 const KeyedVector<String16, uint8_t>& entries = dynamic_ref_table->entries(); 397 398 ssize_t idx = entries.indexOfKey(android::String16("lib_one")); 399 ASSERT_GE(idx, 0); 400 EXPECT_EQ(0x02u, entries.valueAt(idx)); 401 402 idx = entries.indexOfKey(android::String16("lib_two")); 403 ASSERT_GE(idx, 0); 404 EXPECT_EQ(0x03u, entries.valueAt(idx)); 405} 406 407TEST_F(TableFlattenerTest, PackageWithNonStandardIdHasDynamicRefTable) { 408 std::unique_ptr<IAaptContext> context = 409 test::ContextBuilder().SetCompilationPackage("app").SetPackageId(0x80).Build(); 410 std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() 411 .SetPackageId("app", 0x80) 412 .AddSimple("app:id/foo", ResourceId(0x80010000)) 413 .Build(); 414 415 ResTable result; 416 ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result)); 417 418 const DynamicRefTable* dynamic_ref_table = result.getDynamicRefTableForCookie(1); 419 ASSERT_THAT(dynamic_ref_table, NotNull()); 420 421 const KeyedVector<String16, uint8_t>& entries = dynamic_ref_table->entries(); 422 ssize_t idx = entries.indexOfKey(android::String16("app")); 423 ASSERT_GE(idx, 0); 424 EXPECT_EQ(0x80u, entries.valueAt(idx)); 425} 426 427TEST_F(TableFlattenerTest, LongPackageNameIsTruncated) { 428 std::string kPackageName(256, 'F'); 429 430 std::unique_ptr<IAaptContext> context = 431 test::ContextBuilder().SetCompilationPackage(kPackageName).SetPackageId(0x7f).Build(); 432 std::unique_ptr<ResourceTable> table = 433 test::ResourceTableBuilder() 434 .SetPackageId(kPackageName, 0x7f) 435 .AddSimple(kPackageName + ":id/foo", ResourceId(0x7f010000)) 436 .Build(); 437 438 ResTable result; 439 ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result)); 440 441 ASSERT_EQ(1u, result.getBasePackageCount()); 442 EXPECT_EQ(127u, result.getBasePackageName(0).size()); 443} 444 445TEST_F(TableFlattenerTest, LongSharedLibraryPackageNameIsIllegal) { 446 std::string kPackageName(256, 'F'); 447 448 std::unique_ptr<IAaptContext> context = test::ContextBuilder() 449 .SetCompilationPackage(kPackageName) 450 .SetPackageId(0x7f) 451 .SetPackageType(PackageType::kSharedLib) 452 .Build(); 453 std::unique_ptr<ResourceTable> table = 454 test::ResourceTableBuilder() 455 .SetPackageId(kPackageName, 0x7f) 456 .AddSimple(kPackageName + ":id/foo", ResourceId(0x7f010000)) 457 .Build(); 458 459 ResTable result; 460 ASSERT_FALSE(Flatten(context.get(), {}, table.get(), &result)); 461} 462 463TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoWhitelistSucceeds) { 464 std::unique_ptr<ResourceTable> table = 465 test::ResourceTableBuilder() 466 .SetPackageId("com.app.test", 0x7f) 467 .AddSimple("com.app.test:id/one", ResourceId(0x7f020000)) 468 .AddSimple("com.app.test:id/two", ResourceId(0x7f020001)) 469 .AddValue("com.app.test:id/three", ResourceId(0x7f020002), 470 test::BuildReference("com.app.test:id/one", ResourceId(0x7f020000))) 471 .AddValue("com.app.test:integer/one", ResourceId(0x7f030000), 472 util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u)) 473 .AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"), 474 ResourceId(0x7f030000), 475 util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u)) 476 .AddString("com.app.test:string/test", ResourceId(0x7f040000), "foo") 477 .AddString("com.app.test:layout/bar", ResourceId(0x7f050000), "res/layout/bar.xml") 478 .Build(); 479 480 TableFlattenerOptions options; 481 options.collapse_key_stringpool = true; 482 483 ResTable res_table; 484 485 ASSERT_TRUE(Flatten(context_.get(), options, table.get(), &res_table)); 486 487 EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated", 488 ResourceId(0x7f020000), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); 489 490 EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated", 491 ResourceId(0x7f020001), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); 492 493 EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated", 494 ResourceId(0x7f020002), {}, Res_value::TYPE_REFERENCE, 0x7f020000u, 0u)); 495 496 EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated", 497 ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u, 498 ResTable_config::CONFIG_VERSION)); 499 500 EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated", 501 ResourceId(0x7f030000), test::ParseConfigOrDie("v1"), Res_value::TYPE_INT_DEC, 502 2u, ResTable_config::CONFIG_VERSION)); 503 504 std::u16string foo_str = u"foo"; 505 ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size()); 506 ASSERT_GE(idx, 0); 507 EXPECT_TRUE(Exists(&res_table, "com.app.test:string/0_resource_name_obfuscated", 508 ResourceId(0x7f040000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u)); 509 510 std::u16string bar_path = u"res/layout/bar.xml"; 511 idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size()); 512 ASSERT_GE(idx, 0); 513 EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/0_resource_name_obfuscated", 514 ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u)); 515} 516 517TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithWhitelistSucceeds) { 518 std::unique_ptr<ResourceTable> table = 519 test::ResourceTableBuilder() 520 .SetPackageId("com.app.test", 0x7f) 521 .AddSimple("com.app.test:id/one", ResourceId(0x7f020000)) 522 .AddSimple("com.app.test:id/two", ResourceId(0x7f020001)) 523 .AddValue("com.app.test:id/three", ResourceId(0x7f020002), 524 test::BuildReference("com.app.test:id/one", ResourceId(0x7f020000))) 525 .AddValue("com.app.test:integer/one", ResourceId(0x7f030000), 526 util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u)) 527 .AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"), 528 ResourceId(0x7f030000), 529 util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u)) 530 .AddString("com.app.test:string/test", ResourceId(0x7f040000), "foo") 531 .AddString("com.app.test:layout/bar", ResourceId(0x7f050000), "res/layout/bar.xml") 532 .Build(); 533 534 TableFlattenerOptions options; 535 options.collapse_key_stringpool = true; 536 options.whitelisted_resources.insert("test"); 537 options.whitelisted_resources.insert("three"); 538 ResTable res_table; 539 540 ASSERT_TRUE(Flatten(context_.get(), options, table.get(), &res_table)); 541 542 EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated", 543 ResourceId(0x7f020000), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); 544 545 EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated", 546 ResourceId(0x7f020001), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); 547 548 EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three", ResourceId(0x7f020002), {}, 549 Res_value::TYPE_REFERENCE, 0x7f020000u, 0u)); 550 551 EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated", 552 ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u, 553 ResTable_config::CONFIG_VERSION)); 554 555 EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated", 556 ResourceId(0x7f030000), test::ParseConfigOrDie("v1"), Res_value::TYPE_INT_DEC, 557 2u, ResTable_config::CONFIG_VERSION)); 558 559 std::u16string foo_str = u"foo"; 560 ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size()); 561 ASSERT_GE(idx, 0); 562 EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test", ResourceId(0x7f040000), {}, 563 Res_value::TYPE_STRING, (uint32_t)idx, 0u)); 564 565 std::u16string bar_path = u"res/layout/bar.xml"; 566 idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size()); 567 ASSERT_GE(idx, 0); 568 EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/0_resource_name_obfuscated", 569 ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u)); 570} 571 572TEST_F(TableFlattenerTest, FlattenOverlayable) { 573 std::unique_ptr<ResourceTable> table = 574 test::ResourceTableBuilder() 575 .SetPackageId("com.app.test", 0x7f) 576 .AddSimple("com.app.test:integer/overlayable", ResourceId(0x7f020000)) 577 .Build(); 578 579 ASSERT_TRUE(table->SetOverlayable(test::ParseNameOrDie("com.app.test:integer/overlayable"), 580 Overlayable{}, test::GetDiagnostics())); 581 582 ResTable res_table; 583 ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table)); 584 585 const StringPiece16 overlayable_name(u"com.app.test:integer/overlayable"); 586 uint32_t spec_flags = 0u; 587 ASSERT_THAT(res_table.identifierForName(overlayable_name.data(), overlayable_name.size(), nullptr, 588 0u, nullptr, 0u, &spec_flags), 589 Gt(0u)); 590 EXPECT_TRUE(spec_flags & android::ResTable_typeSpec::SPEC_OVERLAYABLE); 591} 592 593} // namespace aapt 594