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