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