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