1/* 2 * Copyright (C) 2014 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 "androidfw/ResourceTypes.h" 18 19#include <codecvt> 20#include <locale> 21#include <string> 22 23#include "utils/String16.h" 24#include "utils/String8.h" 25 26#include "TestHelpers.h" 27#include "data/basic/R.h" 28#include "data/lib_one/R.h" 29 30namespace basic = com::android::basic; 31namespace lib = com::android::lib_one; 32 33namespace android { 34 35TEST(ResTableTest, ShouldLoadSuccessfully) { 36 std::string contents; 37 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 38 "resources.arsc", &contents)); 39 40 ResTable table; 41 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 42} 43 44TEST(ResTableTest, ShouldLoadSparseEntriesSuccessfully) { 45 std::string contents; 46 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc", 47 &contents)); 48 49 ResTable table; 50 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 51 52 ResTable_config config; 53 memset(&config, 0, sizeof(config)); 54 config.sdkVersion = 26; 55 table.setParameters(&config); 56 57 String16 name(u"com.android.sparse:integer/foo_9"); 58 uint32_t flags; 59 uint32_t resid = 60 table.identifierForName(name.string(), name.size(), nullptr, 0, nullptr, 0, &flags); 61 ASSERT_NE(0u, resid); 62 63 Res_value val; 64 ResTable_config selected_config; 65 ASSERT_GE( 66 table.getResource(resid, &val, false /*mayBeBag*/, 0u /*density*/, &flags, &selected_config), 67 0); 68 EXPECT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 69 EXPECT_EQ(900u, val.data); 70} 71 72TEST(ResTableTest, SimpleTypeIsRetrievedCorrectly) { 73 std::string contents; 74 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 75 "resources.arsc", &contents)); 76 77 ResTable table; 78 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 79 80 EXPECT_TRUE(IsStringEqual(table, basic::R::string::test1, "test1")); 81} 82 83TEST(ResTableTest, ResourceNameIsResolved) { 84 std::string contents; 85 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 86 "resources.arsc", &contents)); 87 88 ResTable table; 89 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 90 91 String16 defPackage("com.android.basic"); 92 String16 testName("@string/test1"); 93 uint32_t resID = 94 table.identifierForName(testName.string(), testName.size(), 0, 0, 95 defPackage.string(), defPackage.size()); 96 ASSERT_NE(uint32_t(0x00000000), resID); 97 ASSERT_EQ(basic::R::string::test1, resID); 98} 99 100TEST(ResTableTest, NoParentThemeIsAppliedCorrectly) { 101 std::string contents; 102 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 103 "resources.arsc", &contents)); 104 105 ResTable table; 106 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 107 108 ResTable::Theme theme(table); 109 ASSERT_EQ(NO_ERROR, theme.applyStyle(basic::R::style::Theme1)); 110 111 Res_value val; 112 uint32_t specFlags = 0; 113 ssize_t index = theme.getAttribute(basic::R::attr::attr1, &val, &specFlags); 114 ASSERT_GE(index, 0); 115 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 116 ASSERT_EQ(uint32_t(100), val.data); 117 118 index = theme.getAttribute(basic::R::attr::attr2, &val, &specFlags); 119 ASSERT_GE(index, 0); 120 ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); 121 ASSERT_EQ(basic::R::integer::number1, val.data); 122} 123 124TEST(ResTableTest, ParentThemeIsAppliedCorrectly) { 125 std::string contents; 126 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 127 "resources.arsc", &contents)); 128 129 ResTable table; 130 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 131 132 ResTable::Theme theme(table); 133 ASSERT_EQ(NO_ERROR, theme.applyStyle(basic::R::style::Theme2)); 134 135 Res_value val; 136 uint32_t specFlags = 0; 137 ssize_t index = theme.getAttribute(basic::R::attr::attr1, &val, &specFlags); 138 ASSERT_GE(index, 0); 139 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 140 ASSERT_EQ(uint32_t(300), val.data); 141 142 index = theme.getAttribute(basic::R::attr::attr2, &val, &specFlags); 143 ASSERT_GE(index, 0); 144 ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); 145 ASSERT_EQ(basic::R::integer::number1, val.data); 146} 147 148TEST(ResTableTest, LibraryThemeIsAppliedCorrectly) { 149 std::string contents; 150 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", 151 "resources.arsc", &contents)); 152 153 ResTable table; 154 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 155 156 ResTable::Theme theme(table); 157 ASSERT_EQ(NO_ERROR, theme.applyStyle(lib::R::style::Theme)); 158 159 Res_value val; 160 uint32_t specFlags = 0; 161 ssize_t index = theme.getAttribute(lib::R::attr::attr1, &val, &specFlags); 162 ASSERT_GE(index, 0); 163 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 164 ASSERT_EQ(uint32_t(700), val.data); 165 166 index = theme.getAttribute(lib::R::attr::attr2, &val, &specFlags); 167 ASSERT_GE(index, 0); 168 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 169 ASSERT_EQ(uint32_t(700), val.data); 170} 171 172TEST(ResTableTest, ReferenceToBagIsNotResolved) { 173 std::string contents; 174 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 175 "resources.arsc", &contents)); 176 177 ResTable table; 178 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 179 180 Res_value val; 181 ssize_t block = 182 table.getResource(basic::R::integer::number2, &val, MAY_NOT_BE_BAG); 183 ASSERT_GE(block, 0); 184 ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); 185 ASSERT_EQ(basic::R::array::integerArray1, val.data); 186 187 ssize_t newBlock = table.resolveReference(&val, block); 188 EXPECT_EQ(block, newBlock); 189 EXPECT_EQ(Res_value::TYPE_REFERENCE, val.dataType); 190 EXPECT_EQ(basic::R::array::integerArray1, val.data); 191} 192 193TEST(ResTableTest, ResourcesStillAccessibleAfterParameterChange) { 194 std::string contents; 195 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 196 "resources.arsc", &contents)); 197 198 ResTable table; 199 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 200 201 Res_value val; 202 ssize_t block = 203 table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); 204 ASSERT_GE(block, 0); 205 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 206 207 const ResTable::bag_entry* entry; 208 ssize_t count = table.lockBag(basic::R::array::integerArray1, &entry); 209 ASSERT_GE(count, 0); 210 table.unlockBag(entry); 211 212 ResTable_config param; 213 memset(¶m, 0, sizeof(param)); 214 param.density = 320; 215 table.setParameters(¶m); 216 217 block = table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); 218 ASSERT_GE(block, 0); 219 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 220 221 count = table.lockBag(basic::R::array::integerArray1, &entry); 222 ASSERT_GE(count, 0); 223 table.unlockBag(entry); 224} 225 226TEST(ResTableTest, ResourceIsOverridenWithBetterConfig) { 227 std::string contents; 228 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 229 "resources.arsc", &contents)); 230 231 ResTable table; 232 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 233 234 Res_value val; 235 ssize_t block = 236 table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); 237 ASSERT_GE(block, 0); 238 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 239 ASSERT_EQ(uint32_t(200), val.data); 240 241 ResTable_config param; 242 memset(¶m, 0, sizeof(param)); 243 param.language[0] = 's'; 244 param.language[1] = 'v'; 245 param.country[0] = 'S'; 246 param.country[1] = 'E'; 247 table.setParameters(¶m); 248 249 block = table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); 250 ASSERT_GE(block, 0); 251 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 252 ASSERT_EQ(uint32_t(400), val.data); 253} 254 255TEST(ResTableTest, emptyTableHasSensibleDefaults) { 256 const int32_t assetCookie = 1; 257 258 ResTable table; 259 ASSERT_EQ(NO_ERROR, table.addEmpty(assetCookie)); 260 261 // Adding an empty table gives us one table! 262 ASSERT_EQ(uint32_t(1), table.getTableCount()); 263 264 // Adding an empty table doesn't mean we get packages. 265 ASSERT_EQ(uint32_t(0), table.getBasePackageCount()); 266 267 Res_value val; 268 ASSERT_LT(table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG), 269 0); 270} 271 272void testU16StringToInt(const char16_t* str, uint32_t expectedValue, 273 bool expectSuccess, bool expectHex) { 274 size_t len = std::char_traits<char16_t>::length(str); 275 276 // Gtest can't print UTF-16 strings, so we have to convert to UTF-8 :( 277 std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert; 278 std::string s = convert.to_bytes(std::u16string(str, len)); 279 280 Res_value out = {}; 281 ASSERT_EQ(expectSuccess, U16StringToInt(str, len, &out)) << "Failed with " 282 << s; 283 284 if (!expectSuccess) { 285 ASSERT_EQ(out.TYPE_NULL, out.dataType) << "Failed with " << s; 286 return; 287 } 288 289 if (expectHex) { 290 ASSERT_EQ(out.TYPE_INT_HEX, out.dataType) << "Failed with " << s; 291 } else { 292 ASSERT_EQ(out.TYPE_INT_DEC, out.dataType) << "Failed with " << s; 293 } 294 295 ASSERT_EQ(expectedValue, out.data) << "Failed with " << s; 296} 297 298TEST(ResTableTest, U16StringToInt) { 299 testU16StringToInt(u"", 0U, false, false); 300 testU16StringToInt(u" ", 0U, false, false); 301 testU16StringToInt(u"\t\n", 0U, false, false); 302 303 testU16StringToInt(u"abcd", 0U, false, false); 304 testU16StringToInt(u"10abcd", 0U, false, false); 305 testU16StringToInt(u"42 42", 0U, false, false); 306 testU16StringToInt(u"- 42", 0U, false, false); 307 testU16StringToInt(u"-", 0U, false, false); 308 309 testU16StringToInt(u"0x", 0U, false, true); 310 testU16StringToInt(u"0xnope", 0U, false, true); 311 testU16StringToInt(u"0X42", 0U, false, true); 312 testU16StringToInt(u"0x42 0x42", 0U, false, true); 313 testU16StringToInt(u"-0x0", 0U, false, true); 314 testU16StringToInt(u"-0x42", 0U, false, true); 315 testU16StringToInt(u"- 0x42", 0U, false, true); 316 317 // Note that u" 42" would pass. This preserves the old behavior, but it may 318 // not be desired. 319 testU16StringToInt(u"42 ", 0U, false, false); 320 testU16StringToInt(u"0x42 ", 0U, false, true); 321 322 // Decimal cases. 323 testU16StringToInt(u"0", 0U, true, false); 324 testU16StringToInt(u"-0", 0U, true, false); 325 testU16StringToInt(u"42", 42U, true, false); 326 testU16StringToInt(u" 42", 42U, true, false); 327 testU16StringToInt(u"-42", static_cast<uint32_t>(-42), true, false); 328 testU16StringToInt(u" -42", static_cast<uint32_t>(-42), true, false); 329 testU16StringToInt(u"042", 42U, true, false); 330 testU16StringToInt(u"-042", static_cast<uint32_t>(-42), true, false); 331 332 // Hex cases. 333 testU16StringToInt(u"0x0", 0x0, true, true); 334 testU16StringToInt(u"0x42", 0x42, true, true); 335 testU16StringToInt(u" 0x42", 0x42, true, true); 336 337 // Just before overflow cases: 338 testU16StringToInt(u"2147483647", INT_MAX, true, false); 339 testU16StringToInt(u"-2147483648", static_cast<uint32_t>(INT_MIN), true, 340 false); 341 testU16StringToInt(u"0xffffffff", UINT_MAX, true, true); 342 343 // Overflow cases: 344 testU16StringToInt(u"2147483648", 0U, false, false); 345 testU16StringToInt(u"-2147483649", 0U, false, false); 346 testU16StringToInt(u"0x1ffffffff", 0U, false, true); 347} 348 349TEST(ResTableTest, ShareButDontModifyResTable) { 350 std::string contents; 351 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 352 "resources.arsc", &contents)); 353 354 ResTable sharedTable; 355 ASSERT_EQ(NO_ERROR, sharedTable.add(contents.data(), contents.size())); 356 357 ResTable_config param; 358 memset(¶m, 0, sizeof(param)); 359 param.language[0] = 'v'; 360 param.language[1] = 's'; 361 sharedTable.setParameters(¶m); 362 363 // Check that we get the default value for @integer:number1 364 Res_value val; 365 ssize_t block = 366 sharedTable.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); 367 ASSERT_GE(block, 0); 368 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 369 ASSERT_EQ(uint32_t(600), val.data); 370 371 // Create a new table that shares the entries of the shared table. 372 ResTable table; 373 ASSERT_EQ(NO_ERROR, table.add(&sharedTable, false)); 374 375 // Set a new configuration on the new table. 376 memset(¶m, 0, sizeof(param)); 377 param.language[0] = 's'; 378 param.language[1] = 'v'; 379 param.country[0] = 'S'; 380 param.country[1] = 'E'; 381 table.setParameters(¶m); 382 383 // Check that we get a new value in the new table. 384 block = table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); 385 ASSERT_GE(block, 0); 386 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 387 ASSERT_EQ(uint32_t(400), val.data); 388 389 // Check that we still get the old value in the shared table. 390 block = 391 sharedTable.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG); 392 ASSERT_GE(block, 0); 393 ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); 394 ASSERT_EQ(uint32_t(600), val.data); 395} 396 397TEST(ResTableTest, GetConfigurationsReturnsUniqueList) { 398 std::string contents; 399 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", 400 "resources.arsc", &contents)); 401 402 std::string system_contents; 403 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/system/system.apk", 404 "resources.arsc", &system_contents)); 405 406 ResTable table; 407 ASSERT_EQ(NO_ERROR, 408 table.add(system_contents.data(), system_contents.size())); 409 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 410 411 ResTable_config configSv; 412 memset(&configSv, 0, sizeof(configSv)); 413 configSv.language[0] = 's'; 414 configSv.language[1] = 'v'; 415 416 Vector<ResTable_config> configs; 417 table.getConfigurations(&configs); 418 419 EXPECT_EQ(1, std::count(configs.begin(), configs.end(), configSv)); 420 421 Vector<String8> locales; 422 table.getLocales(&locales); 423 424 EXPECT_EQ(1, std::count(locales.begin(), locales.end(), String8("sv"))); 425} 426 427TEST(ResTableTest, TruncatedEncodeLength) { 428 std::string contents; 429 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/length_decode/length_decode_valid.apk", 430 "resources.arsc", &contents)); 431 432 ResTable table; 433 ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size())); 434 435 Res_value val; 436 ssize_t block = table.getResource(0x7f010001, &val, MAY_NOT_BE_BAG); 437 ASSERT_GE(block, 0); 438 ASSERT_EQ(Res_value::TYPE_STRING, val.dataType); 439 440 const ResStringPool* pool = table.getTableStringBlock(block); 441 ASSERT_TRUE(pool != NULL); 442 ASSERT_LT(val.data, pool->size()); 443 444 // Make sure a string with a truncated length is read to its correct length 445 size_t str_len; 446 const char* target_str8 = pool->string8At(val.data, &str_len); 447 ASSERT_TRUE(target_str8 != NULL); 448 ASSERT_EQ(size_t(40076), String8(target_str8, str_len).size()); 449 ASSERT_EQ(target_str8[40075], ']'); 450 451 const char16_t* target_str16 = pool->stringAt(val.data, &str_len); 452 ASSERT_TRUE(target_str16 != NULL); 453 ASSERT_EQ(size_t(40076), String16(target_str16, str_len).size()); 454 ASSERT_EQ(target_str8[40075], (char16_t) ']'); 455 456 // Load an edited apk with the null terminator removed from the end of the 457 // string 458 std::string invalid_contents; 459 ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/length_decode/length_decode_invalid.apk", 460 "resources.arsc", &invalid_contents)); 461 ResTable invalid_table; 462 ASSERT_EQ(NO_ERROR, invalid_table.add(invalid_contents.data(), invalid_contents.size())); 463 464 Res_value invalid_val; 465 ssize_t invalid_block = invalid_table.getResource(0x7f010001, &invalid_val, MAY_NOT_BE_BAG); 466 ASSERT_GE(invalid_block, 0); 467 ASSERT_EQ(Res_value::TYPE_STRING, invalid_val.dataType); 468 469 const ResStringPool* invalid_pool = invalid_table.getTableStringBlock(invalid_block); 470 ASSERT_TRUE(invalid_pool != NULL); 471 ASSERT_LT(invalid_val.data, invalid_pool->size()); 472 473 // Make sure a string with a truncated length that is not null terminated errors 474 // and does not return the string 475 ASSERT_TRUE(invalid_pool->string8At(invalid_val.data, &str_len) == NULL); 476 ASSERT_TRUE(invalid_pool->stringAt(invalid_val.data, &str_len) == NULL); 477} 478 479} // namespace android 480