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