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