Home | History | Annotate | Download | only in android
      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 
     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 
    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.
    114   public ResStringPool GetStringPool() {
    115     return global_string_pool_;
    116   }
    117 
    118   // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc.
    119   List<LoadedPackage> GetPackages() {
    120     return packages_;
    121   }
    122 
    123   // Returns true if this is a system provided resource.
    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:
    181     TypeSpecPtrBuilder(ResTable_typeSpec header, IdmapEntry_header idmap_header) {
    182       this.header_ = header;
    183       this.idmap_header_ = idmap_header;
    184     }
    185 
    186     void AddType(ResTable_type type) {
    187       types_.add(type);
    188     }
    189 
    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.
    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 
    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 
    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 
    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 
    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 
    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 
    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.
    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 
    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.
    788     ResStringPool GetTypeStringPool() {
    789       return type_string_pool_;
    790     }
    791 
    792     // Returns the string pool where the names of resource entries are stored.
    793     ResStringPool GetKeyStringPool() {
    794       return key_string_pool_;
    795     }
    796 
    797     String GetPackageName() {
    798       return package_name_;
    799     }
    800 
    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.
    806     boolean IsDynamic() {
    807       return dynamic_;
    808     }
    809 
    810     // Returns true if this package originates from a system provided resource.
    811     boolean IsSystem() {
    812       return system_;
    813     }
    814 
    815     // Returns true if this package is from an overlay ApkAssets.
    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.
    823     List<DynamicPackageEntry> GetDynamicPackageMap() {
    824       return dynamic_package_map_;
    825     }
    826 
    827     // type_idx is TT - 1 from 0xPPTTEEEE.
    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 {
    836       void apply(TypeSpec spec, byte index);
    837     }
    838 
    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 
    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.
    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 
    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.
    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.
    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 
    994   private static LoadedArsc emptyBraces() {
    995     return new LoadedArsc();
    996   }
    997 
    998 }
    999