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