1 package org.robolectric.res.android; 2 3 import static org.robolectric.res.android.Errors.NO_ERROR; 4 import static org.robolectric.res.android.Errors.NO_INIT; 5 import static org.robolectric.res.android.ResourceTypes.RES_STRING_POOL_TYPE; 6 import static org.robolectric.res.android.ResourceTypes.RES_TABLE_LIBRARY_TYPE; 7 import static org.robolectric.res.android.ResourceTypes.RES_TABLE_PACKAGE_TYPE; 8 import static org.robolectric.res.android.ResourceTypes.RES_TABLE_TYPE; 9 import static org.robolectric.res.android.ResourceTypes.RES_TABLE_TYPE_SPEC_TYPE; 10 import static org.robolectric.res.android.ResourceTypes.RES_TABLE_TYPE_TYPE; 11 import static org.robolectric.res.android.ResourceTypes.kResTableTypeMinSize; 12 import static org.robolectric.res.android.ResourceUtils.make_resid; 13 import static org.robolectric.res.android.Util.UNLIKELY; 14 import static org.robolectric.res.android.Util.dtohl; 15 import static org.robolectric.res.android.Util.dtohs; 16 import static org.robolectric.res.android.Util.isTruthy; 17 import static org.robolectric.res.android.Util.logError; 18 import static org.robolectric.res.android.Util.logWarning; 19 20 import java.util.ArrayList; 21 import java.util.HashMap; 22 import java.util.List; 23 import java.util.Map; 24 import java.util.Map.Entry; 25 import java.util.Set; 26 import org.robolectric.res.android.Chunk.Iterator; 27 import org.robolectric.res.android.Idmap.LoadedIdmap; 28 import org.robolectric.res.android.ResourceTypes.IdmapEntry_header; 29 import org.robolectric.res.android.ResourceTypes.ResStringPool_header; 30 import org.robolectric.res.android.ResourceTypes.ResTable_entry; 31 import org.robolectric.res.android.ResourceTypes.ResTable_header; 32 import org.robolectric.res.android.ResourceTypes.ResTable_lib_entry; 33 import org.robolectric.res.android.ResourceTypes.ResTable_lib_header; 34 import org.robolectric.res.android.ResourceTypes.ResTable_map; 35 import org.robolectric.res.android.ResourceTypes.ResTable_map_entry; 36 import org.robolectric.res.android.ResourceTypes.ResTable_package; 37 import org.robolectric.res.android.ResourceTypes.ResTable_sparseTypeEntry; 38 import org.robolectric.res.android.ResourceTypes.ResTable_type; 39 import org.robolectric.res.android.ResourceTypes.ResTable_typeSpec; 40 import org.robolectric.res.android.ResourceTypes.Res_value; 41 42 // transliterated from https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/include/androidfw/LoadedArsc.h 43 // and https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/LoadedArsc.cpp 44 public class LoadedArsc { 45 46 //#ifndef LOADEDARSC_H_ 47 //#define LOADEDARSC_H_ 48 // 49 //#include <memory> 50 //#include <set> 51 //#include <vector> 52 // 53 //#include "android-base/macros.h" 54 // 55 //#include "androidfw/ByteBucketArray.h" 56 //#include "androidfw/Chunk.h" 57 //#include "androidfw/ResourceTypes.h" 58 //#include "androidfw/Util.h" 59 // 60 //namespace android { 61 // 62 static class DynamicPackageEntry { 63 64 // public: 65 // 66 // DynamicPackageEntry() =default; 67 DynamicPackageEntry(String package_name, int package_id)68 DynamicPackageEntry(String package_name, int package_id) { 69 this.package_name = package_name; 70 this.package_id = package_id; 71 } 72 73 String package_name; 74 int package_id = 0; 75 } 76 77 // TypeSpec is going to be immediately proceeded by 78 // an array of Type structs, all in the same block of memory. 79 static class TypeSpec { 80 81 public static final int SIZEOF = ResTable_typeSpec.SIZEOF + IdmapEntry_header.SIZEOF; 82 83 // Pointer to the mmapped data where flags are kept. 84 // Flags denote whether the resource entry is public 85 // and under which configurations it varies. 86 ResTable_typeSpec type_spec; 87 88 // Pointer to the mmapped data where the IDMAP mappings for this type 89 // exist. May be nullptr if no IDMAP exists. 90 IdmapEntry_header idmap_entries; 91 92 // The number of types that follow this struct. 93 // There is a type for each configuration that entries are defined for. 94 int type_count; 95 96 // Trick to easily access a variable number of Type structs 97 // proceeding this struct, and to ensure their alignment. 98 // ResTable_type* types[0]; 99 ResTable_type[] types; 100 GetFlagsForEntryIndex(int entry_index)101 int GetFlagsForEntryIndex(int entry_index) { 102 if (entry_index >= dtohl(type_spec.entryCount)) { 103 return 0; 104 } 105 106 // uint32_t* flags = reinterpret_cast<uint32_t*>(type_spec + 1); 107 int[] flags = type_spec.getSpecFlags(); 108 return flags[entry_index]; 109 } 110 } 111 112 // Returns the string pool where all string resource values 113 // (Res_value::dataType == Res_value::TYPE_STRING) are indexed. GetStringPool()114 public ResStringPool GetStringPool() { 115 return global_string_pool_; 116 } 117 118 // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. GetPackages()119 List<LoadedPackage> GetPackages() { 120 return packages_; 121 } 122 123 // Returns true if this is a system provided resource. IsSystem()124 boolean IsSystem() { 125 return system_; 126 } 127 128 // 129 // private: 130 // DISALLOW_COPY_AND_ASSIGN(LoadedArsc); 131 // 132 // LoadedArsc() = default; 133 // bool LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library); 134 // 135 final ResStringPool global_string_pool_ = new ResStringPool(); 136 final List<LoadedPackage> packages_ = new ArrayList<>(); 137 boolean system_ = false; 138 //}; 139 // 140 //} // namespace android 141 // 142 //#endif /* LOADEDARSC_H_ */ 143 144 // #define ATRACE_TAG ATRACE_TAG_RESOURCES 145 // 146 // #include "androidfw/LoadedArsc.h" 147 // 148 // #include <cstddef> 149 // #include <limits> 150 // 151 // #include "android-base/logging.h" 152 // #include "android-base/stringprintf.h" 153 // #include "utils/ByteOrder.h" 154 // #include "utils/Trace.h" 155 // 156 // #ifdef _WIN32 157 // #ifdef ERROR 158 // #undef ERROR 159 // #endif 160 // #endif 161 // 162 // #include "androidfw/ByteBucketArray.h" 163 // #include "androidfw/Chunk.h" 164 // #include "androidfw/ResourceUtils.h" 165 // #include "androidfw/Util.h" 166 // 167 // using android::base::StringPrintf; 168 // 169 // namespace android { 170 171 static final int kAppPackageId = 0x7f; 172 173 174 // namespace { 175 176 // Builder that helps accumulate Type structs and then create a single 177 // contiguous block of memory to store both the TypeSpec struct and 178 // the Type structs. 179 static class TypeSpecPtrBuilder { 180 // public: TypeSpecPtrBuilder(ResTable_typeSpec header, IdmapEntry_header idmap_header)181 TypeSpecPtrBuilder(ResTable_typeSpec header, IdmapEntry_header idmap_header) { 182 this.header_ = header; 183 this.idmap_header_ = idmap_header; 184 } 185 AddType(ResTable_type type)186 void AddType(ResTable_type type) { 187 types_.add(type); 188 } 189 Build()190 TypeSpec Build() { 191 // Check for overflow. 192 // using ElementType = ResTable_type*; 193 // if ((std.numeric_limits<size_t>.max() - sizeof(TypeSpec)) / sizeof(ElementType) < 194 // types_.size()) { 195 if ((Integer.MAX_VALUE - TypeSpec.SIZEOF) / 4 < types_.size()) { 196 return null; // {} ; 197 } 198 // TypeSpec* type_spec = 199 // (TypeSpec*).malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType))); 200 TypeSpec type_spec = new TypeSpec(); 201 type_spec.types = new ResTable_type[types_.size()]; 202 type_spec.type_spec = header_; 203 type_spec.idmap_entries = idmap_header_; 204 type_spec.type_count = types_.size(); 205 // memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType)); 206 for (int i = 0; i < type_spec.types.length; i++) { 207 type_spec.types[i] = types_.get(i); 208 209 } 210 return type_spec; 211 } 212 213 // private: 214 // DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder); 215 216 ResTable_typeSpec header_; 217 IdmapEntry_header idmap_header_; 218 final List<ResTable_type> types_ = new ArrayList<>(); 219 }; 220 221 // } // namespace 222 223 // Precondition: The header passed in has already been verified, so reading any fields and trusting 224 // the ResChunk_header is safe. VerifyResTableType(ResTable_type header)225 static boolean VerifyResTableType(ResTable_type header) { 226 if (header.id == 0) { 227 logError("RES_TABLE_TYPE_TYPE has invalid ID 0."); 228 return false; 229 } 230 231 int entry_count = dtohl(header.entryCount); 232 // if (entry_count > std.numeric_limits<uint16_t>.max()) { 233 if (entry_count > 0xffff) { 234 logError("RES_TABLE_TYPE_TYPE has too many entries (" + entry_count + ")."); 235 return false; 236 } 237 238 // Make sure that there is enough room for the entry offsets. 239 int offsets_offset = dtohs(header.header.headerSize); 240 int entries_offset = dtohl(header.entriesStart); 241 int offsets_length = 4 * entry_count; 242 243 if (offsets_offset > entries_offset || entries_offset - offsets_offset < offsets_length) { 244 logError("RES_TABLE_TYPE_TYPE entry offsets overlap actual entry data."); 245 return false; 246 } 247 248 if (entries_offset > dtohl(header.header.size)) { 249 logError("RES_TABLE_TYPE_TYPE entry offsets extend beyond chunk."); 250 return false; 251 } 252 253 if (isTruthy(entries_offset & 0x03)) { 254 logError("RES_TABLE_TYPE_TYPE entries start at unaligned address."); 255 return false; 256 } 257 return true; 258 } 259 VerifyResTableEntry(ResTable_type type, int entry_offset)260 static boolean VerifyResTableEntry(ResTable_type type, int entry_offset) { 261 // Check that the offset is aligned. 262 if (isTruthy(entry_offset & 0x03)) { 263 logError("Entry at offset " + entry_offset + " is not 4-byte aligned."); 264 return false; 265 } 266 267 // Check that the offset doesn't overflow. 268 // if (entry_offset > std.numeric_limits<int>.max() - dtohl(type.entriesStart)) { 269 if (entry_offset > Integer.MAX_VALUE - dtohl(type.entriesStart)) { 270 // Overflow in offset. 271 logError("Entry at offset " + entry_offset + " is too large."); 272 return false; 273 } 274 275 int chunk_size = dtohl(type.header.size); 276 277 entry_offset += dtohl(type.entriesStart); 278 if (entry_offset > chunk_size - ResTable_entry.SIZEOF) { 279 logError("Entry at offset " + entry_offset 280 + " is too large. No room for ResTable_entry."); 281 return false; 282 } 283 284 // ResTable_entry* entry = reinterpret_cast<ResTable_entry*>( 285 // reinterpret_cast<uint8_t*>(type) + entry_offset); 286 ResTable_entry entry = new ResTable_entry(type.myBuf(), type.myOffset() + entry_offset); 287 288 int entry_size = dtohs(entry.size); 289 // if (entry_size < sizeof(*entry)) { 290 if (entry_size < ResTable_entry.SIZEOF) { 291 logError("ResTable_entry size " + entry_size + " at offset " + entry_offset 292 + " is too small."); 293 return false; 294 } 295 296 if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) { 297 logError("ResTable_entry size " + entry_size + " at offset " + entry_offset 298 + " is too large."); 299 return false; 300 } 301 302 if (entry_size < ResTable_map_entry.BASE_SIZEOF) { 303 // There needs to be room for one Res_value struct. 304 if (entry_offset + entry_size > chunk_size - Res_value.SIZEOF) { 305 logError("No room for Res_value after ResTable_entry at offset " + entry_offset 306 + " for type " + (int) type.id + "."); 307 return false; 308 } 309 310 // Res_value value = 311 // reinterpret_cast<Res_value*>(reinterpret_cast<uint8_t*>(entry) + entry_size); 312 Res_value value = 313 new Res_value(entry.myBuf(), entry.myOffset() + ResTable_entry.SIZEOF); 314 int value_size = dtohs(value.size); 315 if (value_size < Res_value.SIZEOF) { 316 logError("Res_value at offset " + entry_offset + " is too small."); 317 return false; 318 } 319 320 if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) { 321 logError("Res_value size " + value_size + " at offset " + entry_offset 322 + " is too large."); 323 return false; 324 } 325 } else { 326 ResTable_map_entry map = new ResTable_map_entry(entry.myBuf(), entry.myOffset()); 327 int map_entry_count = dtohl(map.count); 328 int map_entries_start = entry_offset + entry_size; 329 if (isTruthy(map_entries_start & 0x03)) { 330 logError("Map entries at offset " + entry_offset + " start at unaligned offset."); 331 return false; 332 } 333 334 // Each entry is sizeof(ResTable_map) big. 335 if (map_entry_count > ((chunk_size - map_entries_start) / ResTable_map.SIZEOF)) { 336 logError("Too many map entries in ResTable_map_entry at offset " + entry_offset + "."); 337 return false; 338 } 339 } 340 return true; 341 } 342 343 static class LoadedPackage { 344 // private: 345 346 // DISALLOW_COPY_AND_ASSIGN(LoadedPackage); 347 348 // LoadedPackage(); 349 350 ResStringPool type_string_pool_ = new ResStringPool(); 351 ResStringPool key_string_pool_ = new ResStringPool(); 352 String package_name_; 353 int package_id_ = -1; 354 int type_id_offset_ = 0; 355 boolean dynamic_ = false; 356 boolean system_ = false; 357 boolean overlay_ = false; 358 359 // final ByteBucketArray<TypeSpec> type_specs_ = new ByteBucketArray<TypeSpec>() { 360 // @Override 361 // TypeSpec newInstance() { 362 // return new TypeSpec(); 363 // } 364 // }; 365 final Map<Integer, TypeSpec> type_specs_ = new HashMap<>(); 366 final List<DynamicPackageEntry> dynamic_package_map_ = new ArrayList<>(); 367 GetEntry(ResTable_type type_chunk, short entry_index)368 ResTable_entry GetEntry(ResTable_type type_chunk, 369 short entry_index) { 370 int entry_offset = GetEntryOffset(type_chunk, entry_index); 371 if (entry_offset == ResTable_type.NO_ENTRY) { 372 return null; 373 } 374 return GetEntryFromOffset(type_chunk, entry_offset); 375 } 376 GetEntryOffset(ResTable_type type_chunk, int entry_index)377 static int GetEntryOffset(ResTable_type type_chunk, int entry_index) { 378 // The configuration matches and is better than the previous selection. 379 // Find the entry value if it exists for this configuration. 380 int entry_count = dtohl(type_chunk.entryCount); 381 int offsets_offset = dtohs(type_chunk.header.headerSize); 382 383 // Check if there is the desired entry in this type. 384 385 if (isTruthy(type_chunk.flags & ResTable_type.FLAG_SPARSE)) { 386 // This is encoded as a sparse map, so perform a binary search. 387 // ResTable_sparseTypeEntry sparse_indices = 388 // reinterpret_cast<ResTable_sparseTypeEntry*>( 389 // reinterpret_cast<uint8_t*>(type_chunk) + offsets_offset); 390 // ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; 391 // ResTable_sparseTypeEntry* result = 392 // std.lower_bound(sparse_indices, sparse_indices_end, entry_index, 393 // [](ResTable_sparseTypeEntry& entry, short entry_idx) { 394 // return dtohs(entry.idx) < entry_idx; 395 // }); 396 ResTable_sparseTypeEntry result = null; 397 for (int i = 0; i < entry_count; i++) { 398 ResTable_sparseTypeEntry entry = new ResTable_sparseTypeEntry(type_chunk.myBuf(), 399 type_chunk.myOffset() + offsets_offset); 400 if (entry.idxOrOffset >= entry_index) { 401 result = entry; 402 break; 403 } 404 } 405 406 if (result == null || dtohs(result.idxOrOffset) != entry_index) { 407 // No entry found. 408 return ResTable_type.NO_ENTRY; 409 } 410 411 // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as 412 // the real offset divided by 4. 413 // return int{dtohs(result.offset)} * 4u; 414 return dtohs(result.idxOrOffset) * 4; 415 } 416 417 // This type is encoded as a dense array. 418 if (entry_index >= entry_count) { 419 // This entry cannot be here. 420 return ResTable_type.NO_ENTRY; 421 } 422 423 // int* entry_offsets = reinterpret_cast<int*>( 424 // reinterpret_cast<uint8_t*>(type_chunk) + offsets_offset); 425 // return dtohl(entry_offsets[entry_index]); 426 return dtohl(type_chunk.entryOffset(entry_index)); 427 } 428 GetEntryFromOffset(ResTable_type type_chunk, int offset)429 static ResTable_entry GetEntryFromOffset(ResTable_type type_chunk, 430 int offset) { 431 if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) { 432 return null; 433 } 434 // return reinterpret_cast<ResTable_entry*>(reinterpret_cast<uint8_t*>(type_chunk) + 435 // offset + dtohl(type_chunk.entriesStart)); 436 return new ResTable_entry(type_chunk.myBuf(), 437 type_chunk.myOffset() + offset + dtohl(type_chunk.entriesStart)); 438 } 439 CollectConfigurations(boolean exclude_mipmap, Set<ResTable_config> out_configs)440 void CollectConfigurations(boolean exclude_mipmap, 441 Set<ResTable_config> out_configs) { 442 String kMipMap = "mipmap"; 443 int type_count = type_specs_.size(); 444 for (int i = 0; i < type_count; i++) { 445 TypeSpec type_spec = type_specs_.get(i); 446 if (type_spec != null) { 447 if (exclude_mipmap) { 448 int type_idx = type_spec.type_spec.id - 1; 449 final Ref<Integer> type_name_len = new Ref<>(0); 450 String type_name16 = type_string_pool_.stringAt(type_idx, type_name_len); 451 if (type_name16 != null) { 452 // if (kMipMap.compare(0, std::u16string::npos,type_name16, type_name_len) ==0){ 453 if (kMipMap.equals(type_name16)) { 454 // This is a mipmap type, skip collection. 455 continue; 456 } 457 } 458 String type_name = type_string_pool_.string8At(type_idx, type_name_len); 459 if (type_name != null) { 460 // if (strncmp(type_name, "mipmap", type_name_len) == 0) { 461 if ("mipmap".equals(type_name)) 462 // This is a mipmap type, skip collection. 463 continue; 464 } 465 } 466 } 467 468 for (ResTable_type iter : type_spec.types) { 469 ResTable_config config = ResTable_config.fromDtoH(iter.config); 470 out_configs.add(config); 471 } 472 } 473 } 474 CollectLocales(boolean canonicalize, Set<String> out_locales)475 void CollectLocales(boolean canonicalize, Set<String> out_locales) { 476 // char temp_locale[ RESTABLE_MAX_LOCALE_LEN]; 477 String temp_locale; 478 int type_count = type_specs_.size(); 479 for (int i = 0; i < type_count; i++) { 480 TypeSpec type_spec = type_specs_.get(i); 481 if (type_spec != null) { 482 for (ResTable_type iter : type_spec.types) { 483 ResTable_config configuration = ResTable_config.fromDtoH(iter.config); 484 if (configuration.locale() != 0) { 485 temp_locale = configuration.getBcp47Locale(canonicalize); 486 String locale = temp_locale; 487 out_locales.add(locale); 488 } 489 } 490 } 491 } 492 } 493 494 // Finds the entry with the specified type name and entry name. The names are in UTF-16 because 495 // the underlying ResStringPool API expects this. For now this is acceptable, but since 496 // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. 497 // Returns a partial resource ID, with the package ID left as 0x00. The caller is responsible 498 // for patching the correct package ID to the resource ID. FindEntryByName(String type_name, String entry_name)499 int FindEntryByName(String type_name, String entry_name) { 500 int type_idx = type_string_pool_.indexOfString(type_name); 501 if (type_idx < 0) { 502 return 0; 503 } 504 505 int key_idx = key_string_pool_.indexOfString(entry_name); 506 if (key_idx < 0) { 507 return 0; 508 } 509 510 TypeSpec type_spec = type_specs_.get(type_idx); 511 if (type_spec == null) { 512 return 0; 513 } 514 515 for (ResTable_type iter : type_spec.types) { 516 ResTable_type type = iter; 517 int entry_count = type.entryCount; 518 519 for (int entry_idx = 0; entry_idx < entry_count; entry_idx++) { 520 // const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>( 521 // reinterpret_cast<const uint8_t*>(type.type) + dtohs(type.type.header.headerSize)); 522 // ResTable_type entry_offsets = new ResTable_type(type.myBuf(), 523 // type.myOffset() + type.header.headerSize); 524 // int offset = dtohl(entry_offsets[entry_idx]); 525 int offset = dtohl(type.entryOffset(entry_idx)); 526 if (offset != ResTable_type.NO_ENTRY) { 527 // const ResTable_entry* entry = 528 // reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type.type) + 529 // dtohl(type.type.entriesStart) + offset); 530 ResTable_entry entry = 531 new ResTable_entry(type.myBuf(), type.myOffset() + 532 dtohl(type.entriesStart) + offset); 533 if (dtohl(entry.key.index) == key_idx) { 534 // The package ID will be overridden by the caller (due to runtime assignment of package 535 // IDs for shared libraries). 536 return make_resid((byte) 0x00, (byte) (type_idx + type_id_offset_ + 1), (short) entry_idx); 537 } 538 } 539 } 540 } 541 return 0; 542 } 543 Load(Chunk chunk, LoadedIdmap loaded_idmap, boolean system, boolean load_as_shared_library)544 static LoadedPackage Load(Chunk chunk, 545 LoadedIdmap loaded_idmap, 546 boolean system, boolean load_as_shared_library) { 547 // ATRACE_NAME("LoadedPackage::Load"); 548 LoadedPackage loaded_package = new LoadedPackage(); 549 550 // typeIdOffset was added at some point, but we still must recognize apps built before this 551 // was added. 552 // constexpr int kMinPackageSize = 553 // sizeof(ResTable_package) - sizeof(ResTable_package.typeIdOffset); 554 final int kMinPackageSize = ResTable_package.SIZEOF - 4; 555 // ResTable_package header = chunk.header<ResTable_package, kMinPackageSize>(); 556 ResTable_package header = chunk.asResTable_package(kMinPackageSize); 557 if (header == null) { 558 logError("RES_TABLE_PACKAGE_TYPE too small."); 559 return emptyBraces(); 560 } 561 562 loaded_package.system_ = system; 563 564 loaded_package.package_id_ = dtohl(header.id); 565 if (loaded_package.package_id_ == 0 || 566 (loaded_package.package_id_ == kAppPackageId && load_as_shared_library)) { 567 // Package ID of 0 means this is a shared library. 568 loaded_package.dynamic_ = true; 569 } 570 571 if (loaded_idmap != null) { 572 // This is an overlay and so it needs to pretend to be the target package. 573 loaded_package.package_id_ = loaded_idmap.TargetPackageId(); 574 loaded_package.overlay_ = true; 575 } 576 577 if (header.header.headerSize >= ResTable_package.SIZEOF) { 578 int type_id_offset = dtohl(header.typeIdOffset); 579 // if (type_id_offset > std.numeric_limits<uint8_t>.max()) { 580 if (type_id_offset > 255) { 581 logError("RES_TABLE_PACKAGE_TYPE type ID offset too large."); 582 return emptyBraces(); 583 } 584 loaded_package.type_id_offset_ = type_id_offset; 585 } 586 587 loaded_package.package_name_ = Util 588 .ReadUtf16StringFromDevice(header.name, header.name.length); 589 590 // A map of TypeSpec builders, each associated with an type index. 591 // We use these to accumulate the set of Types available for a TypeSpec, and later build a single, 592 // contiguous block of memory that holds all the Types together with the TypeSpec. 593 Map<Integer, TypeSpecPtrBuilder> type_builder_map = new HashMap<>(); 594 595 Chunk.Iterator iter = new Iterator(chunk.data_ptr(), chunk.data_size()); 596 while (iter.HasNext()) { 597 Chunk child_chunk = iter.Next(); 598 switch (child_chunk.type()) { 599 case RES_STRING_POOL_TYPE: { 600 // uintptr_t pool_address = 601 // reinterpret_cast<uintptr_t>(child_chunk.header<ResChunk_header>()); 602 // uintptr_t header_address = reinterpret_cast<uintptr_t>(header); 603 int pool_address = 604 child_chunk.myOffset(); 605 int header_address = header.myOffset(); 606 if (pool_address == header_address + dtohl(header.typeStrings)) { 607 // This string pool is the type string pool. 608 int err = loaded_package.type_string_pool_.setTo( 609 child_chunk.myBuf(), child_chunk.myOffset(), child_chunk.size(), false); 610 if (err != NO_ERROR) { 611 logError("RES_STRING_POOL_TYPE for types corrupt."); 612 return emptyBraces(); 613 } 614 } else if (pool_address == header_address + dtohl(header.keyStrings)) { 615 // This string pool is the key string pool. 616 int err = loaded_package.key_string_pool_.setTo( 617 child_chunk.myBuf(), child_chunk.myOffset(), child_chunk.size(), false); 618 if (err != NO_ERROR) { 619 logError("RES_STRING_POOL_TYPE for keys corrupt."); 620 return emptyBraces(); 621 } 622 } else { 623 logWarning("Too many RES_STRING_POOL_TYPEs found in RES_TABLE_PACKAGE_TYPE."); 624 } 625 } break; 626 627 case RES_TABLE_TYPE_SPEC_TYPE: { 628 ResTable_typeSpec type_spec = new ResTable_typeSpec(child_chunk.myBuf(), 629 child_chunk.myOffset()); 630 if (type_spec == null) { 631 logError("RES_TABLE_TYPE_SPEC_TYPE too small."); 632 return emptyBraces(); 633 } 634 635 if (type_spec.id == 0) { 636 logError("RES_TABLE_TYPE_SPEC_TYPE has invalid ID 0."); 637 return emptyBraces(); 638 } 639 640 // if (loaded_package.type_id_offset_ + static_cast<int>(type_spec.id) > 641 // std.numeric_limits<uint8_t>.max()) { 642 if (loaded_package.type_id_offset_ + type_spec.id > 255) { 643 logError("RES_TABLE_TYPE_SPEC_TYPE has out of range ID."); 644 return emptyBraces(); 645 } 646 647 // The data portion of this chunk contains entry_count 32bit entries, 648 // each one representing a set of flags. 649 // Here we only validate that the chunk is well formed. 650 int entry_count = dtohl(type_spec.entryCount); 651 652 // There can only be 2^16 entries in a type, because that is the ID 653 // space for entries (EEEE) in the resource ID 0xPPTTEEEE. 654 // if (entry_count > std.numeric_limits<short>.max()) { 655 if (entry_count > 0xffff) { 656 logError("RES_TABLE_TYPE_SPEC_TYPE has too many entries (" + entry_count + ")."); 657 return emptyBraces(); 658 } 659 660 if (entry_count * 4 /*sizeof(int)*/ > chunk.data_size()) { 661 logError("RES_TABLE_TYPE_SPEC_TYPE too small to hold entries."); 662 return emptyBraces(); 663 } 664 665 // If this is an overlay, associate the mapping of this type to the target type 666 // from the IDMAP. 667 IdmapEntry_header idmap_entry_header = null; 668 if (loaded_idmap != null) { 669 idmap_entry_header = loaded_idmap.GetEntryMapForType(type_spec.id); 670 } 671 672 TypeSpecPtrBuilder builder_ptr = type_builder_map.get(type_spec.id - 1); 673 if (builder_ptr == null) { 674 // builder_ptr = util.make_unique<TypeSpecPtrBuilder>(type_spec, idmap_entry_header); 675 builder_ptr = new TypeSpecPtrBuilder(type_spec, idmap_entry_header); 676 type_builder_map.put(type_spec.id - 1, builder_ptr); 677 } else { 678 logWarning(String.format("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x", 679 type_spec.id)); 680 } 681 } break; 682 683 case RES_TABLE_TYPE_TYPE: { 684 // ResTable_type type = child_chunk.header<ResTable_type, kResTableTypeMinSize>(); 685 ResTable_type type = child_chunk.asResTable_type(kResTableTypeMinSize); 686 if (type == null) { 687 logError("RES_TABLE_TYPE_TYPE too small."); 688 return emptyBraces(); 689 } 690 691 if (!VerifyResTableType(type)) { 692 return emptyBraces(); 693 } 694 695 // Type chunks must be preceded by their TypeSpec chunks. 696 TypeSpecPtrBuilder builder_ptr = type_builder_map.get(type.id - 1); 697 if (builder_ptr != null) { 698 builder_ptr.AddType(type); 699 } else { 700 logError(String.format( 701 "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.", 702 type.id)); 703 return emptyBraces(); 704 } 705 } break; 706 707 case RES_TABLE_LIBRARY_TYPE: { 708 ResTable_lib_header lib = child_chunk.asResTable_lib_header(); 709 if (lib == null) { 710 logError("RES_TABLE_LIBRARY_TYPE too small."); 711 return emptyBraces(); 712 } 713 714 if (child_chunk.data_size() / ResTable_lib_entry.SIZEOF < dtohl(lib.count)) { 715 logError("RES_TABLE_LIBRARY_TYPE too small to hold entries."); 716 return emptyBraces(); 717 } 718 719 // loaded_package.dynamic_package_map_.reserve(dtohl(lib.count)); 720 721 // ResTable_lib_entry entry_begin = 722 // reinterpret_cast<ResTable_lib_entry*>(child_chunk.data_ptr()); 723 ResTable_lib_entry entry_begin = 724 child_chunk.asResTable_lib_entry(); 725 // ResTable_lib_entry entry_end = entry_begin + dtohl(lib.count); 726 // for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) { 727 for (ResTable_lib_entry entry_iter = entry_begin; 728 entry_iter.myOffset() != entry_begin.myOffset() + dtohl(lib.count); 729 entry_iter = new ResTable_lib_entry(entry_iter.myBuf(), entry_iter.myOffset() + ResTable_lib_entry.SIZEOF)) { 730 String package_name = 731 Util.ReadUtf16StringFromDevice(entry_iter.packageName, 732 entry_iter.packageName.length); 733 734 if (dtohl(entry_iter.packageId) >= 255) { 735 logError(String.format( 736 "Package ID %02x in RES_TABLE_LIBRARY_TYPE too large for package '%s'.", 737 dtohl(entry_iter.packageId), package_name)); 738 return emptyBraces(); 739 } 740 741 // loaded_package.dynamic_package_map_.emplace_back(std.move(package_name), 742 // dtohl(entry_iter.packageId)); 743 loaded_package.dynamic_package_map_.add(new DynamicPackageEntry(package_name, 744 dtohl(entry_iter.packageId))); 745 } 746 747 } break; 748 749 default: 750 logWarning(String.format("Unknown chunk type '%02x'.", chunk.type())); 751 break; 752 } 753 } 754 755 if (iter.HadError()) { 756 logError(iter.GetLastError()); 757 if (iter.HadFatalError()) { 758 return emptyBraces(); 759 } 760 } 761 762 // Flatten and construct the TypeSpecs. 763 for (Entry<Integer, TypeSpecPtrBuilder> entry : type_builder_map.entrySet()) { 764 byte type_idx = (byte) entry.getKey().byteValue(); 765 TypeSpec type_spec_ptr = entry.getValue().Build(); 766 if (type_spec_ptr == null) { 767 logError("Too many type configurations, overflow detected."); 768 return emptyBraces(); 769 } 770 771 // We only add the type to the package if there is no IDMAP, or if the type is 772 // overlaying something. 773 if (loaded_idmap == null || type_spec_ptr.idmap_entries != null) { 774 // If this is an overlay, insert it at the target type ID. 775 if (type_spec_ptr.idmap_entries != null) { 776 type_idx = (byte) (dtohs(type_spec_ptr.idmap_entries.target_type_id) - 1); 777 } 778 // loaded_package.type_specs_.editItemAt(type_idx) = std.move(type_spec_ptr); 779 loaded_package.type_specs_.put((int) type_idx, type_spec_ptr); 780 } 781 } 782 783 // return std.move(loaded_package); 784 return loaded_package; 785 } 786 787 // Returns the string pool where type names are stored. GetTypeStringPool()788 ResStringPool GetTypeStringPool() { 789 return type_string_pool_; 790 } 791 792 // Returns the string pool where the names of resource entries are stored. GetKeyStringPool()793 ResStringPool GetKeyStringPool() { 794 return key_string_pool_; 795 } 796 GetPackageName()797 String GetPackageName() { 798 return package_name_; 799 } 800 GetPackageId()801 int GetPackageId() { 802 return package_id_; 803 } 804 805 // Returns true if this package is dynamic (shared library) and needs to have an ID assigned. IsDynamic()806 boolean IsDynamic() { 807 return dynamic_; 808 } 809 810 // Returns true if this package originates from a system provided resource. IsSystem()811 boolean IsSystem() { 812 return system_; 813 } 814 815 // Returns true if this package is from an overlay ApkAssets. IsOverlay()816 boolean IsOverlay() { 817 return overlay_; 818 } 819 820 // Returns the map of package name to package ID used in this LoadedPackage. At runtime, a 821 // package could have been assigned a different package ID than what this LoadedPackage was 822 // compiled with. AssetManager rewrites the package IDs so that they are compatible at runtime. GetDynamicPackageMap()823 List<DynamicPackageEntry> GetDynamicPackageMap() { 824 return dynamic_package_map_; 825 } 826 827 // type_idx is TT - 1 from 0xPPTTEEEE. GetTypeSpecByTypeIndex(int type_index)828 TypeSpec GetTypeSpecByTypeIndex(int type_index) { 829 // If the type IDs are offset in this package, we need to take that into account when searching 830 // for a type. 831 return type_specs_.get(type_index - type_id_offset_); 832 } 833 834 // template <typename Func> 835 interface TypeSpecFunc { apply(TypeSpec spec, byte index)836 void apply(TypeSpec spec, byte index); 837 } 838 ForEachTypeSpec(TypeSpecFunc f)839 void ForEachTypeSpec(TypeSpecFunc f) { 840 for (Integer i : type_specs_.keySet()) { 841 TypeSpec ptr = type_specs_.get(i); 842 if (ptr != null) { 843 byte type_id = ptr.type_spec.id; 844 if (ptr.idmap_entries != null) { 845 type_id = (byte) ptr.idmap_entries.target_type_id; 846 } 847 f.apply(ptr, (byte) (type_id - 1)); 848 } 849 } 850 } 851 emptyBraces()852 private static LoadedPackage emptyBraces() { 853 return new LoadedPackage(); 854 } 855 } 856 857 // Gets a pointer to the package with the specified package ID, or nullptr if no such package 858 // exists. GetPackageById(int package_id)859 LoadedPackage GetPackageById(int package_id) { 860 for (LoadedPackage loaded_package : packages_) { 861 if (loaded_package.GetPackageId() == package_id) { 862 return loaded_package; 863 } 864 } 865 return null; 866 } 867 LoadTable(Chunk chunk, LoadedIdmap loaded_idmap, boolean load_as_shared_library)868 boolean LoadTable(Chunk chunk, LoadedIdmap loaded_idmap, 869 boolean load_as_shared_library) { 870 // ResTable_header header = chunk.header<ResTable_header>(); 871 ResTable_header header = chunk.asResTable_header(); 872 if (header == null) { 873 logError("RES_TABLE_TYPE too small."); 874 return false; 875 } 876 877 int package_count = dtohl(header.packageCount); 878 int packages_seen = 0; 879 880 // packages_.reserve(package_count); 881 882 Chunk.Iterator iter = new Iterator(chunk.data_ptr(), chunk.data_size()); 883 while (iter.HasNext()) { 884 Chunk child_chunk = iter.Next(); 885 switch (child_chunk.type()) { 886 case RES_STRING_POOL_TYPE: 887 // Only use the first string pool. Ignore others. 888 if (global_string_pool_.getError() == NO_INIT) { 889 ResStringPool_header resStringPool_header = child_chunk.asResStringPool_header(); 890 int err = global_string_pool_.setTo(resStringPool_header.myBuf(), 891 resStringPool_header.myOffset(), 892 child_chunk.size(), false); 893 if (err != NO_ERROR) { 894 logError("RES_STRING_POOL_TYPE corrupt."); 895 return false; 896 } 897 } else { 898 logWarning("Multiple RES_STRING_POOL_TYPEs found in RES_TABLE_TYPE."); 899 } 900 break; 901 902 case RES_TABLE_PACKAGE_TYPE: { 903 if (packages_seen + 1 > package_count) { 904 logError("More package chunks were found than the " + package_count 905 + " declared in the header."); 906 return false; 907 } 908 packages_seen++; 909 910 LoadedPackage loaded_package = 911 LoadedPackage.Load(child_chunk, loaded_idmap, system_, load_as_shared_library); 912 if (!isTruthy(loaded_package)) { 913 return false; 914 } 915 packages_.add(loaded_package); 916 } break; 917 918 default: 919 logWarning(String.format("Unknown chunk type '%02x'.", chunk.type())); 920 break; 921 } 922 } 923 924 if (iter.HadError()) { 925 logError(iter.GetLastError()); 926 if (iter.HadFatalError()) { 927 return false; 928 } 929 } 930 return true; 931 } 932 933 // Read-only view into a resource table. This class validates all data 934 // when loading, including offsets and lengths. 935 //class LoadedArsc { 936 // public: 937 // Load a resource table from memory pointed to by `data` of size `len`. 938 // The lifetime of `data` must out-live the LoadedArsc returned from this method. 939 // If `system` is set to true, the LoadedArsc is considered as a system provided resource. 940 // If `load_as_shared_library` is set to true, the application package (0x7f) is treated 941 // as a shared library (0x00). When loaded into an AssetManager, the package will be assigned an 942 // ID. Load(StringPiece data, LoadedIdmap loaded_idmap , boolean system , boolean load_as_shared_library )943 static LoadedArsc Load(StringPiece data, 944 LoadedIdmap loaded_idmap /* = null */, boolean system /* = false */, 945 boolean load_as_shared_library /* = false */) { 946 // ATRACE_NAME("LoadedArsc::LoadTable"); 947 948 // Not using make_unique because the constructor is private. 949 LoadedArsc loaded_arsc = new LoadedArsc(); 950 loaded_arsc.system_ = system; 951 952 Chunk.Iterator iter = new Iterator(data, data.size()); 953 while (iter.HasNext()) { 954 Chunk chunk = iter.Next(); 955 switch (chunk.type()) { 956 case RES_TABLE_TYPE: 957 if (!loaded_arsc.LoadTable(chunk, loaded_idmap, load_as_shared_library)) { 958 return emptyBraces(); 959 } 960 break; 961 962 default: 963 logWarning(String.format("Unknown chunk type '%02x'.", chunk.type())); 964 break; 965 } 966 } 967 968 if (iter.HadError()) { 969 logError(iter.GetLastError()); 970 if (iter.HadFatalError()) { 971 return emptyBraces(); 972 } 973 } 974 975 // Need to force a move for mingw32. 976 // return std.move(loaded_arsc); 977 return loaded_arsc; 978 } 979 980 // Create an empty LoadedArsc. This is used when an APK has no resources.arsc. CreateEmpty()981 static LoadedArsc CreateEmpty() { 982 return new LoadedArsc(); 983 } 984 985 // Populates a set of ResTable_config structs, possibly excluding configurations defined for 986 // the mipmap type. 987 // void CollectConfigurations(boolean exclude_mipmap, Set<ResTable_config> out_configs); 988 989 // Populates a set of strings representing locales. 990 // If `canonicalize` is set to true, each locale is transformed into its canonical format 991 // before being inserted into the set. This may cause some equivalent locales to de-dupe. 992 // void CollectLocales(boolean canonicalize, Set<String> out_locales); 993 emptyBraces()994 private static LoadedArsc emptyBraces() { 995 return new LoadedArsc(); 996 } 997 998 } 999