• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.res.android;
2 
3 import static org.robolectric.res.android.ApkAssetsCookie.K_INVALID_COOKIE;
4 import static org.robolectric.res.android.ApkAssetsCookie.kInvalidCookie;
5 import static org.robolectric.res.android.Errors.NO_ERROR;
6 import static org.robolectric.res.android.ResourceUtils.ExtractResourceName;
7 import static org.robolectric.res.android.ResourceUtils.fix_package_id;
8 import static org.robolectric.res.android.ResourceUtils.get_entry_id;
9 import static org.robolectric.res.android.ResourceUtils.get_package_id;
10 import static org.robolectric.res.android.ResourceUtils.get_type_id;
11 import static org.robolectric.res.android.ResourceUtils.is_internal_resid;
12 import static org.robolectric.res.android.ResourceUtils.is_valid_resid;
13 import static org.robolectric.res.android.Util.ATRACE_CALL;
14 import static org.robolectric.res.android.Util.dtohl;
15 import static org.robolectric.res.android.Util.dtohs;
16 import static org.robolectric.res.android.Util.isTruthy;
17 
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Objects;
26 import java.util.Set;
27 import org.robolectric.res.Fs;
28 import org.robolectric.res.FsFile;
29 import org.robolectric.res.android.CppApkAssets.ForEachFileCallback;
30 import org.robolectric.res.android.AssetDir.FileInfo;
31 import org.robolectric.res.android.CppAssetManager.FileType;
32 import org.robolectric.res.android.CppAssetManager2.ResolvedBag.Entry;
33 import org.robolectric.res.android.Idmap.LoadedIdmap;
34 import org.robolectric.res.android.LoadedArsc.DynamicPackageEntry;
35 import org.robolectric.res.android.LoadedArsc.LoadedPackage;
36 import org.robolectric.res.android.LoadedArsc.TypeSpec;
37 import org.robolectric.res.android.ResourceTypes.ResTable_entry;
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_type;
41 import org.robolectric.res.android.ResourceTypes.Res_value;
42 
43 // transliterated from https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/include/androidfw/AssetManager2.h
44 // and https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/AssetManager2.cpp
45 public class CppAssetManager2 {
46 //  #define ATRACE_TAG ATRACE_TAG_RESOURCES
47 //
48 //  #include "androidfw/AssetManager2.h"
49 
50 //#include <array>
51 //#include <limits>
52 //#include <set>
53 //#include <unordered_map>
54 //
55 //#include "androidfw/ApkAssets.h"
56 //#include "androidfw/Asset.h"
57 //#include "androidfw/AssetManager.h"
58 //#include "androidfw/ResourceTypes.h"
59 //#include "androidfw/Util.h"
60 //
61 //namespace android {
62 //
63 //class Theme;
64 //
65 //using ApkAssetsCookie = int32_t;
66 //
67 //enum : ApkAssetsCookie {
68   //};
69 
70   // Holds a bag that has been merged with its parent, if one exists.
71   public static class ResolvedBag {
72     // A single key-value entry in a bag.
73     public static class Entry {
74       // The key, as described in ResTable_map.name.
75       public int key;
76 
77       public Res_value value = new Res_value();
78 
79       // BEGIN-INTERNAL
80       // The resource ID of the origin style associated with the given entry
81       public int style;
82       // END-INTERNAL
83 
84       // Which ApkAssets this entry came from.
85       public ApkAssetsCookie cookie;
86 
87       ResStringPool key_pool;
88       ResStringPool type_pool;
89 
copy()90       public ResolvedBag.Entry copy() {
91         Entry entry = new Entry();
92         entry.key = key;
93         entry.value = value.copy();
94         entry.cookie = cookie == null ? null : ApkAssetsCookie.forInt(cookie.intValue());
95         entry.key_pool = key_pool;
96         entry.type_pool = type_pool;
97         return entry;
98       }
99 
100       @Override
toString()101       public String toString() {
102         return "Entry{" +
103             "key=" + key +
104             ", value=" + value +
105             '}';
106       }
107     };
108 
109     // Denotes the configuration axis that this bag varies with.
110     // If a configuration changes with respect to one of these axis,
111     // the bag should be reloaded.
112     public int type_spec_flags;
113 
114     // The number of entries in this bag. Access them by indexing into `entries`.
115     public int entry_count;
116 
117     // The array of entries for this bag. An empty array is a neat trick to force alignment
118     // of the Entry structs that follow this structure and avoids a bunch of casts.
119     public Entry[] entries;
120   };
121 
122   // AssetManager2 is the main entry point for accessing assets and resources.
123   // AssetManager2 provides caching of resources retrieved via the underlying ApkAssets.
124 //  class AssetManager2 : public .AAssetManager {
125 //   public:
126   public static class ResourceName {
127     public String package_ = null;
128     // int package_len = 0;
129 
130     public String type = null;
131     // public String type16 = null;
132     // int type_len = 0;
133 
134     public String entry = null;
135     // public String entry16 = null;
136     // int entry_len = 0;
137   };
138 
CppAssetManager2()139   public CppAssetManager2() {
140   }
141 
142 
GetApkAssets()143   public final List<CppApkAssets> GetApkAssets() { return apk_assets_; }
144 
GetConfiguration()145   final ResTable_config GetConfiguration() { return configuration_; }
146 
147 // private:
148 //  DISALLOW_COPY_AND_ASSIGN(AssetManager2);
149 
150   // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
151   // have a longer lifetime.
152   private List<CppApkAssets> apk_assets_;
153 
154   // A collection of configurations and their associated ResTable_type that match the current
155   // AssetManager configuration.
156   static class FilteredConfigGroup {
157     final List<ResTable_config> configurations = new ArrayList<>();
158     final List<ResTable_type> types = new ArrayList<>();
159   }
160 
161   // Represents an single package.
162   static class ConfiguredPackage {
163     // A pointer to the immutable, loaded package info.
164     LoadedPackage loaded_package_;
165 
166     // A mutable AssetManager-specific list of configurations that match the AssetManager's
167     // current configuration. This is used as an optimization to avoid checking every single
168     // candidate configuration when looking up resources.
169     ByteBucketArray<FilteredConfigGroup> filtered_configs_;
170 
ConfiguredPackage(LoadedPackage package_)171     public ConfiguredPackage(LoadedPackage package_) {
172       this.loaded_package_ = package_;
173     }
174   }
175 
176   // Represents a logical package, which can be made up of many individual packages. Each package
177   // in a PackageGroup shares the same package name and package ID.
178   static class PackageGroup {
179     // The set of packages that make-up this group.
180     final List<ConfiguredPackage> packages_ = new ArrayList<>();
181 
182     // The cookies associated with each package in the group. They share the same order as
183     // packages_.
184     final List<ApkAssetsCookie> cookies_ = new ArrayList<>();
185 
186     // A library reference table that contains build-package ID to runtime-package ID mappings.
187     DynamicRefTable dynamic_ref_table;
188   }
189 
190   // DynamicRefTables for shared library package resolution.
191   // These are ordered according to apk_assets_. The mappings may change depending on what is
192   // in apk_assets_, therefore they must be stored in the AssetManager and not in the
193   // immutable ApkAssets class.
194   final private List<PackageGroup> package_groups_ = new ArrayList<>();
195 
196   // An array mapping package ID to index into package_groups. This keeps the lookup fast
197   // without taking too much memory.
198 //  private std.array<byte, std.numeric_limits<byte>.max() + 1> package_ids_;
199   final private byte[] package_ids_ = new byte[256];
200 
201   // The current configuration set for this AssetManager. When this changes, cached resources
202   // may need to be purged.
203   private ResTable_config configuration_ = new ResTable_config();
204 
205   // Cached set of bags. These are cached because they can inherit keys from parent bags,
206   // which involves some calculation.
207 //  private std.unordered_map<int, util.unique_cptr<ResolvedBag>> cached_bags_;
208   final private Map<Integer, ResolvedBag> cached_bags_ = new HashMap<>();
209 //  };
210 
211 //final ResolvedBag.Entry* begin(final ResolvedBag* bag) { return bag.entries; }
212 //
213 //final ResolvedBag.Entry* end(final ResolvedBag* bag) {
214 //  return bag.entries + bag.entry_count;
215 //}
216 //
217 //}  // namespace android
218 //
219 //#endif /* ANDROIDFW_ASSETMANAGER2_H_ */
220 
221 
222   //
223 //  #include <set>
224 //
225 //  #include "android-base/logging.h"
226 //  #include "android-base/stringprintf.h"
227 //  #include "utils/ByteOrder.h"
228 //  #include "utils/Trace.h"
229 //
230 //  #ifdef _WIN32
231 //  #ifdef ERROR
232 //  #undef ERROR
233 //  #endif
234 //  #endif
235 //
236 //  #include "androidfw/ResourceUtils.h"
237 //
238 //  namespace android {
239   static class FindEntryResult {
240     // A pointer to the resource table entry for this resource.
241     // If the size of the entry is > sizeof(ResTable_entry), it can be cast to
242     // a ResTable_map_entry and processed as a bag/map.
243     ResTable_entry entry;
244 
245     // The configuration for which the resulting entry was defined. This is already swapped to host
246     // endianness.
247     ResTable_config config;
248 
249     // The bitmask of configuration axis with which the resource value varies.
250     int type_flags;
251 
252     // The dynamic package ID map for the package from which this resource came from.
253     DynamicRefTable dynamic_ref_table;
254 
255     // The string pool reference to the type's name. This uses a different string pool than
256     // the global string pool, but this is hidden from the caller.
257     StringPoolRef type_string_ref;
258 
259     // The string pool reference to the entry's name. This uses a different string pool than
260     // the global string pool, but this is hidden from the caller.
261     StringPoolRef entry_string_ref;
262   }
263 
264 //  AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); }
265 
266   // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets
267   // are not owned by the AssetManager, and must have a longer lifetime.
268   //
269   // Only pass invalidate_caches=false when it is known that the structure
270   // change in ApkAssets is due to a safe addition of resources with completely
271   // new resource IDs.
272 //  boolean SetApkAssets(final List<ApkAssets> apk_assets, boolean invalidate_caches = true);
SetApkAssets(final List<CppApkAssets> apk_assets, boolean invalidate_caches)273   public boolean SetApkAssets(final List<CppApkAssets> apk_assets, boolean invalidate_caches) {
274     apk_assets_ = apk_assets;
275     BuildDynamicRefTable();
276     RebuildFilterList();
277     if (invalidate_caches) {
278 //      InvalidateCaches(static_cast<int>(-1));
279       InvalidateCaches(-1);
280     }
281     return true;
282   }
283 
284   // Assigns package IDs to all shared library ApkAssets.
285   // Should be called whenever the ApkAssets are changed.
286 //  void BuildDynamicRefTable();
BuildDynamicRefTable()287   void BuildDynamicRefTable() {
288     package_groups_.clear();
289 //    package_ids_.fill(0xff);
290     for (int i = 0; i < package_ids_.length; i++) {
291       package_ids_[i] = (byte) 0xff;
292     }
293 
294     // 0x01 is reserved for the android package.
295     int next_package_id = 0x02;
296     final int apk_assets_count = apk_assets_.size();
297     for (int i = 0; i < apk_assets_count; i++) {
298       final LoadedArsc loaded_arsc = apk_assets_.get(i).GetLoadedArsc();
299 //      for (final std.unique_ptr<final LoadedPackage>& package_ :
300       for (final LoadedPackage package_ :
301           loaded_arsc.GetPackages()) {
302         // Get the package ID or assign one if a shared library.
303         int package_id;
304         if (package_.IsDynamic()) {
305           package_id = next_package_id++;
306         } else {
307           package_id = package_.GetPackageId();
308         }
309 
310         // Add the mapping for package ID to index if not present.
311         byte idx = package_ids_[package_id];
312         if (idx == (byte) 0xff) {
313           // package_ids_[package_id] = idx = static_cast<byte>(package_groups_.size());
314           package_ids_[package_id] = idx = (byte) package_groups_.size();
315           // DynamicRefTable& ref_table = package_groups_.back().dynamic_ref_table;
316           // ref_table.mAssignedPackageId = package_id;
317           // ref_table.mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
318           DynamicRefTable ref_table = new DynamicRefTable((byte) package_id,
319               package_.IsDynamic() && package_.GetPackageId() == 0x7f);
320           PackageGroup newPackageGroup = new PackageGroup();
321           newPackageGroup.dynamic_ref_table = ref_table;
322 
323           package_groups_.add(newPackageGroup);
324         }
325         PackageGroup package_group = package_groups_.get(idx);
326 
327         // Add the package and to the set of packages with the same ID.
328         // package_group->packages_.push_back(ConfiguredPackage{package.get(), {}});
329         // package_group.cookies_.push_back(static_cast<ApkAssetsCookie>(i));
330         package_group.packages_.add(new ConfiguredPackage(package_));
331         package_group.cookies_.add(ApkAssetsCookie.forInt(i));
332 
333         // Add the package name . build time ID mappings.
334         for (final DynamicPackageEntry entry : package_.GetDynamicPackageMap()) {
335           // String package_name(entry.package_name.c_str(), entry.package_name.size());
336           package_group.dynamic_ref_table.mEntries.put(
337               entry.package_name, (byte) entry.package_id);
338         }
339       }
340     }
341 
342     // Now assign the runtime IDs so that we have a build-time to runtime ID map.
343     for (PackageGroup iter : package_groups_) {
344       String package_name = iter.packages_.get(0).loaded_package_.GetPackageName();
345       for (PackageGroup iter2 : package_groups_) {
346         iter2.dynamic_ref_table.addMapping(package_name,
347             iter.dynamic_ref_table.mAssignedPackageId);
348       }
349     }
350   }
351 
352 // void AssetManager2::DumpToLog() const {
353 //   base::ScopedLogSeverity _log(base::INFO);
354 //
355 //   LOG(INFO) << base::StringPrintf("AssetManager2(this=%p)", this);
356 //
357 //   std::string list;
358 //   for (const auto& apk_assets : apk_assets_) {
359 //     base::StringAppendF(&list, "%s,", apk_assets->GetPath().c_str());
360 //   }
361 //   LOG(INFO) << "ApkAssets: " << list;
362 //
363 //   list = "";
364 //   for (size_t i = 0; i < package_ids_.size(); i++) {
365 //     if (package_ids_[i] != 0xff) {
366 //       base::StringAppendF(&list, "%02x -> %d, ", (int)i, package_ids_[i]);
367 //     }
368 //   }
369 //   LOG(INFO) << "Package ID map: " << list;
370 //
371 //   for (const auto& package_group: package_groups_) {
372 //     list = "";
373 //     for (const auto& package : package_group.packages_) {
374 //       const LoadedPackage* loaded_package = package.loaded_package_;
375 //       base::StringAppendF(&list, "%s(%02x%s), ", loaded_package->GetPackageName().c_str(),
376 //                           loaded_package->GetPackageId(),
377 //                           (loaded_package->IsDynamic() ? " dynamic" : ""));
378 //     }
379 //     LOG(INFO) << base::StringPrintf("PG (%02x): ",
380 //                                     package_group.dynamic_ref_table.mAssignedPackageId)
381 //               << list;
382 //   }
383 // }
384 
385   // Returns the string pool for the given asset cookie.
386   // Use the string pool returned here with a valid Res_value object of type Res_value.TYPE_STRING.
387 //  final ResStringPool GetStringPoolForCookie(ApkAssetsCookie cookie) const;
GetStringPoolForCookie(ApkAssetsCookie cookie)388   final ResStringPool GetStringPoolForCookie(ApkAssetsCookie cookie) {
389     if (cookie.intValue() < 0 || cookie.intValue() >= apk_assets_.size()) {
390       return null;
391     }
392     return apk_assets_.get(cookie.intValue()).GetLoadedArsc().GetStringPool();
393   }
394 
395   // Returns the DynamicRefTable for the given package ID.
396   // This may be nullptr if the APK represented by `cookie` has no resource table.
397 //  final DynamicRefTable GetDynamicRefTableForPackage(int package_id) const;
GetDynamicRefTableForPackage(int package_id)398   final DynamicRefTable GetDynamicRefTableForPackage(int package_id) {
399     if (package_id >= package_ids_.length) {
400       return null;
401     }
402 
403     final int idx = package_ids_[package_id];
404     if (idx == 0xff) {
405       return null;
406     }
407     return package_groups_.get(idx).dynamic_ref_table;
408   }
409 
410   // Returns the DynamicRefTable for the ApkAssets represented by the cookie.
411 //  final DynamicRefTable GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const;
GetDynamicRefTableForCookie(ApkAssetsCookie cookie)412   public final DynamicRefTable GetDynamicRefTableForCookie(ApkAssetsCookie cookie) {
413     for (final PackageGroup package_group : package_groups_) {
414       for (final ApkAssetsCookie package_cookie : package_group.cookies_) {
415         if (package_cookie == cookie) {
416           return package_group.dynamic_ref_table;
417         }
418       }
419     }
420     return null;
421   }
422 
423   // Sets/resets the configuration for this AssetManager. This will cause all
424   // caches that are related to the configuration change to be invalidated.
425 //  void SetConfiguration(final ResTable_config& configuration);
SetConfiguration(final ResTable_config configuration)426   public void SetConfiguration(final ResTable_config configuration) {
427     final int diff = configuration_.diff(configuration);
428     configuration_ = configuration;
429 
430     if (isTruthy(diff)) {
431       RebuildFilterList();
432 //      InvalidateCaches(static_cast<int>(diff));
433       InvalidateCaches(diff);
434     }
435   }
436 
437   // Returns all configurations for which there are resources defined. This includes resource
438   // configurations in all the ApkAssets set for this AssetManager.
439   // If `exclude_system` is set to true, resource configurations from system APKs
440   // ('android' package, other libraries) will be excluded from the list.
441   // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap'
442   // will be excluded from the list.
443 //  Set<ResTable_config> GetResourceConfigurations(boolean exclude_system = false,
444 //                                                 boolean exclude_mipmap = false);
GetResourceConfigurations(boolean exclude_system, boolean exclude_mipmap)445   public Set<ResTable_config> GetResourceConfigurations(boolean exclude_system,
446       boolean exclude_mipmap) {
447     // ATRACE_NAME("AssetManager::GetResourceConfigurations");
448     Set<ResTable_config> configurations = new HashSet<>();
449     for (final PackageGroup package_group : package_groups_) {
450       for (final ConfiguredPackage package_ : package_group.packages_) {
451         if (exclude_system && package_.loaded_package_.IsSystem()) {
452           continue;
453         }
454         package_.loaded_package_.CollectConfigurations(exclude_mipmap, configurations);
455       }
456     }
457     return configurations;
458   }
459 
460   // Returns all the locales for which there are resources defined. This includes resource
461   // locales in all the ApkAssets set for this AssetManager.
462   // If `exclude_system` is set to true, resource locales from system APKs
463   // ('android' package, other libraries) will be excluded from the list.
464   // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized
465   // and de-duped in the resulting list.
466 //  Set<String> GetResourceLocales(boolean exclude_system = false,
467 //                                 boolean merge_equivalent_languages = false);
GetResourceLocales(boolean exclude_system, boolean merge_equivalent_languages)468   public Set<String> GetResourceLocales(boolean exclude_system,
469       boolean merge_equivalent_languages) {
470     ATRACE_CALL();
471     Set<String> locales = new HashSet<>();
472     for (final PackageGroup package_group : package_groups_) {
473       for (final ConfiguredPackage package_ : package_group.packages_) {
474         if (exclude_system && package_.loaded_package_.IsSystem()) {
475           continue;
476         }
477         package_.loaded_package_.CollectLocales(merge_equivalent_languages, locales);
478       }
479     }
480     return locales;
481   }
482 
483   // Searches the set of APKs loaded by this AssetManager and opens the first one found located
484   // in the assets/ directory.
485   // `mode` controls how the file is opened.
486   //
487   // NOTE: The loaded APKs are searched in reverse order.
488 //  Asset Open(final String filename, Asset.AccessMode mode);
Open(final String filename, Asset.AccessMode mode)489   public Asset Open(final String filename, Asset.AccessMode mode) {
490     final String new_path = "assets/" + filename;
491     return OpenNonAsset(new_path, mode);
492   }
493 
494   // Opens a file within the assets/ directory of the APK specified by `cookie`.
495   // `mode` controls how the file is opened.
496 //  Asset Open(final String filename, ApkAssetsCookie cookie,
497 //             Asset.AccessMode mode);
Open(final String filename, ApkAssetsCookie cookie, Asset.AccessMode mode)498   Asset Open(final String filename, ApkAssetsCookie cookie,
499       Asset.AccessMode mode) {
500     final String new_path = "assets/" + filename;
501     return OpenNonAsset(new_path, cookie, mode);
502   }
503 
504   // Opens the directory specified by `dirname`. The result is an AssetDir that is the combination
505   // of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded.
506   // The entries are sorted by their ASCII name.
507 //  AssetDir OpenDir(final String dirname);
OpenDir(final String dirname)508   public AssetDir OpenDir(final String dirname) {
509     ATRACE_CALL();
510 
511     String full_path = "assets/" + dirname;
512     // std.unique_ptr<SortedVector<AssetDir.FileInfo>> files =
513     //     util.make_unique<SortedVector<AssetDir.FileInfo>>();
514     SortedVector<FileInfo> files = new SortedVector<>();
515 
516     // Start from the back.
517     for (CppApkAssets apk_assets : apk_assets_) {
518       // auto func = [&](final String& name, FileType type) {
519       ForEachFileCallback func = (final String name, FileType type) -> {
520         AssetDir.FileInfo info = new FileInfo();
521         info.setFileName(new String8(name));
522         info.setFileType(type);
523         info.setSourceName(new String8(apk_assets.GetPath()));
524         files.add(info);
525       };
526 
527       if (!apk_assets.ForEachFile(full_path, func)) {
528         return new AssetDir();
529       }
530     }
531 
532     // std.unique_ptr<AssetDir> asset_dir = util.make_unique<AssetDir>();
533     AssetDir asset_dir = new AssetDir();
534     asset_dir.setFileList(files);
535     return asset_dir;
536   }
537 
538   // Searches the set of APKs loaded by this AssetManager and opens the first one found.
539   // `mode` controls how the file is opened.
540   // `out_cookie` is populated with the cookie of the APK this file was found in.
541   //
542   // NOTE: The loaded APKs are searched in reverse order.
543 //  Asset OpenNonAsset(final String filename, Asset.AccessMode mode,
544 //                     ApkAssetsCookie* out_cookie = null);
545   // Search in reverse because that's how we used to do it and we need to preserve behaviour.
546   // This is unfortunate, because ClassLoaders delegate to the parent first, so the order
547   // is inconsistent for split APKs.
OpenNonAsset(final String filename, Asset.AccessMode mode, Ref<ApkAssetsCookie> out_cookie)548   public Asset OpenNonAsset(final String filename,
549       Asset.AccessMode mode,
550       Ref<ApkAssetsCookie> out_cookie) {
551     ATRACE_CALL();
552     for (int i = apk_assets_.size() - 1; i >= 0; i--) {
553       Asset asset = apk_assets_.get(i).Open(filename, mode);
554       if (isTruthy(asset)) {
555         if (out_cookie != null) {
556           out_cookie.set(ApkAssetsCookie.forInt(i));
557         }
558         return asset;
559       }
560     }
561 
562     if (out_cookie != null) {
563       out_cookie.set(K_INVALID_COOKIE);
564     }
565     return null;
566   }
567 
OpenNonAsset(final String filename, Asset.AccessMode mode)568   public Asset OpenNonAsset(final String filename, Asset.AccessMode mode) {
569     return OpenNonAsset(filename, mode, null);
570   }
571 
572   // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened.
573   // This is typically used to open a specific AndroidManifest.xml, or a binary XML file
574   // referenced by a resource lookup with GetResource().
575 //  Asset OpenNonAsset(final String filename, ApkAssetsCookie cookie,
576 //                     Asset.AccessMode mode);
OpenNonAsset(final String filename, ApkAssetsCookie cookie, Asset.AccessMode mode)577   public Asset OpenNonAsset(final String filename,
578       ApkAssetsCookie cookie, Asset.AccessMode mode) {
579     ATRACE_CALL();
580     if (cookie.intValue() < 0 || cookie.intValue() >= apk_assets_.size()) {
581       return null;
582     }
583     return apk_assets_.get(cookie.intValue()).Open(filename, mode);
584   }
585 
586   // template <typename Func>
587   public interface PackageFunc {
apply(String package_name, byte package_id)588     void apply(String package_name, byte package_id);
589   }
590 
ForEachPackage(PackageFunc func)591   public void ForEachPackage(PackageFunc func) {
592     for (PackageGroup package_group : package_groups_) {
593       func.apply(package_group.packages_.get(0).loaded_package_.GetPackageName(),
594           package_group.dynamic_ref_table.mAssignedPackageId);
595     }
596   }
597 
598   // Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple
599   // Res_value, or a complex map/bag type. If successful, it is available in `out_entry`.
600   // Returns kInvalidCookie on failure. Otherwise, the return value is the cookie associated with
601   // the ApkAssets in which the entry was found.
602   //
603   // `density_override` overrides the density of the current configuration when doing a search.
604   //
605   // When `stop_at_first_match` is true, the first match found is selected and the search
606   // terminates. This is useful for methods that just look up the name of a resource and don't
607   // care about the value. In this case, the value of `FindEntryResult::type_flags` is incomplete
608   // and should not be used.
609   //
610   // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly
611   // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds.
612 //  ApkAssetsCookie FindEntry(int resid, short density_override, boolean stop_at_first_match,
613 //                            LoadedArscEntry* out_entry, ResTable_config out_selected_config,
614 //                            int* out_flags);
FindEntry(int resid, short density_override, boolean stop_at_first_match, final Ref<FindEntryResult> out_entry)615   private ApkAssetsCookie FindEntry(int resid, short density_override,
616       boolean stop_at_first_match,
617       final Ref<FindEntryResult> out_entry) {
618     ATRACE_CALL();
619 
620     // Might use this if density_override != 0.
621     ResTable_config density_override_config;
622 
623     // Select our configuration or generate a density override configuration.
624     ResTable_config desired_config = configuration_;
625     if (density_override != 0 && density_override != configuration_.density) {
626       density_override_config = configuration_;
627       density_override_config.density = density_override;
628       desired_config = density_override_config;
629     }
630 
631     if (!is_valid_resid(resid)) {
632       System.err.println(String.format("Invalid ID 0x%08x.", resid));
633       return K_INVALID_COOKIE;
634     }
635 
636     final int package_id = get_package_id(resid);
637     final int type_idx = (byte) (get_type_id(resid) - 1);
638     final int entry_idx = get_entry_id(resid);
639 
640     final byte package_idx = package_ids_[package_id];
641     if (package_idx == (byte) 0xff) {
642       System.err.println(String.format("No package ID %02x found for ID 0x%08x.", package_id, resid));
643       return K_INVALID_COOKIE;
644     }
645 
646     final PackageGroup package_group = package_groups_.get(package_idx);
647     final int package_count = package_group.packages_.size();
648 
649     ApkAssetsCookie best_cookie = K_INVALID_COOKIE;
650     LoadedPackage best_package = null;
651     ResTable_type best_type = null;
652     ResTable_config best_config = null;
653     ResTable_config best_config_copy;
654     int best_offset = 0;
655     int type_flags = 0;
656 
657     // If desired_config is the same as the set configuration, then we can use our filtered list
658     // and we don't need to match the configurations, since they already matched.
659     boolean use_fast_path = desired_config == configuration_;
660 
661     for (int pi = 0; pi < package_count; pi++) {
662       ConfiguredPackage loaded_package_impl = package_group.packages_.get(pi);
663       LoadedPackage loaded_package = loaded_package_impl.loaded_package_;
664       ApkAssetsCookie cookie = package_group.cookies_.get(pi);
665 
666       // If the type IDs are offset in this package, we need to take that into account when searching
667       // for a type.
668       TypeSpec type_spec = loaded_package.GetTypeSpecByTypeIndex(type_idx);
669       if (Util.UNLIKELY(type_spec == null)) {
670         continue;
671       }
672 
673       int local_entry_idx = entry_idx;
674 
675       // If there is an IDMAP supplied with this package, translate the entry ID.
676       if (type_spec.idmap_entries != null) {
677         if (!LoadedIdmap
678             .Lookup(type_spec.idmap_entries, local_entry_idx, new Ref<>(local_entry_idx))) {
679           // There is no mapping, so the resource is not meant to be in this overlay package.
680           continue;
681         }
682       }
683 
684       type_flags |= type_spec.GetFlagsForEntryIndex(local_entry_idx);
685 
686       // If the package is an overlay, then even configurations that are the same MUST be chosen.
687       boolean package_is_overlay = loaded_package.IsOverlay();
688 
689       FilteredConfigGroup filtered_group = loaded_package_impl.filtered_configs_.get(type_idx);
690       if (use_fast_path) {
691         List<ResTable_config> candidate_configs = filtered_group.configurations;
692         int type_count = candidate_configs.size();
693         for (int i = 0; i < type_count; i++) {
694           ResTable_config this_config = candidate_configs.get(i);
695 
696           // We can skip calling ResTable_config.match() because we know that all candidate
697           // configurations that do NOT match have been filtered-out.
698           if ((best_config == null || this_config.isBetterThan(best_config, desired_config)) ||
699               (package_is_overlay && this_config.compare(best_config) == 0)) {
700             // The configuration matches and is better than the previous selection.
701             // Find the entry value if it exists for this configuration.
702             ResTable_type type_chunk = filtered_group.types.get(i);
703             int offset = LoadedPackage.GetEntryOffset(type_chunk, local_entry_idx);
704             if (offset == ResTable_type.NO_ENTRY) {
705               continue;
706             }
707 
708             best_cookie = cookie;
709             best_package = loaded_package;
710             best_type = type_chunk;
711             best_config = this_config;
712             best_offset = offset;
713           }
714         }
715       } else {
716         // This is the slower path, which doesn't use the filtered list of configurations.
717         // Here we must read the ResTable_config from the mmapped APK, convert it to host endianness
718         // and fill in any new fields that did not exist when the APK was compiled.
719         // Furthermore when selecting configurations we can't just record the pointer to the
720         // ResTable_config, we must copy it.
721         // auto iter_end = type_spec.types + type_spec.type_count;
722         //   for (auto iter = type_spec.types; iter != iter_end; ++iter) {
723         for (ResTable_type type : type_spec.types) {
724           ResTable_config this_config = ResTable_config.fromDtoH(type.config);
725 
726           if (this_config.match(desired_config)) {
727             if ((best_config == null || this_config.isBetterThan(best_config, desired_config)) ||
728                 (package_is_overlay && this_config.compare(best_config) == 0)) {
729               // The configuration matches and is better than the previous selection.
730               // Find the entry value if it exists for this configuration.
731               int offset = LoadedPackage.GetEntryOffset(type, local_entry_idx);
732               if (offset == ResTable_type.NO_ENTRY) {
733                 continue;
734               }
735 
736               best_cookie = cookie;
737               best_package = loaded_package;
738               best_type = type;
739               best_config_copy = this_config;
740               best_config = best_config_copy;
741               best_offset = offset;
742             }
743           }
744         }
745       }
746     }
747 
748     if (Util.UNLIKELY(best_cookie.intValue() == kInvalidCookie)) {
749       return K_INVALID_COOKIE;
750     }
751 
752     ResTable_entry best_entry = LoadedPackage.GetEntryFromOffset(best_type, best_offset);
753     if (Util.UNLIKELY(best_entry == null)) {
754       return K_INVALID_COOKIE;
755     }
756 
757     FindEntryResult out_entry_ = new FindEntryResult();
758     out_entry_.entry = best_entry;
759     out_entry_.config = best_config;
760     out_entry_.type_flags = type_flags;
761     out_entry_.type_string_ref = new StringPoolRef(best_package.GetTypeStringPool(), best_type.id - 1);
762     out_entry_.entry_string_ref =
763         new StringPoolRef(best_package.GetKeyStringPool(), best_entry.key.index);
764     out_entry_.dynamic_ref_table = package_group.dynamic_ref_table;
765     out_entry.set(out_entry_);
766     return best_cookie;
767   }
768 
769   // Populates the `out_name` parameter with resource name information.
770   // Utf8 strings are preferred, and only if they are unavailable are
771   // the Utf16 variants populated.
772   // Returns false if the resource was not found or the name was missing/corrupt.
773 //  boolean GetResourceName(int resid, ResourceName* out_name);
GetResourceName(int resid, ResourceName out_name)774   public boolean GetResourceName(int resid, ResourceName out_name) {
775     final Ref<FindEntryResult> entryRef = new Ref<>(null);
776     ApkAssetsCookie cookie =
777         FindEntry(resid, (short) 0 /* density_override */, true /* stop_at_first_match */, entryRef);
778     if (cookie.intValue() == kInvalidCookie) {
779       return false;
780     }
781 
782     final LoadedPackage package_ =
783         apk_assets_.get(cookie.intValue()).GetLoadedArsc().GetPackageById(get_package_id(resid));
784     if (package_ == null) {
785       return false;
786     }
787 
788     out_name.package_ = package_.GetPackageName();
789     // out_name.package_len = out_name.package_.length();
790 
791     FindEntryResult entry = entryRef.get();
792     out_name.type = entry.type_string_ref.string();
793     // out_name.type_len = out_name.type == null ? 0 : out_name.type.length();
794     // out_name.type16 = null;
795     if (out_name.type == null) {
796       // out_name.type16 = entry.type_string_ref.string();
797       // out_name.type_len = out_name.type16 == null ? 0 : out_name.type16.length();
798       // if (out_name.type16 == null) {
799         return false;
800       // }
801     }
802 
803     out_name.entry = entry.entry_string_ref.string();
804     // out_name.entry_len = out_name.entry == null ? 0 : out_name.entry.length();
805     // out_name.entry16 = null;
806     if (out_name.entry == null) {
807       // out_name.entry16 = entry.entry_string_ref.string();
808       // out_name.entry_len = out_name.entry16 == null ? 0 : out_name.entry16.length();
809       // if (out_name.entry16 == null) {
810         return false;
811       // }
812     }
813     return true;
814   }
815 
816   // Populates `out_flags` with the bitmask of configuration axis that this resource varies with.
817   // See ResTable_config for the list of configuration axis.
818   // Returns false if the resource was not found.
819 //  boolean GetResourceFlags(int resid, int* out_flags);
GetResourceFlags(int resid, Ref<Integer> out_flags)820   boolean GetResourceFlags(int resid, Ref<Integer> out_flags) {
821     final Ref<FindEntryResult> entry = new Ref<>(null);
822     ApkAssetsCookie cookie = FindEntry(resid, (short) 0 /* density_override */,
823         false /* stop_at_first_match */, entry);
824     if (cookie.intValue() != kInvalidCookie) {
825       out_flags.set(entry.get().type_flags);
826       // this makes no sense, not a boolean:
827       // return cookie;
828     }
829     // this makes no sense, not a boolean:
830     // return kInvalidCookie;
831 
832     return cookie.intValue() != kInvalidCookie;
833   }
834 
835 
836   // Retrieves the best matching resource with ID `resid`. The resource value is filled into
837   // `out_value` and the configuration for the selected value is populated in `out_selected_config`.
838   // `out_flags` holds the same flags as retrieved with GetResourceFlags().
839   // If `density_override` is non-zero, the configuration to match against is overridden with that
840   // density.
841   //
842   // Returns a valid cookie if the resource was found. If the resource was not found, or if the
843   // resource was a map/bag type, then kInvalidCookie is returned. If `may_be_bag` is false,
844   // this function logs if the resource was a map/bag type before returning kInvalidCookie.
845 //  ApkAssetsCookie GetResource(int resid, boolean may_be_bag, short density_override,
846 //                              Res_value out_value, ResTable_config out_selected_config,
847 //                              int* out_flags);
GetResource(int resid, boolean may_be_bag, short density_override, Ref<Res_value> out_value, final Ref<ResTable_config> out_selected_config, final Ref<Integer> out_flags)848   public ApkAssetsCookie GetResource(int resid, boolean may_be_bag,
849       short density_override, Ref<Res_value> out_value,
850       final Ref<ResTable_config> out_selected_config,
851       final Ref<Integer> out_flags) {
852     final Ref<FindEntryResult> entry = new Ref<>(null);
853     ApkAssetsCookie cookie =
854         FindEntry(resid, density_override, false /* stop_at_first_match */, entry);
855     if (cookie.intValue() == kInvalidCookie) {
856       return K_INVALID_COOKIE;
857     }
858 
859     if (isTruthy(dtohl(entry.get().entry.flags) & ResTable_entry.FLAG_COMPLEX)) {
860       if (!may_be_bag) {
861         System.err.println(String.format("Resource %08x is a complex map type.", resid));
862         return K_INVALID_COOKIE;
863       }
864 
865       // Create a reference since we can't represent this complex type as a Res_value.
866       out_value.set(new Res_value((byte) Res_value.TYPE_REFERENCE, resid));
867       out_selected_config.set(entry.get().config);
868       out_flags.set(entry.get().type_flags);
869       return cookie;
870     }
871 
872     // final Res_value device_value = reinterpret_cast<final Res_value>(
873     //     reinterpret_cast<final byte*>(entry.entry) + dtohs(entry.entry.size));
874     // out_value.copyFrom_dtoh(*device_value);
875     Res_value device_value = entry.get().entry.getResValue();
876     out_value.set(device_value.copy());
877 
878     // Convert the package ID to the runtime assigned package ID.
879     entry.get().dynamic_ref_table.lookupResourceValue(out_value);
880 
881     out_selected_config.set(entry.get().config);
882     out_flags.set(entry.get().type_flags);
883     return cookie;
884   }
885 
886   // Resolves the resource reference in `in_out_value` if the data type is
887   // Res_value::TYPE_REFERENCE.
888   // `cookie` is the ApkAssetsCookie of the reference in `in_out_value`.
889   // `in_out_value` is the reference to resolve. The result is placed back into this object.
890   // `in_out_flags` is the type spec flags returned from calls to GetResource() or
891   // GetResourceFlags(). Configuration flags of the values pointed to by the reference
892   // are OR'd together with `in_out_flags`.
893   // `in_out_config` is populated with the configuration for which the resolved value was defined.
894   // `out_last_reference` is populated with the last reference ID before resolving to an actual
895   // value. This is only initialized if the passed in `in_out_value` is a reference.
896   // Returns the cookie of the APK the resolved resource was defined in, or kInvalidCookie if
897   // it was not found.
898 //  ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value in_out_value,
899 //                                   ResTable_config in_out_selected_config, int* in_out_flags,
900 //                                   int* out_last_reference);
ResolveReference(ApkAssetsCookie cookie, Ref<Res_value> in_out_value, final Ref<ResTable_config> in_out_selected_config, final Ref<Integer> in_out_flags, final Ref<Integer> out_last_reference)901   public ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Ref<Res_value> in_out_value,
902       final Ref<ResTable_config> in_out_selected_config,
903       final Ref<Integer> in_out_flags,
904       final Ref<Integer> out_last_reference) {
905     final int kMaxIterations = 20;
906 
907     for (int iteration = 0; in_out_value.get().dataType == Res_value.TYPE_REFERENCE &&
908         in_out_value.get().data != 0 && iteration < kMaxIterations;
909         iteration++) {
910       out_last_reference.set(in_out_value.get().data);
911       final Ref<Integer> new_flags = new Ref<>(0);
912       cookie = GetResource(in_out_value.get().data, true /*may_be_bag*/, (short) 0 /*density_override*/,
913           in_out_value, in_out_selected_config, new_flags);
914       if (cookie.intValue() == kInvalidCookie) {
915         return K_INVALID_COOKIE;
916       }
917       if (in_out_flags != null) {
918         in_out_flags.set(in_out_flags.get() | new_flags.get());
919       }
920       if (out_last_reference.get() == in_out_value.get().data) {
921         // This reference can't be resolved, so exit now and let the caller deal with it.
922         return cookie;
923       }
924     }
925     return cookie;
926   }
927 
928   // AssetManager2::GetBag(resid) wraps this function to track which resource ids have already
929   // been seen while traversing bag parents.
930   //  final ResolvedBag* GetBag(int resid);
GetBag(int resid)931   public final ResolvedBag GetBag(int resid) {
932     List<Integer> found_resids = new ArrayList<>();
933     return GetBag(resid, found_resids);
934   }
935 
936   // Retrieves the best matching bag/map resource with ID `resid`.
937   // This method will resolve all parent references for this bag and merge keys with the child.
938   // To iterate over the keys, use the following idiom:
939   //
940   //  final ResolvedBag* bag = asset_manager.GetBag(id);
941   //  if (bag != null) {
942   //    for (auto iter = begin(bag); iter != end(bag); ++iter) {
943   //      ...
944   //    }
945   //  }
GetBag(int resid, List<Integer> child_resids)946   ResolvedBag GetBag(int resid, List<Integer> child_resids) {
947     // ATRACE_NAME("AssetManager::GetBag");
948 
949     ResolvedBag cached_iter = cached_bags_.get(resid);
950     if (cached_iter != null) {
951       return cached_iter;
952     }
953 
954     final Ref<FindEntryResult> entryRef = new Ref<>(null);
955     ApkAssetsCookie cookie =
956         FindEntry(resid, (short) 0 /* density_override */, false /* stop_at_first_match */, entryRef);
957     if (cookie.intValue() == kInvalidCookie) {
958       return null;
959     }
960 
961     FindEntryResult entry = entryRef.get();
962 
963     // Check that the size of the entry header is at least as big as
964     // the desired ResTable_map_entry. Also verify that the entry
965     // was intended to be a map.
966     if (dtohs(entry.entry.size) < ResTable_map_entry.BASE_SIZEOF ||
967         (dtohs(entry.entry.flags) & ResourceTypes.ResTable_entry.FLAG_COMPLEX) == 0) {
968       // Not a bag, nothing to do.
969       return null;
970     }
971 
972     // final ResTable_map_entry map = reinterpret_cast<final ResTable_map_entry*>(entry.entry);
973     // final ResTable_map map_entry =
974     //     reinterpret_cast<final ResTable_map*>(reinterpret_cast<final byte*>(map) + map.size);
975     // final ResTable_map map_entry_end = map_entry + dtohl(map.count);
976     final ResTable_map_entry map = new ResTable_map_entry(entry.entry.myBuf(), entry.entry.myOffset());
977     int curOffset = map.myOffset() + map.size;
978     ResTable_map map_entry = null; // = new ResTable_map(map.myBuf(), curOffset);
979     final int map_entry_end =
980         curOffset + dtohl(map.count) * ResTable_map.SIZEOF;
981     if (curOffset < map_entry_end) {
982       map_entry = new ResTable_map(map.myBuf(), curOffset);
983     }
984 
985     // Keep track of ids that have already been seen to prevent infinite loops caused by circular
986     // dependencies between bags
987     child_resids.add(resid);
988 
989     final Ref<Integer> parent_resid = new Ref<>(dtohl(map.parent.ident));
990     if (parent_resid.get() == 0 || child_resids.contains(parent_resid.get())) {
991       // There is no parent or that a circular dependency exist, meaning there is nothing to
992       // inherit and we can do a simple copy of the entries in the map.
993       final int entry_count = (map_entry_end - curOffset) / ResTable_map.SIZEOF;
994       // util.unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
995       //     malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag.Entry))))};
996       ResolvedBag new_bag = new ResolvedBag();
997       ResolvedBag.Entry[] new_entry = new_bag.entries = new Entry[entry_count];
998       int i = 0;
999       for (; curOffset < map_entry_end;
1000           map_entry = new ResTable_map(map_entry.myBuf(), curOffset)) {
1001         final Ref<Integer> new_key = new Ref<>(dtohl(map_entry.name.ident));
1002         if (!is_internal_resid(new_key.get())) {
1003           // Attributes, arrays, etc don't have a resource id as the name. They specify
1004           // other data, which would be wrong to change via a lookup.
1005           if (entry.dynamic_ref_table.lookupResourceId(new_key) != NO_ERROR) {
1006             System.err.println(String.format("Failed to resolve key 0x%08x in bag 0x%08x.", new_key.get(),
1007                 resid));
1008             return null;
1009           }
1010         }
1011         Entry new_entry_ = new_entry[i] = new Entry();
1012         new_entry_.cookie = cookie;
1013         new_entry_.key = new_key.get();
1014         new_entry_.key_pool = null;
1015         new_entry_.type_pool = null;
1016         // BEGIN-INTERNAL
1017         new_entry_.style = resid;
1018         // END-INTERNAL
1019         new_entry_.value = map_entry.value.copy();
1020         final Ref<Res_value> valueRef = new Ref<>(new_entry_.value);
1021         int err = entry.dynamic_ref_table.lookupResourceValue(valueRef);
1022         new_entry_.value = valueRef.get();
1023         if (err != NO_ERROR) {
1024           System.err.println(String.format(
1025               "Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry_.value.dataType,
1026               new_entry_.value.data, new_key.get()));
1027           return null;
1028         }
1029         // ++new_entry;
1030         ++i;
1031 
1032         final int size = dtohs(map_entry.value.size);
1033 //      curOffset += size + sizeof(*map)-sizeof(map->value);
1034         curOffset += size + ResTable_map.SIZEOF-Res_value.SIZEOF;
1035 
1036       }
1037       new_bag.type_spec_flags = entry.type_flags;
1038       new_bag.entry_count = entry_count;
1039       ResolvedBag result = new_bag;
1040       cached_bags_.put(resid, new_bag);
1041       return result;
1042     }
1043 
1044     // In case the parent is a dynamic reference, resolve it.
1045     entry.dynamic_ref_table.lookupResourceId(parent_resid);
1046 
1047     // Get the parent and do a merge of the keys.
1048     final ResolvedBag parent_bag = GetBag(parent_resid.get(), child_resids);
1049     if (parent_bag == null) {
1050       // Failed to get the parent that should exist.
1051       System.err.println(String.format("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid.get(),
1052           resid));
1053       return null;
1054     }
1055 
1056     // Create the max possible entries we can make. Once we construct the bag,
1057     // we will realloc to fit to size.
1058     final int max_count = parent_bag.entry_count + dtohl(map.count);
1059     // util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
1060     //     malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry))))};
1061     ResolvedBag new_bag = new ResolvedBag();
1062     new_bag.entries = new Entry[max_count];
1063     final ResolvedBag.Entry[] new_entry = new_bag.entries;
1064     int newEntryIndex = 0;
1065 
1066   // const ResolvedBag::Entry* parent_entry = parent_bag->entries;
1067     int parentEntryIndex = 0;
1068     // final ResolvedBag.Entry parent_entry_end = parent_entry + parent_bag.entry_count;
1069     final int parentEntryCount = parent_bag.entry_count;
1070 
1071     // The keys are expected to be in sorted order. Merge the two bags.
1072     while (map_entry != null && map_entry.myOffset() != map_entry_end && parentEntryIndex != parentEntryCount) {
1073       final Ref<Integer> child_keyRef = new Ref<>(dtohl(map_entry.name.ident));
1074       if (!is_internal_resid(child_keyRef.get())) {
1075         if (entry.dynamic_ref_table.lookupResourceId(child_keyRef) != NO_ERROR) {
1076           System.err.println(String.format("Failed to resolve key 0x%08x in bag 0x%08x.", child_keyRef.get(),
1077               resid));
1078           return null;
1079         }
1080       }
1081       int child_key = child_keyRef.get();
1082 
1083       Entry parent_entry = parent_bag.entries[parentEntryIndex];
1084       if (parent_entry == null) {
1085         parent_entry = new Entry();
1086       }
1087 
1088       if (child_key <= parent_entry.key) {
1089         // Use the child key if it comes before the parent
1090         // or is equal to the parent (overrides).
1091         Entry new_entry_ = new_entry[newEntryIndex] = new Entry();
1092         new_entry_.cookie = cookie;
1093         new_entry_.key = child_key;
1094         new_entry_.key_pool = null;
1095         new_entry_.type_pool = null;
1096         new_entry_.value = map_entry.value.copy();
1097         // BEGIN-INTERNAL
1098         new_entry_.style = resid;
1099         // END-INTERNAL
1100         final Ref<Res_value> valueRef = new Ref<>(new_entry_.value);
1101         int err = entry.dynamic_ref_table.lookupResourceValue(valueRef);
1102         new_entry_.value = valueRef.get();
1103         if (err != NO_ERROR) {
1104           System.err.println(String.format(
1105               "Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry_.value.dataType,
1106               new_entry_.value.data, child_key));
1107           return null;
1108         }
1109 
1110         // ++map_entry;
1111         map_entry = new ResTable_map(map_entry.myBuf(), map_entry.myOffset() + map_entry.value.size + ResTable_map.SIZEOF - Res_value.SIZEOF);
1112       } else {
1113         // Take the parent entry as-is.
1114         // memcpy(new_entry, parent_entry, sizeof(*new_entry));
1115         new_entry[newEntryIndex] = parent_entry.copy();
1116       }
1117 
1118       if (child_key >= parent_entry.key) {
1119         // Move to the next parent entry if we used it or it was overridden.
1120         // ++parent_entry;
1121         ++parentEntryIndex;
1122         // parent_entry = parent_bag.entries[parentEntryIndex];
1123       }
1124       // Increment to the next entry to fill.
1125       // ++new_entry;
1126       ++newEntryIndex;
1127     }
1128 
1129     // Finish the child entries if they exist.
1130     while (map_entry != null && map_entry.myOffset() != map_entry_end) {
1131       final Ref<Integer> new_key = new Ref<>(map_entry.name.ident);
1132       if (!is_internal_resid(new_key.get())) {
1133         if (entry.dynamic_ref_table.lookupResourceId(new_key) != NO_ERROR) {
1134           System.err.println(String.format("Failed to resolve key 0x%08x in bag 0x%08x.", new_key.get(),
1135               resid));
1136           return null;
1137         }
1138       }
1139       Entry new_entry_ = new_entry[newEntryIndex] = new Entry();
1140       new_entry_.cookie = cookie;
1141       new_entry_.key = new_key.get();
1142       new_entry_.key_pool = null;
1143       new_entry_.type_pool = null;
1144       new_entry_.value = map_entry.value.copy();
1145       // BEGIN-INTERNAL
1146       new_entry_.style = resid;
1147       // END-INTERNAL
1148       final Ref<Res_value> valueRef = new Ref<>(new_entry_.value);
1149       int err = entry.dynamic_ref_table.lookupResourceValue(valueRef);
1150       new_entry_.value = valueRef.get();
1151       if (err != NO_ERROR) {
1152         System.err.println(String.format(
1153             "Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.",
1154             new_entry_.value.dataType,
1155             new_entry_.value.data, new_key.get()));
1156         return null;
1157       }
1158       // ++map_entry;
1159       map_entry = new ResTable_map(map_entry.myBuf(), map_entry.myOffset() + map_entry.value.size + ResTable_map.SIZEOF - Res_value.SIZEOF);
1160       // ++new_entry;
1161       ++newEntryIndex;
1162     }
1163 
1164     // Finish the parent entries if they exist.
1165     while (parentEntryIndex != parent_bag.entry_count) {
1166       // Take the rest of the parent entries as-is.
1167       // final int num_entries_to_copy = parent_entry_end - parent_entry;
1168       // final int num_entries_to_copy = parent_bag.entry_count - parentEntryIndex;
1169       // memcpy(new_entry, parent_entry, num_entries_to_copy * sizeof(*new_entry));
1170       Entry parentEntry = parent_bag.entries[parentEntryIndex];
1171       new_entry[newEntryIndex] = parentEntry == null ? new Entry() : parentEntry.copy();
1172       // new_entry += num_entries_to_copy;
1173       ++newEntryIndex;
1174       ++parentEntryIndex;
1175     }
1176 
1177     // Resize the resulting array to fit.
1178     // final int actual_count = new_entry - new_bag.entries;
1179     final int actual_count = newEntryIndex;
1180     if (actual_count != max_count) {
1181       // new_bag.reset(reinterpret_cast<ResolvedBag*>(realloc(
1182       //     new_bag.release(), sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry)))));
1183       Entry[] resizedEntries = new Entry[actual_count];
1184       System.arraycopy(new_bag.entries, 0, resizedEntries, 0, actual_count);
1185       new_bag.entries = resizedEntries;
1186     }
1187 
1188     // Combine flags from the parent and our own bag.
1189     new_bag.type_spec_flags = entry.type_flags | parent_bag.type_spec_flags;
1190     new_bag.entry_count = actual_count;
1191     ResolvedBag result2 = new_bag;
1192     // cached_bags_[resid] = std::move(new_bag);
1193     cached_bags_.put(resid, new_bag);
1194     return result2;
1195   }
1196 
GetResourceName(int resid)1197   String GetResourceName(int resid) {
1198     ResourceName out_name = new ResourceName();
1199     if (GetResourceName(resid, out_name)) {
1200       return out_name.package_ + ":" + out_name.type + "@" + out_name.entry;
1201     } else {
1202       return null;
1203     }
1204   }
1205 
Utf8ToUtf16(final String str, Ref<String> out)1206   static boolean Utf8ToUtf16(final String str, Ref<String> out) {
1207     throw new UnsupportedOperationException();
1208     // ssize_t len =
1209     //     utf8_to_utf16_length(reinterpret_cast<final byte*>(str.data()), str.size(), false);
1210     // if (len < 0) {
1211     //   return false;
1212     // }
1213     // out.resize(static_cast<int>(len));
1214     // utf8_to_utf16(reinterpret_cast<final byte*>(str.data()), str.size(), &*out.begin(),
1215     //               static_cast<int>(len + 1));
1216     // return true;
1217   }
1218 
1219   // Finds the resource ID assigned to `resource_name`.
1220   // `resource_name` must be of the form '[package:][type/]entry'.
1221   // If no package is specified in `resource_name`, then `fallback_package` is used as the package.
1222   // If no type is specified in `resource_name`, then `fallback_type` is used as the type.
1223   // Returns 0x0 if no resource by that name was found.
1224 //  int GetResourceId(final String resource_name, final String fallback_type = {},
1225 //    final String fallback_package = {});
1226   @SuppressWarnings("NewApi")
GetResourceId(final String resource_name, final String fallback_type, final String fallback_package)1227   public int GetResourceId(final String resource_name,
1228       final String fallback_type,
1229       final String fallback_package) {
1230     final Ref<String> package_name = new Ref<>(null),
1231         type = new Ref<>(null),
1232         entry = new Ref<>(null);
1233     if (!ExtractResourceName(resource_name, package_name, type, entry)) {
1234       return 0;
1235     }
1236 
1237     if (entry.get().isEmpty()) {
1238       return 0;
1239     }
1240 
1241     if (package_name.get().isEmpty()) {
1242       package_name.set(fallback_package);
1243     }
1244 
1245     if (type.get().isEmpty()) {
1246       type.set(fallback_type);
1247     }
1248 
1249     String type16 = type.get();
1250     // if (!Utf8ToUtf16(type, &type16)) {
1251     //   return 0;
1252     // }
1253 
1254     String entry16 = entry.get();
1255     // if (!Utf8ToUtf16(entry, &entry16)) {
1256     //   return 0;
1257     // }
1258 
1259     final String kAttr16 = "attr";
1260     final String kAttrPrivate16 = "^attr-private";
1261 
1262     for (final PackageGroup package_group : package_groups_) {
1263       for (final ConfiguredPackage package_impl : package_group.packages_) {
1264         LoadedPackage package_= package_impl.loaded_package_;
1265         if (!Objects.equals(package_name.get(), package_.GetPackageName())) {
1266           // All packages in the same group are expected to have the same package name.
1267           break;
1268         }
1269 
1270         int resid = package_.FindEntryByName(type16, entry16);
1271         if (resid == 0 && Objects.equals(kAttr16, type16)) {
1272           // Private attributes in libraries (such as the framework) are sometimes encoded
1273           // under the type '^attr-private' in order to leave the ID space of public 'attr'
1274           // free for future additions. Check '^attr-private' for the same name.
1275           resid = package_.FindEntryByName(kAttrPrivate16, entry16);
1276         }
1277 
1278         if (resid != 0) {
1279           return fix_package_id(resid, package_group.dynamic_ref_table.mAssignedPackageId);
1280         }
1281       }
1282     }
1283     return 0;
1284   }
1285 
1286   // Triggers the re-construction of lists of types that match the set configuration.
1287   // This should always be called when mutating the AssetManager's configuration or ApkAssets set.
RebuildFilterList()1288   void RebuildFilterList() {
1289     for (PackageGroup group : package_groups_) {
1290       for (ConfiguredPackage impl : group.packages_) {
1291         // // Destroy it.
1292         // impl.filtered_configs_.~ByteBucketArray();
1293         //
1294         // // Re-create it.
1295         // new (impl.filtered_configs_) ByteBucketArray<FilteredConfigGroup>();
1296         impl.filtered_configs_ =
1297             new ByteBucketArray<FilteredConfigGroup>(new FilteredConfigGroup()) {
1298               @Override
1299               FilteredConfigGroup newInstance() {
1300                 return new FilteredConfigGroup();
1301               }
1302             };
1303 
1304         // Create the filters here.
1305         impl.loaded_package_.ForEachTypeSpec((TypeSpec spec, byte type_index) -> {
1306           FilteredConfigGroup configGroup = impl.filtered_configs_.editItemAt(type_index);
1307           // const auto iter_end = spec->types + spec->type_count;
1308           //   for (auto iter = spec->types; iter != iter_end; ++iter) {
1309           for (ResTable_type iter : spec.types) {
1310             ResTable_config this_config = ResTable_config.fromDtoH(iter.config);
1311             if (this_config.match(configuration_)) {
1312               configGroup.configurations.add(this_config);
1313               configGroup.types.add(iter);
1314             }
1315           }
1316         });
1317       }
1318     }
1319   }
1320 
1321   // Purge all resources that are cached and vary by the configuration axis denoted by the
1322   // bitmask `diff`.
1323 //  void InvalidateCaches(int diff);
InvalidateCaches(int diff)1324   private void InvalidateCaches(int diff) {
1325     if (diff == 0xffffffff) {
1326       // Everything must go.
1327       cached_bags_.clear();
1328       return;
1329     }
1330 
1331     // Be more conservative with what gets purged. Only if the bag has other possible
1332     // variations with respect to what changed (diff) should we remove it.
1333     // for (auto iter = cached_bags_.cbegin(); iter != cached_bags_.cend();) {
1334     for (Integer key : new ArrayList<>(cached_bags_.keySet())) {
1335       // if (diff & iter.second.type_spec_flags) {
1336       if (isTruthy(diff & cached_bags_.get(key).type_spec_flags)) {
1337         // iter = cached_bags_.erase(iter);
1338         cached_bags_.remove(key);
1339       }
1340     }
1341   }
1342 
1343   // Creates a new Theme from this AssetManager.
1344 //  std.unique_ptr<Theme> NewTheme();
NewTheme()1345   public Theme NewTheme() {
1346     return new Theme(this);
1347   }
1348 
1349   public static class Theme {
1350     //  friend class AssetManager2;
1351 //
1352 // public:
1353 //
1354 //
1355 //
1356 //  final AssetManager2* GetAssetManager() { return asset_manager_; }
1357 //
GetAssetManager()1358     public CppAssetManager2 GetAssetManager() { return asset_manager_; }
1359     //
1360 //  // Returns a bit mask of configuration changes that will impact this
1361 //  // theme (and thus require completely reloading it).
GetChangingConfigurations()1362     public int GetChangingConfigurations() { return type_spec_flags_; }
1363 
1364 // private:
1365 //  private DISALLOW_COPY_AND_ASSIGN(Theme);
1366 
1367     // Called by AssetManager2.
1368 //  private explicit Theme(AssetManager2* asset_manager) : asset_manager_(asset_manager) {}
1369 
1370     private final CppAssetManager2 asset_manager_;
1371     private int type_spec_flags_ = 0;
1372     //  std.array<std.unique_ptr<Package>, kPackageCount> packages_;
1373     private Package[] packages_ = new Package[kPackageCount];
1374 
Theme(CppAssetManager2 cppAssetManager2)1375     public Theme(CppAssetManager2 cppAssetManager2) {
1376       asset_manager_ = cppAssetManager2;
1377     }
1378 
1379     private static class ThemeEntry {
1380       static final int SIZEOF = 8 + Res_value.SIZEOF;
1381 
1382       ApkAssetsCookie cookie;
1383       int type_spec_flags;
1384       Res_value value;
1385     }
1386 
1387     private static class ThemeType {
1388       static final int SIZEOF_WITHOUT_ENTRIES = 8;
1389 
1390       int entry_count;
1391       ThemeEntry entries[];
1392     }
1393 
1394     //  static final int kPackageCount = std.numeric_limits<byte>.max() + 1;
1395     static final int kPackageCount = 256;
1396     //  static final int kTypeCount = std.numeric_limits<byte>.max() + 1;
1397     static final int kTypeCount = 256;
1398 
1399     private static class Package {
1400       // Each element of Type will be a dynamically sized object
1401       // allocated to have the entries stored contiguously with the Type.
1402       // std::array<util::unique_cptr<ThemeType>, kTypeCount> types;
1403       ThemeType[] types = new ThemeType[kTypeCount];
1404     }
1405 
1406     // Applies the style identified by `resid` to this theme. This can be called
1407     // multiple times with different styles. By default, any theme attributes that
1408     // are already defined before this call are not overridden. If `force` is set
1409     // to true, this behavior is changed and all theme attributes from the style at
1410     // `resid` are applied.
1411     // Returns false if the style failed to apply.
1412 //  boolean ApplyStyle(int resid, boolean force = false);
ApplyStyle(int resid, boolean force)1413     public boolean ApplyStyle(int resid, boolean force) {
1414       // ATRACE_NAME("Theme::ApplyStyle");
1415 
1416       final ResolvedBag bag = asset_manager_.GetBag(resid);
1417       if (bag == null) {
1418         return false;
1419       }
1420 
1421       // Merge the flags from this style.
1422       type_spec_flags_ |= bag.type_spec_flags;
1423 
1424       int last_type_idx = -1;
1425       int last_package_idx = -1;
1426       Package last_package = null;
1427       ThemeType last_type = null;
1428 
1429       // Iterate backwards, because each bag is sorted in ascending key ID order, meaning we will only
1430       // need to perform one resize per type.
1431       //     using reverse_bag_iterator = std::reverse_iterator<const ResolvedBag::Entry*>;
1432       // const auto bag_iter_end = reverse_bag_iterator(begin(bag));
1433       //     for (auto bag_iter = reverse_bag_iterator(end(bag)); bag_iter != bag_iter_end; ++bag_iter) {
1434       List<Entry> bagEntries = new ArrayList<>(Arrays.asList(bag.entries));
1435       Collections.reverse(bagEntries);
1436       for (ResolvedBag.Entry bag_iter : bagEntries) {
1437         //   final int attr_resid = bag_iter.key;
1438         final int attr_resid = bag_iter == null ? 0 : bag_iter.key;
1439 
1440         // If the resource ID passed in is not a style, the key can be some other identifier that is not
1441         // a resource ID. We should fail fast instead of operating with strange resource IDs.
1442         if (!is_valid_resid(attr_resid)) {
1443           return false;
1444         }
1445 
1446         // We don't use the 0-based index for the type so that we can avoid doing ID validation
1447         // upon lookup. Instead, we keep space for the type ID 0 in our data structures. Since
1448         // the construction of this type is guarded with a resource ID check, it will never be
1449         // populated, and querying type ID 0 will always fail.
1450         int package_idx = get_package_id(attr_resid);
1451         int type_idx = get_type_id(attr_resid);
1452         int entry_idx = get_entry_id(attr_resid);
1453 
1454         if (last_package_idx != package_idx) {
1455           Package package_ = packages_[package_idx];
1456           if (package_ == null) {
1457             package_ = packages_[package_idx] = new Package();
1458           }
1459           last_package_idx = package_idx;
1460           last_package = package_;
1461           last_type_idx = -1;
1462         }
1463 
1464         if (last_type_idx != type_idx) {
1465           ThemeType type = last_package.types[type_idx];
1466           if (type == null) {
1467             // Allocate enough memory to contain this entry_idx. Since we're iterating in reverse over
1468             // a sorted list of attributes, this shouldn't be resized again during this method call.
1469             // type.reset(reinterpret_cast<ThemeType*>(
1470             //     calloc(sizeof(ThemeType) + (entry_idx + 1) * sizeof(ThemeEntry), 1)));
1471             type = last_package.types[type_idx] = new ThemeType();
1472             type.entries = new ThemeEntry[entry_idx + 1];
1473             type.entry_count = entry_idx + 1;
1474           } else if (entry_idx >= type.entry_count) {
1475             // Reallocate the memory to contain this entry_idx. Since we're iterating in reverse over
1476             // a sorted list of attributes, this shouldn't be resized again during this method call.
1477             int new_count = entry_idx + 1;
1478             // type.reset(reinterpret_cast<ThemeType*>(
1479             //     realloc(type.release(), sizeof(ThemeType) + (new_count * sizeof(ThemeEntry)))));
1480             ThemeEntry[] oldEntries = type.entries;
1481             type.entries = new ThemeEntry[new_count];
1482             System.arraycopy(oldEntries, 0, type.entries, 0, oldEntries.length);
1483 
1484             // Clear out the newly allocated space (which isn't zeroed).
1485             // memset(type.entries + type.entry_count, 0,
1486             //     (new_count - type.entry_count) * sizeof(ThemeEntry));
1487             type.entry_count = new_count;
1488           }
1489           last_type_idx = type_idx;
1490           last_type = type;
1491         }
1492 
1493         ThemeEntry entry = last_type.entries[entry_idx];
1494         if (entry == null) {
1495           entry = last_type.entries[entry_idx] = new ThemeEntry();
1496           entry.value = new Res_value();
1497         }
1498         if (force || (entry.value.dataType == Res_value.TYPE_NULL &&
1499             entry.value.data != Res_value.DATA_NULL_EMPTY)) {
1500           entry.cookie = bag_iter.cookie;
1501           entry.type_spec_flags |= bag.type_spec_flags;
1502           entry.value = bag_iter.value;
1503         }
1504       }
1505       return true;
1506     }
1507 
1508     // Retrieve a value in the theme. If the theme defines this value, returns an asset cookie
1509     // indicating which ApkAssets it came from and populates `out_value` with the value.
1510     // `out_flags` is populated with a bitmask of the configuration axis with which the resource
1511     // varies.
1512     //
1513     // If the attribute is not found, returns kInvalidCookie.
1514     //
1515     // NOTE: This function does not do reference traversal. If you want to follow references to other
1516     // resources to get the "real" value to use, you need to call ResolveReference() after this
1517     // function.
1518 //  ApkAssetsCookie GetAttribute(int resid, Res_value* out_value,
1519 //                               int* out_flags) const;
GetAttribute(int resid, Ref<Res_value> out_value, final Ref<Integer> out_flags)1520     public ApkAssetsCookie GetAttribute(int resid, Ref<Res_value> out_value,
1521         final Ref<Integer> out_flags) {
1522       int cnt = 20;
1523 
1524       int type_spec_flags = 0;
1525 
1526       do {
1527         int package_idx = get_package_id(resid);
1528         Package package_ = packages_[package_idx];
1529         if (package_ != null) {
1530           // The themes are constructed with a 1-based type ID, so no need to decrement here.
1531           int type_idx = get_type_id(resid);
1532           ThemeType type = package_.types[type_idx];
1533           if (type != null) {
1534             int entry_idx = get_entry_id(resid);
1535             if (entry_idx < type.entry_count) {
1536               ThemeEntry entry = type.entries[entry_idx];
1537               if (entry == null) {
1538                 entry = new ThemeEntry();
1539                 entry.value = new Res_value();
1540               }
1541               type_spec_flags |= entry.type_spec_flags;
1542 
1543               if (entry.value.dataType == Res_value.TYPE_ATTRIBUTE) {
1544                 if (cnt > 0) {
1545                   cnt--;
1546                   resid = entry.value.data;
1547                   continue;
1548                 }
1549                 return K_INVALID_COOKIE;
1550               }
1551 
1552               // @null is different than @empty.
1553               if (entry.value.dataType == Res_value.TYPE_NULL &&
1554                   entry.value.data != Res_value.DATA_NULL_EMPTY) {
1555                 return K_INVALID_COOKIE;
1556               }
1557 
1558               out_value.set(entry.value);
1559               out_flags.set(type_spec_flags);
1560               return entry.cookie;
1561             }
1562           }
1563         }
1564         break;
1565       } while (true);
1566       return K_INVALID_COOKIE;
1567     }
1568 
1569     // This is like ResolveReference(), but also takes
1570     // care of resolving attribute references to the theme.
1571 //  ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value,
1572 //                                            ResTable_config in_out_selected_config = null,
1573 //                                            int* in_out_type_spec_flags = null,
1574 //                                            int* out_last_ref = null);
ResolveAttributeReference(ApkAssetsCookie cookie, Ref<Res_value> in_out_value, final Ref<ResTable_config> in_out_selected_config, final Ref<Integer> in_out_type_spec_flags, final Ref<Integer> out_last_ref)1575     ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Ref<Res_value> in_out_value,
1576         final Ref<ResTable_config> in_out_selected_config,
1577         final Ref<Integer> in_out_type_spec_flags,
1578         final Ref<Integer> out_last_ref) {
1579       if (in_out_value.get().dataType == Res_value.TYPE_ATTRIBUTE) {
1580         final Ref<Integer> new_flags = new Ref<>(0);
1581         cookie = GetAttribute(in_out_value.get().data, in_out_value, new_flags);
1582         if (cookie.intValue() == kInvalidCookie) {
1583           return K_INVALID_COOKIE;
1584         }
1585 
1586         if (in_out_type_spec_flags != null) {
1587 //          *in_out_type_spec_flags |= new_flags;
1588           in_out_type_spec_flags.set(in_out_type_spec_flags.get() | new_flags.get());
1589         }
1590       }
1591       return asset_manager_.ResolveReference(cookie, in_out_value, in_out_selected_config,
1592           in_out_type_spec_flags, out_last_ref);
1593     }
1594 
1595     //  void Clear();
Clear()1596     public void Clear() {
1597       type_spec_flags_ = 0;
1598       for (int i = 0; i < packages_.length; i++) {
1599 //        package_.reset();
1600         packages_[i] = null;
1601       }
1602     }
1603 
1604     // Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme.
1605     // Returns false if the AssetManagers of the Themes were not compatible.
1606 //  boolean SetTo(final Theme& o);
SetTo(final Theme o)1607     public boolean SetTo(final Theme o) {
1608       if (this == o) {
1609         return true;
1610       }
1611 
1612       type_spec_flags_ = o.type_spec_flags_;
1613 
1614       boolean copy_only_system = asset_manager_ != o.asset_manager_;
1615 
1616       // for (int p = 0; p < packages_.size(); p++) {
1617       //   final Package package_ = o.packages_[p].get();
1618       for (int p = 0; p < packages_.length; p++) {
1619         Package package_ = o.packages_[p];
1620         if (package_ == null || (copy_only_system && p != 0x01)) {
1621           // The other theme doesn't have this package, clear ours.
1622           packages_[p] = new Package();
1623           continue;
1624         }
1625 
1626         if (packages_[p] == null) {
1627           // The other theme has this package, but we don't. Make one.
1628           packages_[p] = new Package();
1629         }
1630 
1631         // for (int t = 0; t < package_.types.size(); t++) {
1632         // final Type type = package_.types[t].get();
1633         for (int t = 0; t < package_.types.length; t++) {
1634           ThemeType type = package_.types[t];
1635           if (type == null) {
1636             // The other theme doesn't have this type, clear ours.
1637             // packages_[p].types[t].reset();
1638             continue;
1639           }
1640 
1641           // Create a new type and update it to theirs.
1642           // const size_t type_alloc_size = sizeof(ThemeType) + (type->entry_count * sizeof(ThemeEntry));
1643           // void* copied_data = malloc(type_alloc_size);
1644           ThemeType copied_data = new ThemeType();
1645           copied_data.entry_count = type.entry_count;
1646           // memcpy(copied_data, type, type_alloc_size);
1647           ThemeEntry[] newEntries = copied_data.entries = new ThemeEntry[type.entry_count];
1648           for (int i = 0; i < type.entry_count; i++) {
1649             ThemeEntry entry = type.entries[i];
1650             ThemeEntry newEntry = new ThemeEntry();
1651             if (entry != null) {
1652               newEntry.cookie = entry.cookie;
1653               newEntry.type_spec_flags = entry.type_spec_flags;
1654               newEntry.value = entry.value.copy();
1655             } else {
1656               newEntry.value = Res_value.NULL_VALUE;
1657             }
1658             newEntries[i] = newEntry;
1659           }
1660 
1661           packages_[p].types[t] = copied_data;
1662           // packages_[p].types[t].reset(reinterpret_cast<Type*>(copied_data));
1663         }
1664       }
1665       return true;
1666     }
1667 
1668 //
1669   }  // namespace android
1670 
getAssetPaths()1671   public List<AssetPath> getAssetPaths() {
1672     ArrayList<AssetPath> assetPaths = new ArrayList<>(apk_assets_.size());
1673     for (CppApkAssets apkAssets : apk_assets_) {
1674       FsFile fsFile = Fs.newFile(apkAssets.GetPath());
1675       assetPaths.add(new AssetPath(fsFile, apkAssets.GetLoadedArsc().IsSystem()));
1676     }
1677     return assetPaths;
1678   }
1679 
1680 }
1681