From 970bd8d2835b05237c4561bd6c12329e26f136b3 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 25 Sep 2017 13:21:55 -0700 Subject: AssetManager2: Implement IDMAP support This enables RRO (runtime resource overlays) with AssetManager2 Test: make libandroidfw_tests Test: out/host//nativetest64/libandroidfw_tests/libandroidfw_tests --testdata=frameworks/base/libs/androidfw/tests/data Change-Id: Id8079104faefbfaa3f4017d8f7ee1a8968f151a2 --- libs/androidfw/LoadedArsc.cpp | 102 +++++++++++++++++++++++++++++++++--------- 1 file changed, 81 insertions(+), 21 deletions(-) (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index bd7b80469ddc..b62fc813350e 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -37,7 +37,7 @@ #include "androidfw/ResourceUtils.h" #include "androidfw/Util.h" -using android::base::StringPrintf; +using ::android::base::StringPrintf; namespace android { @@ -61,6 +61,10 @@ struct TypeSpec { // and under which configurations it varies. const ResTable_typeSpec* type_spec; + // Pointer to the mmapped data where the IDMAP mappings for this type + // exist. May be nullptr if no IDMAP exists. + const IdmapEntry_header* idmap_entries; + // The number of types that follow this struct. // There is a type for each configuration // that entries are defined for. @@ -84,7 +88,10 @@ namespace { // the Type structs. class TypeSpecPtrBuilder { public: - TypeSpecPtrBuilder(const ResTable_typeSpec* header) : header_(header) {} + explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header, + const IdmapEntry_header* idmap_header) + : header_(header), idmap_header_(idmap_header) { + } void AddType(const ResTable_type* type) { ResTable_config config; @@ -99,6 +106,7 @@ class TypeSpecPtrBuilder { } TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type))); type_spec->type_spec = header_; + type_spec->idmap_entries = idmap_header_; type_spec->type_count = types_.size(); memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type)); return TypeSpecPtr(type_spec); @@ -108,6 +116,7 @@ class TypeSpecPtrBuilder { DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder); const ResTable_typeSpec* header_; + const IdmapEntry_header* idmap_header_; std::vector types_; }; @@ -125,6 +134,14 @@ bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTab return false; } + // If there is an IDMAP supplied with this package, translate the entry ID. + if (ptr->idmap_entries != nullptr) { + if (!LoadedIdmap::Lookup(ptr->idmap_entries, entry_idx, &entry_idx)) { + // There is no mapping, so the resource is not meant to be in this overlay package. + return false; + } + } + // Don't bother checking if the entry ID is larger than // the number of entries. if (entry_idx >= dtohl(ptr->type_spec->entryCount)) { @@ -225,7 +242,7 @@ static bool VerifyType(const Chunk& chunk) { const size_t entries_offset = dtohl(header->entriesStart); const size_t offsets_length = sizeof(uint32_t) * entry_count; - if (offsets_offset + offsets_length > entries_offset) { + if (offsets_offset > entries_offset || entries_offset - offsets_offset < offsets_length) { LOG(ERROR) << "Entry offsets overlap actual entry data."; return false; } @@ -269,13 +286,13 @@ static bool VerifyType(const Chunk& chunk) { reinterpret_cast(header) + offset); const size_t entry_size = dtohs(entry->size); if (entry_size < sizeof(*entry)) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " is too small."; + LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << i << " is too small."; return false; } // Check the declared entrySize. if (entry_size > chunk.size() || offset > chunk.size() - entry_size) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " is too large."; + LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << i << " is too large."; return false; } @@ -286,13 +303,13 @@ static bool VerifyType(const Chunk& chunk) { size_t map_entries_start = offset + entry_size; if (map_entries_start & 0x03) { - LOG(ERROR) << "Map entries start at unaligned offset."; + LOG(ERROR) << "Map entries at index " << i << " start at unaligned offset."; return false; } // Each entry is sizeof(ResTable_map) big. if (map_entry_count > ((chunk.size() - map_entries_start) / sizeof(ResTable_map))) { - LOG(ERROR) << "Too many map entries in ResTable_map_entry."; + LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << i << "."; return false; } @@ -300,7 +317,9 @@ static bool VerifyType(const Chunk& chunk) { } else { // There needs to be room for one Res_value struct. if (offset + entry_size > chunk.size() - sizeof(Res_value)) { - LOG(ERROR) << "No room for Res_value after ResTable_entry."; + LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << i << " for type " + << (int)header->id << " with config " << header->config.toString().string() + << "."; return false; } @@ -308,12 +327,12 @@ static bool VerifyType(const Chunk& chunk) { reinterpret_cast(entry) + entry_size); const size_t value_size = dtohs(value->size); if (value_size < sizeof(Res_value)) { - LOG(ERROR) << "Res_value is too small."; + LOG(ERROR) << "Res_value at index " << i << " is too small."; return false; } if (value_size > chunk.size() || offset + entry_size > chunk.size() - value_size) { - LOG(ERROR) << "Res_value size is too large."; + LOG(ERROR) << "Res_value size " << value_size << " at index " << i << " is too large."; return false; } } @@ -412,10 +431,13 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } -std::unique_ptr LoadedPackage::Load(const Chunk& chunk) { +std::unique_ptr LoadedPackage::Load(const Chunk& chunk, + const LoadedIdmap* loaded_idmap) { ATRACE_CALL(); std::unique_ptr loaded_package{new LoadedPackage()}; + // typeIdOffset was added at some point, but we still must recognize apps built before this + // was added. constexpr size_t kMinPackageSize = sizeof(ResTable_package) - sizeof(ResTable_package::typeIdOffset); const ResTable_package* header = chunk.header(); @@ -430,6 +452,12 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk) { loaded_package->dynamic_ = true; } + if (loaded_idmap != nullptr) { + // This is an overlay and so it needs to pretend to be the target package. + loaded_package->package_id_ = loaded_idmap->TargetPackageId(); + loaded_package->overlay_ = true; + } + if (header->header.headerSize >= sizeof(ResTable_package)) { uint32_t type_id_offset = dtohl(header->typeIdOffset); if (type_id_offset > std::numeric_limits::max()) { @@ -490,7 +518,16 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk) { LOG(ERROR) << "Too many type configurations, overflow detected."; return {}; } - loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); + + // We only add the type to the package if there is no IDMAP, or if the type is + // overlaying something. + if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) { + // If this is an overlay, insert it at the target type ID. + if (type_spec_ptr->idmap_entries != nullptr) { + last_type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; + } + loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); + } types_builder = {}; last_type_idx = 0; @@ -531,7 +568,15 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk) { } last_type_idx = type_spec->id - 1; - types_builder = util::make_unique(type_spec); + + // If this is an overlay, associate the mapping of this type to the target type + // from the IDMAP. + const IdmapEntry_header* idmap_entry_header = nullptr; + if (loaded_idmap != nullptr) { + idmap_entry_header = loaded_idmap->GetEntryMapForType(type_spec->id); + } + + types_builder = util::make_unique(type_spec, idmap_entry_header); } break; case RES_TABLE_TYPE_TYPE: { @@ -608,7 +653,16 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk) { LOG(ERROR) << "Too many type configurations, overflow detected."; return {}; } - loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); + + // We only add the type to the package if there is no IDMAP, or if the type is + // overlaying something. + if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) { + // If this is an overlay, insert it at the target type ID. + if (type_spec_ptr->idmap_entries != nullptr) { + last_type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; + } + loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); + } } if (iter.HadError()) { @@ -618,7 +672,8 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk) { return loaded_package; } -bool LoadedArsc::LoadTable(const Chunk& chunk, bool load_as_shared_library) { +bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, + bool load_as_shared_library) { ATRACE_CALL(); const ResTable_header* header = chunk.header(); if (header == nullptr) { @@ -652,13 +707,13 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, bool load_as_shared_library) { case RES_TABLE_PACKAGE_TYPE: { if (packages_seen + 1 > package_count) { LOG(ERROR) << "More package chunks were found than the " << package_count - << " declared in the " - "header."; + << " declared in the header."; return false; } packages_seen++; - std::unique_ptr loaded_package = LoadedPackage::Load(child_chunk); + std::unique_ptr loaded_package = + LoadedPackage::Load(child_chunk, loaded_idmap); if (!loaded_package) { return false; } @@ -684,7 +739,8 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, bool load_as_shared_library) { return true; } -std::unique_ptr LoadedArsc::Load(const void* data, size_t len, bool system, +std::unique_ptr LoadedArsc::Load(const StringPiece& data, + const LoadedIdmap* loaded_idmap, bool system, bool load_as_shared_library) { ATRACE_CALL(); @@ -692,12 +748,12 @@ std::unique_ptr LoadedArsc::Load(const void* data, size_t len, std::unique_ptr loaded_arsc(new LoadedArsc()); loaded_arsc->system_ = system; - ChunkIterator iter(data, len); + ChunkIterator iter(data.data(), data.size()); while (iter.HasNext()) { const Chunk chunk = iter.Next(); switch (chunk.type()) { case RES_TABLE_TYPE: - if (!loaded_arsc->LoadTable(chunk, load_as_shared_library)) { + if (!loaded_arsc->LoadTable(chunk, loaded_idmap, load_as_shared_library)) { return {}; } break; @@ -717,4 +773,8 @@ std::unique_ptr LoadedArsc::Load(const void* data, size_t len, return std::move(loaded_arsc); } +std::unique_ptr LoadedArsc::CreateEmpty() { + return std::unique_ptr(new LoadedArsc()); +} + } // namespace android -- cgit v1.2.3-59-g8ed1b From 1a1e9c272459f05c846a03fc15989ff7e492517e Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Fri, 13 Oct 2017 15:45:34 -0700 Subject: AssetManager2: Run ApkAssets that have failed verification ApkAssets who have failed verification should still run for compatibility. Not all resources are accessed, and therefore errors in the APK are not necessarily fatal. However, this means we must do bounds checks when retrieving resources, which is slower. Test: make libandroidfw_tests && $ANDROID_BUILD_TOP/out/host//nativetest64/libandroidfw_tests/libandroidfw_tests Test: make libandroidfw_benchmarks && adb sync system && adb sync data && /data/benchmarktest64/libandroidfw_benchmarks/libandroidfw_benchmarks Change-Id: I4cc926c064bca0491785d82cdac0419d74d7d9b0 --- libs/androidfw/AssetManager2.cpp | 80 ++--- libs/androidfw/LoadedArsc.cpp | 362 ++++++++++++--------- libs/androidfw/ResourceTypes.cpp | 3 - libs/androidfw/include/androidfw/AssetManager2.h | 17 +- libs/androidfw/include/androidfw/LoadedArsc.h | 51 ++- libs/androidfw/include/androidfw/ResourceTypes.h | 12 +- libs/androidfw/tests/ApkAssets_test.cpp | 22 +- libs/androidfw/tests/AssetManager2_bench.cpp | 32 ++ libs/androidfw/tests/AssetManager2_test.cpp | 28 ++ libs/androidfw/tests/LoadedArsc_test.cpp | 37 +-- libs/androidfw/tests/data/unverified/R.h | 32 ++ .../androidfw/tests/data/unverified/unverified.apk | Bin 0 -> 3874 bytes 12 files changed, 406 insertions(+), 270 deletions(-) create mode 100644 libs/androidfw/tests/data/unverified/R.h create mode 100644 libs/androidfw/tests/data/unverified/unverified.apk (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index c47db69a48b6..83c82af4385c 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -264,9 +264,7 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, - bool stop_at_first_match, LoadedArscEntry* out_entry, - ResTable_config* out_selected_config, - uint32_t* out_flags) { + bool stop_at_first_match, FindEntryResult* out_entry) { ATRACE_CALL(); // Might use this if density_override != 0. @@ -295,30 +293,28 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri return kInvalidCookie; } - LoadedArscEntry best_entry; - ResTable_config best_config; + FindEntryResult best_entry; ApkAssetsCookie best_cookie = kInvalidCookie; uint32_t cumulated_flags = 0u; const PackageGroup& package_group = package_groups_[idx]; const size_t package_count = package_group.packages_.size(); for (size_t i = 0; i < package_count; i++) { - LoadedArscEntry current_entry; - ResTable_config current_config; - uint32_t current_flags = 0; - const LoadedPackage* loaded_package = package_group.packages_[i]; - if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry, - ¤t_config, ¤t_flags)) { + + FindEntryResult current_entry; + if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry)) { continue; } - cumulated_flags |= current_flags; + cumulated_flags |= current_entry.type_flags; - if (best_cookie == kInvalidCookie || current_config.isBetterThan(best_config, desired_config) || - (loaded_package->IsOverlay() && current_config.compare(best_config) == 0)) { + const ResTable_config* current_config = current_entry.config; + const ResTable_config* best_config = best_entry.config; + if (best_cookie == kInvalidCookie || + current_config->isBetterThan(*best_config, desired_config) || + (loaded_package->IsOverlay() && current_config->compare(*best_config) == 0)) { best_entry = current_entry; - best_config = current_config; best_cookie = package_group.cookies_[i]; if (stop_at_first_match) { break; @@ -332,19 +328,16 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri *out_entry = best_entry; out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; - *out_selected_config = best_config; - *out_flags = cumulated_flags; + out_entry->type_flags = cumulated_flags; return best_cookie; } bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { ATRACE_CALL(); - LoadedArscEntry entry; - ResTable_config config; - uint32_t flags = 0u; - ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, - true /* stop_at_first_match */, &entry, &config, &flags); + FindEntryResult entry; + ApkAssetsCookie cookie = + FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */, &entry); if (cookie == kInvalidCookie) { return false; } @@ -378,11 +371,14 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { } bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { - LoadedArscEntry entry; - ResTable_config config; - ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, - false /* stop_at_first_match */, &entry, &config, out_flags); - return cookie != kInvalidCookie; + FindEntryResult entry; + ApkAssetsCookie cookie = + FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); + if (cookie != kInvalidCookie) { + *out_flags = entry.type_flags; + return cookie; + } + return kInvalidCookie; } ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, @@ -391,11 +387,9 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, uint32_t* out_flags) { ATRACE_CALL(); - LoadedArscEntry entry; - ResTable_config config; - uint32_t flags = 0u; + FindEntryResult entry; ApkAssetsCookie cookie = - FindEntry(resid, density_override, false /* stop_at_first_match */, &entry, &config, &flags); + FindEntry(resid, density_override, false /* stop_at_first_match */, &entry); if (cookie == kInvalidCookie) { return kInvalidCookie; } @@ -409,8 +403,8 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Create a reference since we can't represent this complex type as a Res_value. out_value->dataType = Res_value::TYPE_REFERENCE; out_value->data = resid; - *out_selected_config = config; - *out_flags = flags; + *out_selected_config = *entry.config; + *out_flags = entry.type_flags; return cookie; } @@ -421,8 +415,8 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Convert the package ID to the runtime assigned package ID. entry.dynamic_ref_table->lookupResourceValue(out_value); - *out_selected_config = config; - *out_flags = flags; + *out_selected_config = *entry.config; + *out_flags = entry.type_flags; return cookie; } @@ -465,11 +459,9 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { return cached_iter->second.get(); } - LoadedArscEntry entry; - ResTable_config config; - uint32_t flags = 0u; - ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, - false /* stop_at_first_match */, &entry, &config, &flags); + FindEntryResult entry; + ApkAssetsCookie cookie = + FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); if (cookie == kInvalidCookie) { return nullptr; } @@ -520,7 +512,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { } ++new_entry; } - new_bag->type_spec_flags = flags; + new_bag->type_spec_flags = entry.type_flags; new_bag->entry_count = static_cast(entry_count); ResolvedBag* result = new_bag.get(); cached_bags_[resid] = std::move(new_bag); @@ -538,9 +530,6 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { return nullptr; } - // Combine flags from the parent and our own bag. - flags |= parent_bag->type_spec_flags; - // Create the max possible entries we can make. Once we construct the bag, // we will realloc to fit to size. const size_t max_count = parent_bag->entry_count + dtohl(map->count); @@ -629,7 +618,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { new_bag.release(), sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry))))); } - new_bag->type_spec_flags = flags; + // Combine flags from the parent and our own bag. + new_bag->type_spec_flags = entry.type_flags | parent_bag->type_spec_flags; new_bag->entry_count = static_cast(actual_count); ResolvedBag* result = new_bag.get(); cached_bags_[resid] = std::move(new_bag); diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index b62fc813350e..c361ea2dbe0a 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -122,53 +122,151 @@ class TypeSpecPtrBuilder { } // namespace -bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, - LoadedArscEntry* out_entry, ResTable_config* out_selected_config, - uint32_t* out_flags) const { - ATRACE_CALL(); +LoadedPackage::LoadedPackage() = default; +LoadedPackage::~LoadedPackage() = default; - // If the type IDs are offset in this package, we need to take that into account when searching - // for a type. - const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; - if (ptr == nullptr) { +// Precondition: The header passed in has already been verified, so reading any fields and trusting +// the ResChunk_header is safe. +static bool VerifyResTableType(const ResTable_type* header) { + const size_t entry_count = dtohl(header->entryCount); + if (entry_count > std::numeric_limits::max()) { + LOG(ERROR) << "Too many entries in RES_TABLE_TYPE_TYPE."; return false; } - // If there is an IDMAP supplied with this package, translate the entry ID. - if (ptr->idmap_entries != nullptr) { - if (!LoadedIdmap::Lookup(ptr->idmap_entries, entry_idx, &entry_idx)) { - // There is no mapping, so the resource is not meant to be in this overlay package. - return false; - } + // Make sure that there is enough room for the entry offsets. + const size_t offsets_offset = dtohs(header->header.headerSize); + const size_t entries_offset = dtohl(header->entriesStart); + const size_t offsets_length = sizeof(uint32_t) * entry_count; + + if (offsets_offset > entries_offset || entries_offset - offsets_offset < offsets_length) { + LOG(ERROR) << "Entry offsets overlap actual entry data."; + return false; } - // Don't bother checking if the entry ID is larger than - // the number of entries. - if (entry_idx >= dtohl(ptr->type_spec->entryCount)) { + if (entries_offset > dtohl(header->header.size)) { + LOG(ERROR) << "Entry offsets extend beyond chunk."; + return false; + } + + if (entries_offset & 0x03) { + LOG(ERROR) << "Entries start at unaligned address."; + return false; + } + return true; +} + +static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset, + size_t entry_idx) { + // Check that the offset is aligned. + if (entry_offset & 0x03) { + LOG(ERROR) << "Entry offset at index " << entry_idx << " is not 4-byte aligned."; + return false; + } + + // Check that the offset doesn't overflow. + if (entry_offset > std::numeric_limits::max() - dtohl(type->entriesStart)) { + // Overflow in offset. + LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large."; + return false; + } + + const size_t chunk_size = dtohl(type->header.size); + + entry_offset += dtohl(type->entriesStart); + if (entry_offset > chunk_size - sizeof(ResTable_entry)) { + LOG(ERROR) << "Entry offset at index " << entry_idx + << " is too large. No room for ResTable_entry."; + return false; + } + + const ResTable_entry* entry = reinterpret_cast( + reinterpret_cast(type) + entry_offset); + + const size_t entry_size = dtohs(entry->size); + if (entry_size < sizeof(*entry)) { + LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx + << " is too small."; + return false; + } + + if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) { + LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx + << " is too large."; return false; } + if (entry_size < sizeof(ResTable_map_entry)) { + // There needs to be room for one Res_value struct. + if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) { + LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << entry_idx + << " for type " << (int)type->id << "."; + return false; + } + + const Res_value* value = + reinterpret_cast(reinterpret_cast(entry) + entry_size); + const size_t value_size = dtohs(value->size); + if (value_size < sizeof(Res_value)) { + LOG(ERROR) << "Res_value at index " << entry_idx << " is too small."; + return false; + } + + if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) { + LOG(ERROR) << "Res_value size " << value_size << " at index " << entry_idx + << " is too large."; + return false; + } + } else { + const ResTable_map_entry* map = reinterpret_cast(entry); + const size_t map_entry_count = dtohl(map->count); + size_t map_entries_start = entry_offset + entry_size; + if (map_entries_start & 0x03) { + LOG(ERROR) << "Map entries at index " << entry_idx << " start at unaligned offset."; + return false; + } + + // Each entry is sizeof(ResTable_map) big. + if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) { + LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << entry_idx << "."; + return false; + } + } + return true; +} + +template +bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_idx, + const ResTable_config& config, FindEntryResult* out_entry) const { const ResTable_config* best_config = nullptr; const ResTable_type* best_type = nullptr; uint32_t best_offset = 0; - for (uint32_t i = 0; i < ptr->type_count; i++) { - const Type* type = &ptr->types[i]; + for (uint32_t i = 0; i < type_spec_ptr->type_count; i++) { + const Type* type = &type_spec_ptr->types[i]; if (type->configuration.match(config) && (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) { // The configuration matches and is better than the previous selection. // Find the entry value if it exists for this configuration. - size_t entry_count = dtohl(type->type->entryCount); + const size_t entry_count = dtohl(type->type->entryCount); + const size_t offsets_offset = dtohs(type->type->header.headerSize); if (entry_idx < entry_count) { + // If the package hasn't been verified, do bounds checking. + if (!Verified) { + if (!VerifyResTableType(type->type)) { + continue; + } + } + const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type->type) + dtohs(type->type->header.headerSize)); + reinterpret_cast(type->type) + offsets_offset); const uint32_t offset = dtohl(entry_offsets[entry_idx]); if (offset != ResTable_type::NO_ENTRY) { // There is an entry for this resource, record it. best_config = &type->configuration; best_type = type->type; - best_offset = offset + dtohl(type->type->entriesStart); + best_offset = offset; } } } @@ -178,84 +276,64 @@ bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTab return false; } - const uint32_t* flags = reinterpret_cast(ptr->type_spec + 1); - *out_flags = dtohl(flags[entry_idx]); - *out_selected_config = *best_config; + if (!Verified) { + if (!VerifyResTableEntry(best_type, best_offset, entry_idx)) { + return false; + } + } const ResTable_entry* best_entry = reinterpret_cast( - reinterpret_cast(best_type) + best_offset); + reinterpret_cast(best_type) + best_offset + dtohl(best_type->entriesStart)); + + const uint32_t* flags = reinterpret_cast(type_spec_ptr->type_spec + 1); + out_entry->type_flags = dtohl(flags[entry_idx]); out_entry->entry = best_entry; + out_entry->config = best_config; out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1); out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index)); return true; } -// The destructor gets generated into arbitrary translation units -// if left implicit, which causes the compiler to complain about -// forward declarations and incomplete types. -LoadedArsc::~LoadedArsc() {} - -bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, - LoadedArscEntry* out_entry, ResTable_config* out_selected_config, - uint32_t* out_flags) const { +bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, + FindEntryResult* out_entry) const { ATRACE_CALL(); - const uint8_t package_id = get_package_id(resid); - const uint8_t type_id = get_type_id(resid); - const uint16_t entry_id = get_entry_id(resid); - if (type_id == 0) { - LOG(ERROR) << "Invalid ID 0x" << std::hex << resid << std::dec << "."; + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; + if (ptr == nullptr) { return false; } - for (const auto& loaded_package : packages_) { - if (loaded_package->package_id_ == package_id) { - return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry, - out_selected_config, out_flags); + // If there is an IDMAP supplied with this package, translate the entry ID. + if (ptr->idmap_entries != nullptr) { + if (!LoadedIdmap::Lookup(ptr->idmap_entries, entry_idx, &entry_idx)) { + // There is no mapping, so the resource is not meant to be in this overlay package. + return false; } } - return false; -} -const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const { - const uint8_t package_id = get_package_id(resid); - for (const auto& loaded_package : packages_) { - if (loaded_package->package_id_ == package_id) { - return loaded_package.get(); - } + // Don't bother checking if the entry ID is larger than the number of entries. + if (entry_idx >= dtohl(ptr->type_spec->entryCount)) { + return false; } - return nullptr; + + if (verified_) { + return FindEntry(ptr, entry_idx, config, out_entry); + } + return FindEntry(ptr, entry_idx, config, out_entry); } static bool VerifyType(const Chunk& chunk) { ATRACE_CALL(); const ResTable_type* header = chunk.header(); - const size_t entry_count = dtohl(header->entryCount); - if (entry_count > std::numeric_limits::max()) { - LOG(ERROR) << "Too many entries in RES_TABLE_TYPE_TYPE."; + if (!VerifyResTableType(header)) { return false; } - // Make sure that there is enough room for the entry offsets. + const size_t entry_count = dtohl(header->entryCount); const size_t offsets_offset = chunk.header_size(); - const size_t entries_offset = dtohl(header->entriesStart); - const size_t offsets_length = sizeof(uint32_t) * entry_count; - - if (offsets_offset > entries_offset || entries_offset - offsets_offset < offsets_length) { - LOG(ERROR) << "Entry offsets overlap actual entry data."; - return false; - } - - if (entries_offset > chunk.size()) { - LOG(ERROR) << "Entry offsets extend beyond chunk."; - return false; - } - - if (entries_offset & 0x03) { - LOG(ERROR) << "Entries start at unaligned address."; - return false; - } // Check each entry offset. const uint32_t* offsets = @@ -263,79 +341,9 @@ static bool VerifyType(const Chunk& chunk) { for (size_t i = 0; i < entry_count; i++) { uint32_t offset = dtohl(offsets[i]); if (offset != ResTable_type::NO_ENTRY) { - // Check that the offset is aligned. - if (offset & 0x03) { - LOG(ERROR) << "Entry offset at index " << i << " is not 4-byte aligned."; - return false; - } - - // Check that the offset doesn't overflow. - if (offset > std::numeric_limits::max() - entries_offset) { - // Overflow in offset. - LOG(ERROR) << "Entry offset at index " << i << " is too large."; + if (!VerifyResTableEntry(header, offset, i)) { return false; } - - offset += entries_offset; - if (offset > chunk.size() - sizeof(ResTable_entry)) { - LOG(ERROR) << "Entry offset at index " << i << " is too large. No room for ResTable_entry."; - return false; - } - - const ResTable_entry* entry = reinterpret_cast( - reinterpret_cast(header) + offset); - const size_t entry_size = dtohs(entry->size); - if (entry_size < sizeof(*entry)) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << i << " is too small."; - return false; - } - - // Check the declared entrySize. - if (entry_size > chunk.size() || offset > chunk.size() - entry_size) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << i << " is too large."; - return false; - } - - // If this is a map entry, then keep validating. - if (entry_size >= sizeof(ResTable_map_entry)) { - const ResTable_map_entry* map = reinterpret_cast(entry); - const size_t map_entry_count = dtohl(map->count); - - size_t map_entries_start = offset + entry_size; - if (map_entries_start & 0x03) { - LOG(ERROR) << "Map entries at index " << i << " start at unaligned offset."; - return false; - } - - // Each entry is sizeof(ResTable_map) big. - if (map_entry_count > ((chunk.size() - map_entries_start) / sizeof(ResTable_map))) { - LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << i << "."; - return false; - } - - // Great, all the map entries fit!. - } else { - // There needs to be room for one Res_value struct. - if (offset + entry_size > chunk.size() - sizeof(Res_value)) { - LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << i << " for type " - << (int)header->id << " with config " << header->config.toString().string() - << "."; - return false; - } - - const Res_value* value = reinterpret_cast( - reinterpret_cast(entry) + entry_size); - const size_t value_size = dtohs(value->size); - if (value_size < sizeof(Res_value)) { - LOG(ERROR) << "Res_value at index " << i << " is too small."; - return false; - } - - if (value_size > chunk.size() || offset + entry_size > chunk.size() - value_size) { - LOG(ERROR) << "Res_value size " << value_size << " at index " << i << " is too large."; - return false; - } - } } } return true; @@ -431,10 +439,21 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } -std::unique_ptr LoadedPackage::Load(const Chunk& chunk, - const LoadedIdmap* loaded_idmap) { +const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const { + const uint8_t package_id = get_package_id(resid); + for (const auto& loaded_package : packages_) { + if (loaded_package->GetPackageId() == package_id) { + return loaded_package.get(); + } + } + return nullptr; +} + +std::unique_ptr LoadedPackage::Load(const Chunk& chunk, + const LoadedIdmap* loaded_idmap, + bool system, bool load_as_shared_library) { ATRACE_CALL(); - std::unique_ptr loaded_package{new LoadedPackage()}; + std::unique_ptr loaded_package(new LoadedPackage()); // typeIdOffset was added at some point, but we still must recognize apps built before this // was added. @@ -446,8 +465,11 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, return {}; } + loaded_package->system_ = system; + loaded_package->package_id_ = dtohl(header->id); - if (loaded_package->package_id_ == 0) { + if (loaded_package->package_id_ == 0 || + (loaded_package->package_id_ == kAppPackageId && load_as_shared_library)) { // Package ID of 0 means this is a shared library. loaded_package->dynamic_ = true; } @@ -593,13 +615,16 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, // Type chunks must be preceded by their TypeSpec chunks. if (!types_builder || type->id - 1 != last_type_idx) { - LOG(ERROR) << "Found RES_TABLE_TYPE_TYPE chunk without " - "RES_TABLE_TYPE_SPEC_TYPE."; + LOG(ERROR) << "Found RES_TABLE_TYPE_TYPE chunk without RES_TABLE_TYPE_SPEC_TYPE."; return {}; } - if (!VerifyType(child_chunk)) { - return {}; + // Only verify the type if we haven't already failed verification. + if (loaded_package->verified_) { + if (!VerifyType(child_chunk)) { + LOG(WARNING) << "Package failed verification, resource retrieval may be slower"; + loaded_package->verified_ = false; + } } types_builder->AddType(type); @@ -669,7 +694,28 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, LOG(ERROR) << iter.GetLastError(); return {}; } - return loaded_package; + return std::move(loaded_package); +} + +bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, + FindEntryResult* out_entry) const { + ATRACE_CALL(); + + const uint8_t package_id = get_package_id(resid); + const uint8_t type_id = get_type_id(resid); + const uint16_t entry_id = get_entry_id(resid); + + if (type_id == 0) { + LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); + return false; + } + + for (const auto& loaded_package : packages_) { + if (loaded_package->GetPackageId() == package_id) { + return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry); + } + } + return false; } bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, @@ -712,17 +758,11 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, } packages_seen++; - std::unique_ptr loaded_package = - LoadedPackage::Load(child_chunk, loaded_idmap); + std::unique_ptr loaded_package = + LoadedPackage::Load(child_chunk, loaded_idmap, system_, load_as_shared_library); if (!loaded_package) { return false; } - - // Mark the package as dynamic if we are forcefully loading the Apk as a shared library. - if (loaded_package->package_id_ == kAppPackageId) { - loaded_package->dynamic_ = load_as_shared_library; - } - loaded_package->system_ = system_; packages_.push_back(std::move(loaded_package)); } break; diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 7a0ef2b770ce..87999c353a90 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -6014,9 +6014,6 @@ void ResTable::getLocales(Vector* locales, bool includeSystemLocales, StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index) : mPool(pool), mIndex(index) {} -StringPoolRef::StringPoolRef() - : mPool(NULL), mIndex(0) {} - const char* StringPoolRef::string8(size_t* outLen) const { if (mPool != NULL) { return mPool->string8At(mIndex, outLen); diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 2dd8125704e2..a77c4b9ad721 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -245,21 +245,22 @@ class AssetManager2 { private: DISALLOW_COPY_AND_ASSIGN(AssetManager2); - // Finds the best entry for `resid` amongst all the ApkAssets. The entry can be a simple - // Res_value, or a complex map/bag type. + // Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple + // Res_value, or a complex map/bag type. If successful, it is available in `out_entry`. + // Returns kInvalidCookie on failure. Otherwise, the return value is the cookie associated with + // the ApkAssets in which the entry was found. // // `density_override` overrides the density of the current configuration when doing a search. // // When `stop_at_first_match` is true, the first match found is selected and the search // terminates. This is useful for methods that just look up the name of a resource and don't - // care about the value. In this case, the value of `out_flags` is incomplete and should not - // be used. + // care about the value. In this case, the value of `FindEntryResult::type_flags` is incomplete + // and should not be used. // - // `out_flags` stores the resulting bitmask of configuration axis with which the resource - // value varies. + // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly + // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds. ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, - LoadedArscEntry* out_entry, ResTable_config* out_selected_config, - uint32_t* out_flags); + FindEntryResult* out_entry); // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 1f272e8f84e9..377735bcfbba 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -41,12 +41,18 @@ class DynamicPackageEntry { int package_id = 0; }; -struct LoadedArscEntry { +struct FindEntryResult { // A pointer to the resource table entry for this resource. // If the size of the entry is > sizeof(ResTable_entry), it can be cast to // a ResTable_map_entry and processed as a bag/map. const ResTable_entry* entry = nullptr; + // The configuration for which the resulting entry was defined. + const ResTable_config* config = nullptr; + + // Stores the resulting bitmask of configuration axis with which the resource value varies. + uint32_t type_flags = 0u; + // The dynamic package ID map for the package from which this resource came from. const DynamicRefTable* dynamic_ref_table = nullptr; @@ -63,12 +69,22 @@ struct TypeSpec; class LoadedArsc; class LoadedPackage { - friend class LoadedArsc; - public: + static std::unique_ptr Load(const Chunk& chunk, + const LoadedIdmap* loaded_idmap, bool system, + bool load_as_shared_library); + + ~LoadedPackage(); + bool FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, - LoadedArscEntry* out_entry, ResTable_config* out_selected_config, - uint32_t* out_flags) const; + FindEntryResult* out_entry) const; + + // Finds the entry with the specified type name and entry name. The names are in UTF-16 because + // the underlying ResStringPool API expects this. For now this is acceptable, but since + // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. + // Returns a partial resource ID, with the package ID left as 0x00. The caller is responsible + // for patching the correct package ID to the resource ID. + uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; // Returns the string pool where type names are stored. inline const ResStringPool* GetTypeStringPool() const { @@ -98,10 +114,16 @@ class LoadedPackage { return system_; } + // Returns true if this package is from an overlay ApkAssets. inline bool IsOverlay() const { return overlay_; } + // Returns true if this package is verified to be valid. + inline bool IsVerified() const { + return verified_; + } + // Returns the map of package name to package ID used in this LoadedPackage. At runtime, a // package could have been assigned a different package ID than what this LoadedPackage was // compiled with. AssetManager rewrites the package IDs so that they are compatible at runtime. @@ -118,19 +140,14 @@ class LoadedPackage { // before being inserted into the set. This may cause some equivalent locales to de-dupe. void CollectLocales(bool canonicalize, std::set* out_locales) const; - // Finds the entry with the specified type name and entry name. The names are in UTF-16 because - // the underlying ResStringPool API expects this. For now this is acceptable, but since - // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. - // Returns a partial resource ID, with the package ID left as 0x00. The caller is responsible - // for patching the correct package ID to the resource ID. - uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; - private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); - static std::unique_ptr Load(const Chunk& chunk, const LoadedIdmap* loaded_idmap); + LoadedPackage(); - LoadedPackage() = default; + template + bool FindEntry(const util::unique_cptr& type_spec_ptr, uint16_t entry_idx, + const ResTable_config& config, FindEntryResult* out_entry) const; ResStringPool type_string_pool_; ResStringPool key_string_pool_; @@ -140,6 +157,7 @@ class LoadedPackage { bool dynamic_ = false; bool system_ = false; bool overlay_ = false; + bool verified_ = true; ByteBucketArray> type_specs_; std::vector dynamic_package_map_; @@ -163,8 +181,6 @@ class LoadedArsc { // Create an empty LoadedArsc. This is used when an APK has no resources.arsc. static std::unique_ptr CreateEmpty(); - ~LoadedArsc(); - // Returns the string pool where all string resource values // (Res_value::dataType == Res_value::TYPE_STRING) are indexed. inline const ResStringPool* GetStringPool() const { @@ -175,8 +191,7 @@ class LoadedArsc { // The parameter `out_entry` will be filled with the resulting resource entry. // The resource entry can be a simple entry (ResTable_entry) or a complex bag // (ResTable_entry_map). - bool FindEntry(uint32_t resid, const ResTable_config& config, LoadedArscEntry* out_entry, - ResTable_config* selected_config, uint32_t* out_flags) const; + bool FindEntry(uint32_t resid, const ResTable_config& config, FindEntryResult* out_entry) const; // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist. const LoadedPackage* GetPackageForId(uint32_t resid) const; diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 8f858b6fd7fd..854795559750 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -546,15 +546,15 @@ private: */ class StringPoolRef { public: - StringPoolRef(); - StringPoolRef(const ResStringPool* pool, uint32_t index); + StringPoolRef() = default; + StringPoolRef(const ResStringPool* pool, uint32_t index); - const char* string8(size_t* outLen) const; - const char16_t* string16(size_t* outLen) const; + const char* string8(size_t* outLen) const; + const char16_t* string16(size_t* outLen) const; private: - const ResStringPool* mPool; - uint32_t mIndex; + const ResStringPool* mPool = nullptr; + uint32_t mIndex = 0u; }; /** ******************************************************************** diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index 2766ce127cf0..d65d93f9af1a 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -32,7 +32,13 @@ TEST(ApkAssetsTest, LoadApk) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); ASSERT_NE(nullptr, loaded_apk); - EXPECT_NE(nullptr, loaded_apk->GetLoadedArsc()); + + const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); + ASSERT_NE(nullptr, loaded_arsc); + + const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); + ASSERT_NE(nullptr, loaded_package); + EXPECT_TRUE(loaded_package->IsVerified()); std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); ASSERT_NE(nullptr, asset); @@ -87,6 +93,20 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { ASSERT_NE(nullptr, loaded_overlay_apk); } +TEST(ApkAssetsTest, LoadUnverifiableApk) { + std::unique_ptr loaded_apk = + ApkAssets::Load(GetTestDataPath() + "/unverified/unverified.apk"); + ASSERT_NE(nullptr, loaded_apk); + + const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); + ASSERT_NE(nullptr, loaded_arsc); + + const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); + ASSERT_NE(nullptr, loaded_package); + + EXPECT_FALSE(loaded_package->IsVerified()); +} + TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp index 99a07a501356..739e733716c9 100644 --- a/libs/androidfw/tests/AssetManager2_bench.cpp +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -26,10 +26,12 @@ #include "data/basic/R.h" #include "data/libclient/R.h" #include "data/styles/R.h" +#include "data/unverified/R.h" namespace app = com::android::app; namespace basic = com::android::basic; namespace libclient = com::android::libclient; +namespace unverified = com::android::unverified; namespace android { @@ -124,6 +126,12 @@ static void BM_AssetManagerGetResourceOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerGetResourceOld); +static void BM_AssetManagerGetResourceUnverified(benchmark::State& state) { + GetResourceBenchmark({GetTestDataPath() + "/unverified/unverified.apk"}, nullptr /*config*/, + unverified::R::integer::number1, state); +} +BENCHMARK(BM_AssetManagerGetResourceUnverified); + static void BM_AssetManagerGetLibraryResource(benchmark::State& state) { GetResourceBenchmark( {GetTestDataPath() + "/lib_two/lib_two.apk", GetTestDataPath() + "/lib_one/lib_one.apk", @@ -206,6 +214,30 @@ static void BM_AssetManagerGetBagOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerGetBagOld); +static void BM_AssetManagerGetBagUnverified(benchmark::State& state) { + std::unique_ptr apk = + ApkAssets::Load(GetTestDataPath() + "/unverified/unverified.apk"); + if (apk == nullptr) { + state.SkipWithError("Failed to load assets"); + return; + } + + AssetManager2 assets; + assets.SetApkAssets({apk.get()}); + + while (state.KeepRunning()) { + const ResolvedBag* bag = assets.GetBag(unverified::R::array::integerArray1); + const auto bag_end = end(bag); + for (auto iter = begin(bag); iter != bag_end; ++iter) { + uint32_t key = iter->key; + Res_value value = iter->value; + benchmark::DoNotOptimize(key); + benchmark::DoNotOptimize(value); + } + } +} +BENCHMARK(BM_AssetManagerGetBagUnverified); + static void BM_AssetManagerGetResourceLocales(benchmark::State& state) { std::unique_ptr apk = ApkAssets::Load(kFrameworkPath); if (apk == nullptr) { diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index fcae53b322b3..ab1a22ed9dc3 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -28,6 +28,7 @@ #include "data/libclient/R.h" #include "data/styles/R.h" #include "data/system/R.h" +#include "data/unverified/R.h" namespace app = com::android::app; namespace appaslib = com::android::appaslib::app; @@ -35,6 +36,7 @@ namespace basic = com::android::basic; namespace lib_one = com::android::lib_one; namespace lib_two = com::android::lib_two; namespace libclient = com::android::libclient; +namespace unverified = com::android::unverified; namespace android { @@ -431,4 +433,30 @@ TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {} TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {} +TEST_F(AssetManager2Test, OperateOnUnverifiedApkAssets) { + std::unique_ptr unverified_assets = + ApkAssets::Load(GetTestDataPath() + "/unverified/unverified.apk"); + ASSERT_NE(nullptr, unverified_assets); + + AssetManager2 assetmanager; + assetmanager.SetApkAssets({unverified_assets.get()}); + + Res_value value; + ResTable_config config; + uint32_t flags; + + EXPECT_EQ(kInvalidCookie, + assetmanager.GetResource(unverified::R::string::test1, false /*may_be_bag*/, 0u, &value, + &config, &flags)); + EXPECT_EQ(kInvalidCookie, + assetmanager.GetResource(unverified::R::string::test2, false /*may_be_bag*/, 0u, &value, + &config, &flags)); + EXPECT_NE(kInvalidCookie, + assetmanager.GetResource(unverified::R::integer::number1, false /*may_be_bag*/, 0u, + &value, &config, &flags)); + + EXPECT_EQ(nullptr, assetmanager.GetBag(unverified::R::style::Theme1)); + EXPECT_NE(nullptr, assetmanager.GetBag(unverified::R::array::integerArray1)); +} + } // namespace android diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 2b72d146f83a..954a54d1ce62 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -44,12 +44,9 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { memset(&config, 0, sizeof(config)); config.sdkVersion = 24; - LoadedArscEntry entry; - ResTable_config selected_config; - uint32_t flags; + FindEntryResult entry; - ASSERT_TRUE( - loaded_arsc->FindEntry(app::R::string::string_one, config, &entry, &selected_config, &flags)); + ASSERT_TRUE(loaded_arsc->FindEntry(app::R::string::string_one, config, &entry)); ASSERT_NE(nullptr, entry.entry); } @@ -66,12 +63,8 @@ TEST(LoadedArscTest, FindDefaultEntry) { desired_config.language[0] = 'd'; desired_config.language[1] = 'e'; - LoadedArscEntry entry; - ResTable_config selected_config; - uint32_t flags; - - ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry, - &selected_config, &flags)); + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry)); ASSERT_NE(nullptr, entry.entry); } @@ -150,23 +143,15 @@ TEST(LoadedArscTest, LoadFeatureSplit) { ResTable_config desired_config; memset(&desired_config, 0, sizeof(desired_config)); - LoadedArscEntry entry; - ResTable_config selected_config; - uint32_t flags; - - ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry, - &selected_config, &flags)); + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry)); size_t len; const char16_t* type_name16 = entry.type_string_ref.string16(&len); ASSERT_NE(nullptr, type_name16); ASSERT_NE(0u, len); - size_t utf8_len = utf16_to_utf8_length(type_name16, len); - std::string type_name; - type_name.resize(utf8_len); - utf16_to_utf8(type_name16, len, &*type_name.begin(), utf8_len + 1); - + std::string type_name = util::Utf16ToUtf8(StringPiece16(type_name16, len)); EXPECT_EQ(std::string("string"), type_name); } @@ -210,12 +195,8 @@ TEST(LoadedArscTest, LoadOverlay) { ResTable_config desired_config; memset(&desired_config, 0, sizeof(desired_config)); - LoadedArscEntry entry; - ResTable_config selected_config; - uint32_t flags; - - ASSERT_TRUE( - loaded_arsc->FindEntry(0x08030001u, desired_config, &entry, &selected_config, &flags)); + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(0x08030001u, desired_config, &entry)); } // structs with size fields (like Res_value, ResTable_entry) should be diff --git a/libs/androidfw/tests/data/unverified/R.h b/libs/androidfw/tests/data/unverified/R.h new file mode 100644 index 000000000000..b734b4988ea2 --- /dev/null +++ b/libs/androidfw/tests/data/unverified/R.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TESTS_DATA_UNVERIFIED_R_H_ +#define TESTS_DATA_UNVERIFIED_R_H_ + +#include + +#include "tests/data/basic/R.h" + +namespace com { +namespace android { + +namespace unverified = basic; + +} // namespace android +} // namespace com + +#endif /* TESTS_DATA_UNVERIFIED_R_H_ */ diff --git a/libs/androidfw/tests/data/unverified/unverified.apk b/libs/androidfw/tests/data/unverified/unverified.apk new file mode 100644 index 000000000000..234b390dc37d Binary files /dev/null and b/libs/androidfw/tests/data/unverified/unverified.apk differ -- cgit v1.2.3-59-g8ed1b From 73f6f9daf6bb38e49747bd103c97617b3dccddc4 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Tue, 14 Nov 2017 10:18:05 -0800 Subject: libandroidfw: Add SparseEntry support for LoadedArsc go/o-restable-sparse-entries Test: make libandroidfw_tests Change-Id: Ib1a7d1fc69008390eee53a1de04356dc50e05b45 --- libs/androidfw/LoadedArsc.cpp | 86 +++++++++++++++++++++------- libs/androidfw/tests/AssetManager2_bench.cpp | 31 ---------- libs/androidfw/tests/BenchmarkHelpers.cpp | 31 ++++++++++ libs/androidfw/tests/BenchmarkHelpers.h | 3 + libs/androidfw/tests/LoadedArsc_test.cpp | 19 ++++++ libs/androidfw/tests/SparseEntry_bench.cpp | 32 +++++------ 6 files changed, 135 insertions(+), 67 deletions(-) (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index c361ea2dbe0a..6ee5e86c55a2 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -18,6 +18,7 @@ #include "androidfw/LoadedArsc.h" +#include #include #include @@ -244,31 +245,63 @@ bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_i for (uint32_t i = 0; i < type_spec_ptr->type_count; i++) { const Type* type = &type_spec_ptr->types[i]; + const ResTable_type* type_chunk = type->type; if (type->configuration.match(config) && (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) { // The configuration matches and is better than the previous selection. // Find the entry value if it exists for this configuration. - const size_t entry_count = dtohl(type->type->entryCount); - const size_t offsets_offset = dtohs(type->type->header.headerSize); - if (entry_idx < entry_count) { - // If the package hasn't been verified, do bounds checking. - if (!Verified) { - if (!VerifyResTableType(type->type)) { - continue; - } + const size_t entry_count = dtohl(type_chunk->entryCount); + const size_t offsets_offset = dtohs(type_chunk->header.headerSize); + + // If the package hasn't been verified, do bounds checking. + if (!Verified) { + if (!VerifyResTableType(type_chunk)) { + continue; + } + } + + // Check if there is the desired entry in this type. + + if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { + // This is encoded as a sparse map, so perform a binary search. + const ResTable_sparseTypeEntry* sparse_indices = + reinterpret_cast( + reinterpret_cast(type_chunk) + offsets_offset); + const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; + const ResTable_sparseTypeEntry* result = + std::lower_bound(sparse_indices, sparse_indices_end, entry_idx, + [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { + return dtohs(entry.idx) < entry_idx; + }); + + if (result == sparse_indices_end || dtohs(result->idx) != entry_idx) { + // No entry found. + continue; + } + + // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as + // the real offset divided by 4. + best_offset = dtohs(result->offset) * 4u; + } else { + if (entry_idx >= entry_count) { + // This entry cannot be here. + continue; } const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type->type) + offsets_offset); + reinterpret_cast(type_chunk) + offsets_offset); const uint32_t offset = dtohl(entry_offsets[entry_idx]); - if (offset != ResTable_type::NO_ENTRY) { - // There is an entry for this resource, record it. - best_config = &type->configuration; - best_type = type->type; - best_offset = offset; + if (offset == ResTable_type::NO_ENTRY) { + continue; } + + // There is an entry for this resource, record it. + best_offset = offset; } + + best_config = &type->configuration; + best_type = type_chunk; } } @@ -335,16 +368,29 @@ static bool VerifyType(const Chunk& chunk) { const size_t entry_count = dtohl(header->entryCount); const size_t offsets_offset = chunk.header_size(); - // Check each entry offset. - const uint32_t* offsets = - reinterpret_cast(reinterpret_cast(header) + offsets_offset); - for (size_t i = 0; i < entry_count; i++) { - uint32_t offset = dtohl(offsets[i]); - if (offset != ResTable_type::NO_ENTRY) { + if (header->flags & ResTable_type::FLAG_SPARSE) { + // This is encoded as a sparse map, so perform a binary search. + const ResTable_sparseTypeEntry* sparse_indices = + reinterpret_cast(reinterpret_cast(header) + + offsets_offset); + for (size_t i = 0; i < entry_count; i++) { + const uint32_t offset = uint32_t{dtohs(sparse_indices[i].offset)} * 4u; if (!VerifyResTableEntry(header, offset, i)) { return false; } } + } else { + // Check each entry offset. + const uint32_t* offsets = reinterpret_cast( + reinterpret_cast(header) + offsets_offset); + for (size_t i = 0; i < entry_count; i++) { + uint32_t offset = dtohl(offsets[i]); + if (offset != ResTable_type::NO_ENTRY) { + if (!VerifyResTableEntry(header, offset, i)) { + return false; + } + } + } } return true; } diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp index 739e733716c9..ea9d427f8f11 100644 --- a/libs/androidfw/tests/AssetManager2_bench.cpp +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -83,37 +83,6 @@ static void BM_AssetManagerLoadFrameworkAssetsOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerLoadFrameworkAssetsOld); -static void GetResourceBenchmark(const std::vector& paths, - const ResTable_config* config, uint32_t resid, - benchmark::State& state) { - std::vector> apk_assets; - std::vector apk_assets_ptrs; - for (const std::string& path : paths) { - std::unique_ptr apk = ApkAssets::Load(path); - if (apk == nullptr) { - state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str()); - return; - } - apk_assets_ptrs.push_back(apk.get()); - apk_assets.push_back(std::move(apk)); - } - - AssetManager2 assetmanager; - assetmanager.SetApkAssets(apk_assets_ptrs); - if (config != nullptr) { - assetmanager.SetConfiguration(*config); - } - - Res_value value; - ResTable_config selected_config; - uint32_t flags; - - while (state.KeepRunning()) { - assetmanager.GetResource(resid, false /* may_be_bag */, 0u /* density_override */, &value, - &selected_config, &flags); - } -} - static void BM_AssetManagerGetResource(benchmark::State& state) { GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, basic::R::integer::number1, state); diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index 3619b7ee83ab..7149beef797f 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -18,6 +18,7 @@ #include "android-base/stringprintf.h" #include "androidfw/AssetManager.h" +#include "androidfw/AssetManager2.h" namespace android { @@ -48,4 +49,34 @@ void GetResourceBenchmarkOld(const std::vector& paths, const ResTab } } +void GetResourceBenchmark(const std::vector& paths, const ResTable_config* config, + uint32_t resid, benchmark::State& state) { + std::vector> apk_assets; + std::vector apk_assets_ptrs; + for (const std::string& path : paths) { + std::unique_ptr apk = ApkAssets::Load(path); + if (apk == nullptr) { + state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str()); + return; + } + apk_assets_ptrs.push_back(apk.get()); + apk_assets.push_back(std::move(apk)); + } + + AssetManager2 assetmanager; + assetmanager.SetApkAssets(apk_assets_ptrs); + if (config != nullptr) { + assetmanager.SetConfiguration(*config); + } + + Res_value value; + ResTable_config selected_config; + uint32_t flags; + + while (state.KeepRunning()) { + assetmanager.GetResource(resid, false /* may_be_bag */, 0u /* density_override */, &value, + &selected_config, &flags); + } +} + } // namespace android diff --git a/libs/androidfw/tests/BenchmarkHelpers.h b/libs/androidfw/tests/BenchmarkHelpers.h index 0bb96b5d3471..eb0939d0e23b 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.h +++ b/libs/androidfw/tests/BenchmarkHelpers.h @@ -30,6 +30,9 @@ namespace android { void GetResourceBenchmarkOld(const std::vector& paths, const ResTable_config* config, uint32_t resid, ::benchmark::State& state); +void GetResourceBenchmark(const std::vector& paths, const ResTable_config* config, + uint32_t resid, benchmark::State& state); + } // namespace android #endif // ANDROIDFW_TESTS_BENCHMARKHELPERS_H diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 954a54d1ce62..37ddafb14fd3 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -19,11 +19,13 @@ #include "TestHelpers.h" #include "data/basic/R.h" #include "data/libclient/R.h" +#include "data/sparse/R.h" #include "data/styles/R.h" namespace app = com::android::app; namespace basic = com::android::basic; namespace libclient = com::android::libclient; +namespace sparse = com::android::sparse; namespace android { @@ -68,6 +70,23 @@ TEST(LoadedArscTest, FindDefaultEntry) { ASSERT_NE(nullptr, entry.entry); } +TEST(LoadedArscTest, LoadSparseEntryApp) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc", + &contents)); + + std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + ASSERT_NE(nullptr, loaded_arsc); + + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.sdkVersion = 26; + + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(sparse::R::integer::foo_9, config, &entry)); + ASSERT_NE(nullptr, entry.entry); +} + TEST(LoadedArscTest, LoadSharedLibrary) { std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc", diff --git a/libs/androidfw/tests/SparseEntry_bench.cpp b/libs/androidfw/tests/SparseEntry_bench.cpp index d6dc07dfb704..c9b4ad8af278 100644 --- a/libs/androidfw/tests/SparseEntry_bench.cpp +++ b/libs/androidfw/tests/SparseEntry_bench.cpp @@ -24,40 +24,40 @@ namespace sparse = com::android::sparse; namespace android { -static void BM_SparseEntryGetResourceSparseSmall(benchmark::State& state) { +static void BM_SparseEntryGetResourceOldSparse(benchmark::State& state, uint32_t resid) { ResTable_config config; memset(&config, 0, sizeof(config)); config.sdkVersion = 26; - GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/sparse.apk"}, &config, - sparse::R::integer::foo_9, state); + GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/sparse.apk"}, &config, resid, state); } -BENCHMARK(BM_SparseEntryGetResourceSparseSmall); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldSparse, Small, sparse::R::integer::foo_9); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldSparse, Large, sparse::R::string::foo_999); -static void BM_SparseEntryGetResourceNotSparseSmall(benchmark::State& state) { +static void BM_SparseEntryGetResourceOldNotSparse(benchmark::State& state, uint32_t resid) { ResTable_config config; memset(&config, 0, sizeof(config)); config.sdkVersion = 26; - GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/not_sparse.apk"}, &config, - sparse::R::integer::foo_9, state); + GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/not_sparse.apk"}, &config, resid, state); } -BENCHMARK(BM_SparseEntryGetResourceNotSparseSmall); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldNotSparse, Small, sparse::R::integer::foo_9); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceOldNotSparse, Large, sparse::R::string::foo_999); -static void BM_SparseEntryGetResourceSparseLarge(benchmark::State& state) { +static void BM_SparseEntryGetResourceSparse(benchmark::State& state, uint32_t resid) { ResTable_config config; memset(&config, 0, sizeof(config)); config.sdkVersion = 26; - GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/sparse.apk"}, &config, - sparse::R::string::foo_999, state); + GetResourceBenchmark({GetTestDataPath() + "/sparse/sparse.apk"}, &config, resid, state); } -BENCHMARK(BM_SparseEntryGetResourceSparseLarge); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceSparse, Small, sparse::R::integer::foo_9); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceSparse, Large, sparse::R::string::foo_999); -static void BM_SparseEntryGetResourceNotSparseLarge(benchmark::State& state) { +static void BM_SparseEntryGetResourceNotSparse(benchmark::State& state, uint32_t resid) { ResTable_config config; memset(&config, 0, sizeof(config)); config.sdkVersion = 26; - GetResourceBenchmarkOld({GetTestDataPath() + "/sparse/not_sparse.apk"}, &config, - sparse::R::string::foo_999, state); + GetResourceBenchmark({GetTestDataPath() + "/sparse/not_sparse.apk"}, &config, resid, state); } -BENCHMARK(BM_SparseEntryGetResourceNotSparseLarge); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceNotSparse, Small, sparse::R::integer::foo_9); +BENCHMARK_CAPTURE(BM_SparseEntryGetResourceNotSparse, Large, sparse::R::string::foo_999); } // namespace android -- cgit v1.2.3-59-g8ed1b From 498f6053dad29d715dd532f5272df12dabccf8c8 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Wed, 29 Nov 2017 13:24:29 -0800 Subject: libandroidfw: Remove pre-verification This added more up-front cost to loading an APK and didn't provide a significant benefit to resource retrieval. Test: make libandroidfw_tests Change-Id: Idbf993abc433fa8c8950d106c66469b310b66f7f --- libs/androidfw/AssetManager2.cpp | 5 +- libs/androidfw/LoadedArsc.cpp | 127 ++++++--------------- libs/androidfw/include/androidfw/LoadedArsc.h | 7 -- libs/androidfw/tests/ApkAssets_test.cpp | 16 --- libs/androidfw/tests/AssetManager2_bench.cpp | 32 ------ libs/androidfw/tests/AssetManager2_test.cpp | 28 ----- libs/androidfw/tests/data/unverified/R.h | 32 ------ .../androidfw/tests/data/unverified/unverified.apk | Bin 3874 -> 0 bytes 8 files changed, 35 insertions(+), 212 deletions(-) delete mode 100644 libs/androidfw/tests/data/unverified/R.h delete mode 100644 libs/androidfw/tests/data/unverified/unverified.apk (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 94a05b2f90e9..415d3e36adf9 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -299,10 +299,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri const PackageGroup& package_group = package_groups_[idx]; const size_t package_count = package_group.packages_.size(); + FindEntryResult current_entry; for (size_t i = 0; i < package_count; i++) { const LoadedPackage* loaded_package = package_group.packages_[i]; - - FindEntryResult current_entry; if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry)) { continue; } @@ -394,7 +393,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, return kInvalidCookie; } - if (dtohl(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) { + if (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) { if (!may_be_bag) { LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid); return kInvalidCookie; diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 6ee5e86c55a2..28548e27baf0 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -129,9 +129,14 @@ LoadedPackage::~LoadedPackage() = default; // Precondition: The header passed in has already been verified, so reading any fields and trusting // the ResChunk_header is safe. static bool VerifyResTableType(const ResTable_type* header) { + if (header->id == 0) { + LOG(ERROR) << "RES_TABLE_TYPE_TYPE has invalid ID 0."; + return false; + } + const size_t entry_count = dtohl(header->entryCount); if (entry_count > std::numeric_limits::max()) { - LOG(ERROR) << "Too many entries in RES_TABLE_TYPE_TYPE."; + LOG(ERROR) << "RES_TABLE_TYPE_TYPE has too many entries (" << entry_count << ")."; return false; } @@ -141,17 +146,17 @@ static bool VerifyResTableType(const ResTable_type* header) { const size_t offsets_length = sizeof(uint32_t) * entry_count; if (offsets_offset > entries_offset || entries_offset - offsets_offset < offsets_length) { - LOG(ERROR) << "Entry offsets overlap actual entry data."; + LOG(ERROR) << "RES_TABLE_TYPE_TYPE entry offsets overlap actual entry data."; return false; } if (entries_offset > dtohl(header->header.size)) { - LOG(ERROR) << "Entry offsets extend beyond chunk."; + LOG(ERROR) << "RES_TABLE_TYPE_TYPE entry offsets extend beyond chunk."; return false; } if (entries_offset & 0x03) { - LOG(ERROR) << "Entries start at unaligned address."; + LOG(ERROR) << "RES_TABLE_TYPE_TYPE entries start at unaligned address."; return false; } return true; @@ -236,7 +241,6 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset return true; } -template bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_idx, const ResTable_config& config, FindEntryResult* out_entry) const { const ResTable_config* best_config = nullptr; @@ -254,13 +258,6 @@ bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_i const size_t entry_count = dtohl(type_chunk->entryCount); const size_t offsets_offset = dtohs(type_chunk->header.headerSize); - // If the package hasn't been verified, do bounds checking. - if (!Verified) { - if (!VerifyResTableType(type_chunk)) { - continue; - } - } - // Check if there is the desired entry in this type. if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { @@ -282,7 +279,7 @@ bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_i // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as // the real offset divided by 4. - best_offset = dtohs(result->offset) * 4u; + best_offset = uint32_t{dtohs(result->offset)} * 4u; } else { if (entry_idx >= entry_count) { // This entry cannot be here. @@ -309,10 +306,8 @@ bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_i return false; } - if (!Verified) { - if (!VerifyResTableEntry(best_type, best_offset, entry_idx)) { - return false; - } + if (UNLIKELY(!VerifyResTableEntry(best_type, best_offset, entry_idx))) { + return false; } const ResTable_entry* best_entry = reinterpret_cast( @@ -334,7 +329,7 @@ bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTab // If the type IDs are offset in this package, we need to take that into account when searching // for a type. const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; - if (ptr == nullptr) { + if (UNLIKELY(ptr == nullptr)) { return false; } @@ -345,54 +340,7 @@ bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTab return false; } } - - // Don't bother checking if the entry ID is larger than the number of entries. - if (entry_idx >= dtohl(ptr->type_spec->entryCount)) { - return false; - } - - if (verified_) { - return FindEntry(ptr, entry_idx, config, out_entry); - } - return FindEntry(ptr, entry_idx, config, out_entry); -} - -static bool VerifyType(const Chunk& chunk) { - ATRACE_CALL(); - const ResTable_type* header = chunk.header(); - - if (!VerifyResTableType(header)) { - return false; - } - - const size_t entry_count = dtohl(header->entryCount); - const size_t offsets_offset = chunk.header_size(); - - if (header->flags & ResTable_type::FLAG_SPARSE) { - // This is encoded as a sparse map, so perform a binary search. - const ResTable_sparseTypeEntry* sparse_indices = - reinterpret_cast(reinterpret_cast(header) + - offsets_offset); - for (size_t i = 0; i < entry_count; i++) { - const uint32_t offset = uint32_t{dtohs(sparse_indices[i].offset)} * 4u; - if (!VerifyResTableEntry(header, offset, i)) { - return false; - } - } - } else { - // Check each entry offset. - const uint32_t* offsets = reinterpret_cast( - reinterpret_cast(header) + offsets_offset); - for (size_t i = 0; i < entry_count; i++) { - uint32_t offset = dtohl(offsets[i]); - if (offset != ResTable_type::NO_ENTRY) { - if (!VerifyResTableEntry(header, offset, i)) { - return false; - } - } - } - } - return true; + return FindEntry(ptr, entry_idx, config, out_entry); } void LoadedPackage::CollectConfigurations(bool exclude_mipmap, @@ -507,7 +455,7 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, sizeof(ResTable_package) - sizeof(ResTable_package::typeIdOffset); const ResTable_package* header = chunk.header(); if (header == nullptr) { - LOG(ERROR) << "Chunk RES_TABLE_PACKAGE_TYPE is too small."; + LOG(ERROR) << "RES_TABLE_PACKAGE_TYPE too small."; return {}; } @@ -529,7 +477,7 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, if (header->header.headerSize >= sizeof(ResTable_package)) { uint32_t type_id_offset = dtohl(header->typeIdOffset); if (type_id_offset > std::numeric_limits::max()) { - LOG(ERROR) << "Type ID offset in RES_TABLE_PACKAGE_TYPE is too large."; + LOG(ERROR) << "RES_TABLE_PACKAGE_TYPE type ID offset too large."; return {}; } loaded_package->type_id_offset_ = static_cast(type_id_offset); @@ -560,7 +508,7 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, status_t err = loaded_package->type_string_pool_.setTo( child_chunk.header(), child_chunk.size()); if (err != NO_ERROR) { - LOG(ERROR) << "Corrupt package type string pool."; + LOG(ERROR) << "RES_STRING_POOL_TYPE for types corrupt."; return {}; } } else if (pool_address == header_address + dtohl(header->keyStrings)) { @@ -568,11 +516,11 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, status_t err = loaded_package->key_string_pool_.setTo( child_chunk.header(), child_chunk.size()); if (err != NO_ERROR) { - LOG(ERROR) << "Corrupt package key string pool."; + LOG(ERROR) << "RES_STRING_POOL_TYPE for keys corrupt."; return {}; } } else { - LOG(WARNING) << "Too many string pool chunks found in package."; + LOG(WARNING) << "Too many RES_STRING_POOL_TYPEs found in RES_TABLE_PACKAGE_TYPE."; } } break; @@ -603,18 +551,18 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, const ResTable_typeSpec* type_spec = child_chunk.header(); if (type_spec == nullptr) { - LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE is too small."; + LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small."; return {}; } if (type_spec->id == 0) { - LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE has invalid ID 0."; + LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE has invalid ID 0."; return {}; } if (loaded_package->type_id_offset_ + static_cast(type_spec->id) > std::numeric_limits::max()) { - LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE has out of range ID."; + LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE has out of range ID."; return {}; } @@ -626,12 +574,12 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, // There can only be 2^16 entries in a type, because that is the ID // space for entries (EEEE) in the resource ID 0xPPTTEEEE. if (entry_count > std::numeric_limits::max()) { - LOG(ERROR) << "Too many entries in RES_TABLE_TYPE_SPEC_TYPE: " << entry_count << "."; + LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE has too many entries (" << entry_count << ")."; return {}; } if (entry_count * sizeof(uint32_t) > chunk.data_size()) { - LOG(ERROR) << "Chunk too small to hold entries in RES_TABLE_TYPE_SPEC_TYPE."; + LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small to hold entries."; return {}; } @@ -650,41 +598,32 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, case RES_TABLE_TYPE_TYPE: { const ResTable_type* type = child_chunk.header(); if (type == nullptr) { - LOG(ERROR) << "Chunk RES_TABLE_TYPE_TYPE is too small."; + LOG(ERROR) << "RES_TABLE_TYPE_TYPE too small."; return {}; } - if (type->id == 0) { - LOG(ERROR) << "Chunk RES_TABLE_TYPE_TYPE has invalid ID 0."; + if (!VerifyResTableType(type)) { return {}; } // Type chunks must be preceded by their TypeSpec chunks. if (!types_builder || type->id - 1 != last_type_idx) { - LOG(ERROR) << "Found RES_TABLE_TYPE_TYPE chunk without RES_TABLE_TYPE_SPEC_TYPE."; + LOG(ERROR) << "RES_TABLE_TYPE_TYPE found without preceding RES_TABLE_TYPE_SPEC_TYPE."; return {}; } - // Only verify the type if we haven't already failed verification. - if (loaded_package->verified_) { - if (!VerifyType(child_chunk)) { - LOG(WARNING) << "Package failed verification, resource retrieval may be slower"; - loaded_package->verified_ = false; - } - } - types_builder->AddType(type); } break; case RES_TABLE_LIBRARY_TYPE: { const ResTable_lib_header* lib = child_chunk.header(); if (lib == nullptr) { - LOG(ERROR) << "Chunk RES_TABLE_LIBRARY_TYPE is too small."; + LOG(ERROR) << "RES_TABLE_LIBRARY_TYPE too small."; return {}; } if (child_chunk.data_size() / sizeof(ResTable_lib_entry) < dtohl(lib->count)) { - LOG(ERROR) << "Chunk too small to hold entries in RES_TABLE_LIBRARY_TYPE."; + LOG(ERROR) << "RES_TABLE_LIBRARY_TYPE too small to hold entries."; return {}; } @@ -751,7 +690,7 @@ bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, const uint8_t type_id = get_type_id(resid); const uint16_t entry_id = get_entry_id(resid); - if (type_id == 0) { + if (UNLIKELY(type_id == 0)) { LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); return false; } @@ -769,7 +708,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, ATRACE_CALL(); const ResTable_header* header = chunk.header(); if (header == nullptr) { - LOG(ERROR) << "Chunk RES_TABLE_TYPE is too small."; + LOG(ERROR) << "RES_TABLE_TYPE too small."; return false; } @@ -788,11 +727,11 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, status_t err = global_string_pool_.setTo(child_chunk.header(), child_chunk.size()); if (err != NO_ERROR) { - LOG(ERROR) << "Corrupt string pool."; + LOG(ERROR) << "RES_STRING_POOL_TYPE corrupt."; return false; } } else { - LOG(WARNING) << "Multiple string pool chunks found in resource table."; + LOG(WARNING) << "Multiple RES_STRING_POOL_TYPEs found in RES_TABLE_TYPE."; } break; diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 377735bcfbba..965e2dbd2fb2 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -119,11 +119,6 @@ class LoadedPackage { return overlay_; } - // Returns true if this package is verified to be valid. - inline bool IsVerified() const { - return verified_; - } - // Returns the map of package name to package ID used in this LoadedPackage. At runtime, a // package could have been assigned a different package ID than what this LoadedPackage was // compiled with. AssetManager rewrites the package IDs so that they are compatible at runtime. @@ -145,7 +140,6 @@ class LoadedPackage { LoadedPackage(); - template bool FindEntry(const util::unique_cptr& type_spec_ptr, uint16_t entry_idx, const ResTable_config& config, FindEntryResult* out_entry) const; @@ -157,7 +151,6 @@ class LoadedPackage { bool dynamic_ = false; bool system_ = false; bool overlay_ = false; - bool verified_ = true; ByteBucketArray> type_specs_; std::vector dynamic_package_map_; diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index ba5844bbfbb1..6c43a67e602f 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -39,7 +39,6 @@ TEST(ApkAssetsTest, LoadApk) { const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); ASSERT_NE(nullptr, loaded_package); - EXPECT_TRUE(loaded_package->IsVerified()); std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); ASSERT_NE(nullptr, asset); @@ -59,7 +58,6 @@ TEST(ApkAssetsTest, LoadApkFromFd) { const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); ASSERT_NE(nullptr, loaded_package); - EXPECT_TRUE(loaded_package->IsVerified()); std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); ASSERT_NE(nullptr, asset); @@ -114,20 +112,6 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { ASSERT_NE(nullptr, loaded_overlay_apk); } -TEST(ApkAssetsTest, LoadUnverifiableApk) { - std::unique_ptr loaded_apk = - ApkAssets::Load(GetTestDataPath() + "/unverified/unverified.apk"); - ASSERT_NE(nullptr, loaded_apk); - - const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - - const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); - ASSERT_NE(nullptr, loaded_package); - - EXPECT_FALSE(loaded_package->IsVerified()); -} - TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp index ea9d427f8f11..85e8f25394e9 100644 --- a/libs/androidfw/tests/AssetManager2_bench.cpp +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -26,12 +26,10 @@ #include "data/basic/R.h" #include "data/libclient/R.h" #include "data/styles/R.h" -#include "data/unverified/R.h" namespace app = com::android::app; namespace basic = com::android::basic; namespace libclient = com::android::libclient; -namespace unverified = com::android::unverified; namespace android { @@ -95,12 +93,6 @@ static void BM_AssetManagerGetResourceOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerGetResourceOld); -static void BM_AssetManagerGetResourceUnverified(benchmark::State& state) { - GetResourceBenchmark({GetTestDataPath() + "/unverified/unverified.apk"}, nullptr /*config*/, - unverified::R::integer::number1, state); -} -BENCHMARK(BM_AssetManagerGetResourceUnverified); - static void BM_AssetManagerGetLibraryResource(benchmark::State& state) { GetResourceBenchmark( {GetTestDataPath() + "/lib_two/lib_two.apk", GetTestDataPath() + "/lib_one/lib_one.apk", @@ -183,30 +175,6 @@ static void BM_AssetManagerGetBagOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerGetBagOld); -static void BM_AssetManagerGetBagUnverified(benchmark::State& state) { - std::unique_ptr apk = - ApkAssets::Load(GetTestDataPath() + "/unverified/unverified.apk"); - if (apk == nullptr) { - state.SkipWithError("Failed to load assets"); - return; - } - - AssetManager2 assets; - assets.SetApkAssets({apk.get()}); - - while (state.KeepRunning()) { - const ResolvedBag* bag = assets.GetBag(unverified::R::array::integerArray1); - const auto bag_end = end(bag); - for (auto iter = begin(bag); iter != bag_end; ++iter) { - uint32_t key = iter->key; - Res_value value = iter->value; - benchmark::DoNotOptimize(key); - benchmark::DoNotOptimize(value); - } - } -} -BENCHMARK(BM_AssetManagerGetBagUnverified); - static void BM_AssetManagerGetResourceLocales(benchmark::State& state) { std::unique_ptr apk = ApkAssets::Load(kFrameworkPath); if (apk == nullptr) { diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 567adfebd31f..92462a6cfadf 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -28,7 +28,6 @@ #include "data/libclient/R.h" #include "data/styles/R.h" #include "data/system/R.h" -#include "data/unverified/R.h" namespace app = com::android::app; namespace appaslib = com::android::appaslib::app; @@ -36,7 +35,6 @@ namespace basic = com::android::basic; namespace lib_one = com::android::lib_one; namespace lib_two = com::android::lib_two; namespace libclient = com::android::libclient; -namespace unverified = com::android::unverified; namespace android { @@ -452,30 +450,4 @@ TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {} TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {} -TEST_F(AssetManager2Test, OperateOnUnverifiedApkAssets) { - std::unique_ptr unverified_assets = - ApkAssets::Load(GetTestDataPath() + "/unverified/unverified.apk"); - ASSERT_NE(nullptr, unverified_assets); - - AssetManager2 assetmanager; - assetmanager.SetApkAssets({unverified_assets.get()}); - - Res_value value; - ResTable_config config; - uint32_t flags; - - EXPECT_EQ(kInvalidCookie, - assetmanager.GetResource(unverified::R::string::test1, false /*may_be_bag*/, 0u, &value, - &config, &flags)); - EXPECT_EQ(kInvalidCookie, - assetmanager.GetResource(unverified::R::string::test2, false /*may_be_bag*/, 0u, &value, - &config, &flags)); - EXPECT_NE(kInvalidCookie, - assetmanager.GetResource(unverified::R::integer::number1, false /*may_be_bag*/, 0u, - &value, &config, &flags)); - - EXPECT_EQ(nullptr, assetmanager.GetBag(unverified::R::style::Theme1)); - EXPECT_NE(nullptr, assetmanager.GetBag(unverified::R::array::integerArray1)); -} - } // namespace android diff --git a/libs/androidfw/tests/data/unverified/R.h b/libs/androidfw/tests/data/unverified/R.h deleted file mode 100644 index b734b4988ea2..000000000000 --- a/libs/androidfw/tests/data/unverified/R.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TESTS_DATA_UNVERIFIED_R_H_ -#define TESTS_DATA_UNVERIFIED_R_H_ - -#include - -#include "tests/data/basic/R.h" - -namespace com { -namespace android { - -namespace unverified = basic; - -} // namespace android -} // namespace com - -#endif /* TESTS_DATA_UNVERIFIED_R_H_ */ diff --git a/libs/androidfw/tests/data/unverified/unverified.apk b/libs/androidfw/tests/data/unverified/unverified.apk deleted file mode 100644 index 234b390dc37d..000000000000 Binary files a/libs/androidfw/tests/data/unverified/unverified.apk and /dev/null differ -- cgit v1.2.3-59-g8ed1b From b20a0ce59f59cb5ec857748e056cc341dbd13b92 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 23 Jan 2017 12:58:11 -0800 Subject: Replace AssetManager with AssetManager2 implementation Test: Existing CTS tests pass Test: make libandroidfw_tests Change-Id: I858f7e1d909c08273b096601136e3f28e15eb5d4 --- core/java/android/content/pm/PackageParser.java | 19 +- core/java/android/content/res/ApkAssets.java | 221 ++ core/java/android/content/res/AssetManager.java | 1302 +++++---- core/java/android/content/res/Resources.java | 9 +- core/java/android/content/res/ResourcesImpl.java | 22 +- core/java/android/content/res/TypedArray.java | 185 +- core/java/android/content/res/XmlBlock.java | 8 +- core/jni/Android.bp | 3 +- core/jni/AndroidRuntime.cpp | 2 + core/jni/android/graphics/FontFamily.cpp | 29 +- core/jni/android_app_NativeActivity.cpp | 2 +- core/jni/android_content_res_ApkAssets.cpp | 150 + core/jni/android_util_AssetManager.cpp | 2884 +++++++++----------- .../android_runtime/android_util_AssetManager.h | 15 +- libs/androidfw/AssetManager2.cpp | 6 +- libs/androidfw/AttributeResolution.cpp | 263 +- libs/androidfw/LoadedArsc.cpp | 2 - libs/androidfw/include/androidfw/AttributeFinder.h | 6 + .../include/androidfw/AttributeResolution.h | 11 +- libs/androidfw/include/androidfw/LoadedArsc.h | 13 +- libs/androidfw/include/androidfw/MutexGuard.h | 101 + libs/androidfw/tests/AttributeResolution_test.cpp | 39 +- libs/androidfw/tests/BenchmarkHelpers.cpp | 4 +- native/android/asset_manager.cpp | 28 +- rs/jni/android_renderscript_RenderScript.cpp | 30 +- 25 files changed, 2861 insertions(+), 2493 deletions(-) create mode 100644 core/java/android/content/res/ApkAssets.java create mode 100644 core/jni/android_content_res_ApkAssets.cpp create mode 100644 libs/androidfw/include/androidfw/MutexGuard.h (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 77eb57f25613..07af26b0d4a4 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -55,6 +55,7 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper; import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.SplitAssetDependencyLoader; import android.content.pm.split.SplitAssetLoader; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -1639,21 +1640,19 @@ public class PackageParser { int flags) throws PackageParserException { final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); - AssetManager assets = null; + ApkAssets apkAssets = null; XmlResourceParser parser = null; try { - assets = newConfiguredAssetManager(); - int cookie = fd != null - ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath); - if (cookie == 0) { + try { + apkAssets = fd != null + ? ApkAssets.loadFromFd(fd, debugPathName, false, false) + : ApkAssets.loadFromPath(apkPath); + } catch (IOException e) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse " + apkPath); } - final DisplayMetrics metrics = new DisplayMetrics(); - metrics.setToDefaults(); - - parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); + parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME); final Signature[] signatures; final Certificate[][] certificates; @@ -1682,7 +1681,7 @@ public class PackageParser { "Failed to parse " + apkPath, e); } finally { IoUtils.closeQuietly(parser); - IoUtils.closeQuietly(assets); + IoUtils.closeQuietly(apkAssets); } } diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java new file mode 100644 index 000000000000..b087c48d8d4c --- /dev/null +++ b/core/java/android/content/res/ApkAssets.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.content.res; + +import android.annotation.NonNull; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; + +import java.io.FileDescriptor; +import java.io.IOException; + +/** + * The loaded, immutable, in-memory representation of an APK. + * + * The main implementation is native C++ and there is very little API surface exposed here. The APK + * is mainly accessed via {@link AssetManager}. + * + * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers, + * making the creation of AssetManagers very cheap. + * @hide + */ +public final class ApkAssets implements AutoCloseable { + @GuardedBy("this") private long mNativePtr; + @GuardedBy("this") private StringBlock mStringBlock; + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException { + return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @param system When true, the APK is loaded as a system APK (framework). + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system) + throws IOException { + return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @param system When true, the APK is loaded as a system APK (framework). + * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are + * loaded as a shared library. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system, + boolean forceSharedLibrary) throws IOException { + return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications. + * + * Performs a dup of the underlying fd, so you must take care of still closing + * the FileDescriptor yourself (and can do that whenever you want). + * + * @param fd The FileDescriptor of an open, readable APK. + * @param friendlyName The friendly name used to identify this ApkAssets when logging. + * @param system When true, the APK is loaded as a system APK (framework). + * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are + * loaded as a shared library. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd, + @NonNull String friendlyName, boolean system, boolean forceSharedLibrary) + throws IOException { + return new ApkAssets(fd, friendlyName, system, forceSharedLibrary); + } + + /** + * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path + * is encoded within the IDMAP. + * + * @param idmapPath Path to the IDMAP of an overlay APK. + * @param system When true, the APK is loaded as a system APK (framework). + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system) + throws IOException { + return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/); + } + + private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) + throws IOException { + Preconditions.checkNotNull(path, "path"); + mNativePtr = nativeLoad(path, system, forceSharedLib, overlay); + mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); + } + + private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system, + boolean forceSharedLib) throws IOException { + Preconditions.checkNotNull(fd, "fd"); + Preconditions.checkNotNull(friendlyName, "friendlyName"); + mNativePtr = nativeLoadFromFd(fd, friendlyName, system, forceSharedLib); + mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); + } + + @NonNull String getAssetPath() { + synchronized (this) { + ensureValidLocked(); + return nativeGetAssetPath(mNativePtr); + } + } + + CharSequence getStringFromPool(int idx) { + synchronized (this) { + ensureValidLocked(); + return mStringBlock.get(idx); + } + } + + /** + * Retrieve a parser for a compiled XML file. This is associated with a single APK and + * NOT a full AssetManager. This means that shared-library references will not be + * dynamically assigned runtime package IDs. + * + * @param fileName The path to the file within the APK. + * @return An XmlResourceParser. + * @throws IOException if the file was not found or an error occurred retrieving it. + */ + public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException { + Preconditions.checkNotNull(fileName, "fileName"); + synchronized (this) { + ensureValidLocked(); + long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName); + try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) { + XmlResourceParser parser = block.newParser(); + // If nativeOpenXml doesn't throw, it will always return a valid native pointer, + // which makes newParser always return non-null. But let's be paranoid. + if (parser == null) { + throw new AssertionError("block.newParser() returned a null parser"); + } + return parser; + } + } + } + + /** + * Returns false if the underlying APK was changed since this ApkAssets was loaded. + */ + public boolean isUpToDate() { + synchronized (this) { + ensureValidLocked(); + return nativeIsUpToDate(mNativePtr); + } + } + + /** + * Closes the ApkAssets and destroys the underlying native implementation. Further use of the + * ApkAssets object will cause exceptions to be thrown. + * + * Calling close on an already closed ApkAssets does nothing. + */ + @Override + public void close() { + synchronized (this) { + if (mNativePtr == 0) { + return; + } + + mStringBlock = null; + nativeDestroy(mNativePtr); + mNativePtr = 0; + } + } + + @Override + protected void finalize() throws Throwable { + if (mNativePtr != 0) { + nativeDestroy(mNativePtr); + } + } + + private void ensureValidLocked() { + if (mNativePtr == 0) { + throw new RuntimeException("ApkAssets is closed"); + } + } + + private static native long nativeLoad( + @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) + throws IOException; + private static native long nativeLoadFromFd(@NonNull FileDescriptor fd, + @NonNull String friendlyName, boolean system, boolean forceSharedLib) + throws IOException; + private static native void nativeDestroy(long ptr); + private static native @NonNull String nativeGetAssetPath(long ptr); + private static native long nativeGetStringBlock(long ptr); + private static native boolean nativeIsUpToDate(long ptr); + private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException; +} diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 78665609bdd4..4f614a73a521 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -18,9 +18,11 @@ package android.content.res; import android.annotation.AnyRes; import android.annotation.ArrayRes; +import android.annotation.AttrRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; +import android.annotation.StyleRes; import android.content.pm.ActivityInfo; import android.content.res.Configuration.NativeConfig; import android.os.ParcelFileDescriptor; @@ -28,10 +30,18 @@ import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; -import java.io.FileDescriptor; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; + +import java.io.BufferedReader; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.channels.FileLock; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; /** @@ -42,7 +52,17 @@ import java.util.HashMap; * bytes. */ public final class AssetManager implements AutoCloseable { - /* modes used when opening an asset */ + private static final String TAG = "AssetManager"; + private static final boolean DEBUG_REFS = false; + + private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk"; + + private static final Object sSync = new Object(); + + // Not private for LayoutLib's BridgeAssetManager. + @GuardedBy("sSync") static AssetManager sSystem = null; + + @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0]; /** * Mode for {@link #open(String, int)}: no specific information about how @@ -65,90 +85,282 @@ public final class AssetManager implements AutoCloseable { */ public static final int ACCESS_BUFFER = 3; - private static final String TAG = "AssetManager"; - private static final boolean localLOGV = false || false; - - private static final boolean DEBUG_REFS = false; - - private static final Object sSync = new Object(); - /*package*/ static AssetManager sSystem = null; + @GuardedBy("this") private final TypedValue mValue = new TypedValue(); + @GuardedBy("this") private final long[] mOffsets = new long[2]; - private final TypedValue mValue = new TypedValue(); - private final long[] mOffsets = new long[2]; - - // For communication with native code. - private long mObject; + // Pointer to native implementation, stuffed inside a long. + @GuardedBy("this") private long mObject; + + // The loaded asset paths. + @GuardedBy("this") private ApkAssets[] mApkAssets; + + // Debug/reference counting implementation. + @GuardedBy("this") private boolean mOpen = true; + @GuardedBy("this") private int mNumRefs = 1; + @GuardedBy("this") private HashMap mRefStacks; - private StringBlock mStringBlocks[] = null; - - private int mNumRefs = 1; - private boolean mOpen = true; - private HashMap mRefStacks; - /** * Create a new AssetManager containing only the basic system assets. * Applications will not generally use this method, instead retrieving the * appropriate asset manager with {@link Resources#getAssets}. Not for * use by applications. - * {@hide} + * @hide */ public AssetManager() { - synchronized (this) { - if (DEBUG_REFS) { - mNumRefs = 0; - incRefsLocked(this.hashCode()); - } - init(false); - if (localLOGV) Log.v(TAG, "New asset manager: " + this); - ensureSystemAssets(); + final ApkAssets[] assets; + synchronized (sSync) { + createSystemAssetsInZygoteLocked(); + assets = sSystemApkAssets; } - } - private static void ensureSystemAssets() { - synchronized (sSync) { - if (sSystem == null) { - AssetManager system = new AssetManager(true); - system.makeStringBlocks(null); - sSystem = system; - } + mObject = nativeCreate(); + if (DEBUG_REFS) { + mNumRefs = 0; + incRefsLocked(hashCode()); } + + // Always set the framework resources. + setApkAssets(assets, false /*invalidateCaches*/); } - - private AssetManager(boolean isSystem) { + + /** + * Private constructor that doesn't call ensureSystemAssets. + * Used for the creation of system assets. + */ + @SuppressWarnings("unused") + private AssetManager(boolean sentinel) { + mObject = nativeCreate(); if (DEBUG_REFS) { - synchronized (this) { - mNumRefs = 0; - incRefsLocked(this.hashCode()); + mNumRefs = 0; + incRefsLocked(hashCode()); + } + } + + /** + * This must be called from Zygote so that system assets are shared by all applications. + * @hide + */ + private static void createSystemAssetsInZygoteLocked() { + if (sSystem != null) { + return; + } + + // Make sure that all IDMAPs are up to date. + nativeVerifySystemIdmaps(); + + try { + ArrayList apkAssets = new ArrayList<>(); + apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/)); + + // Load all static RROs. + try (FileInputStream fis = new FileInputStream( + "/data/resource-cache/overlays.list"); + BufferedReader br = new BufferedReader(new InputStreamReader(fis))) { + // Acquire a lock so that any idmap scanning doesn't impact the current set. + try (FileLock flock = fis.getChannel().lock(0, Long.MAX_VALUE, + true /*shared*/)) { + for (String line; (line = br.readLine()) != null; ) { + String idmapPath = line.split(" ")[1]; + apkAssets.add( + ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/)); + } + } } + + sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]); + sSystem = new AssetManager(true /*sentinel*/); + sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/); + } catch (IOException e) { + throw new IllegalStateException("Failed to create system AssetManager", e); } - init(true); - if (localLOGV) Log.v(TAG, "New asset manager: " + this); } /** * Return a global shared asset manager that provides access to only * system assets (no application assets). - * {@hide} + * @hide */ public static AssetManager getSystem() { - ensureSystemAssets(); - return sSystem; + synchronized (sSync) { + createSystemAssetsInZygoteLocked(); + return sSystem; + } } /** * Close this asset manager. */ + @Override public void close() { - synchronized(this) { - //System.out.println("Release: num=" + mNumRefs - // + ", released=" + mReleased); - if (mOpen) { - mOpen = false; - decRefsLocked(this.hashCode()); + synchronized (this) { + if (!mOpen) { + return; + } + + mOpen = false; + decRefsLocked(hashCode()); + } + } + + /** + * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)} + * family of methods. + * + * @param apkAssets The new set of paths. + * @param invalidateCaches Whether to invalidate any caches. This should almost always be true. + * Set this to false if you are appending new resources + * (not new configurations). + * @hide + */ + public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) { + Preconditions.checkNotNull(apkAssets, "apkAssets"); + synchronized (this) { + ensureValidLocked(); + mApkAssets = apkAssets; + nativeSetApkAssets(mObject, apkAssets, invalidateCaches); + if (invalidateCaches) { + // Invalidate all caches. + invalidateCachesLocked(-1); } } } + /** + * Invalidates the caches in this AssetManager according to the bitmask `diff`. + * + * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}. + * @see ActivityInfo.Config + */ + private void invalidateCachesLocked(int diff) { + // TODO(adamlesinski): Currently there are no caches to invalidate in Java code. + } + + /** + * @hide + */ + public @NonNull ApkAssets[] getApkAssets() { + synchronized (this) { + ensureValidLocked(); + return mApkAssets; + } + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addAssetPath(String path) { + return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/); + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addAssetPathAsSharedLibrary(String path) { + return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/); + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addOverlayPath(String path) { + return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/); + } + + private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) { + Preconditions.checkNotNull(path, "path"); + synchronized (this) { + ensureOpenLocked(); + final int count = mApkAssets.length; + for (int i = 0; i < count; i++) { + if (mApkAssets[i].getAssetPath().equals(path)) { + return i + 1; + } + } + + final ApkAssets assets; + try { + if (overlay) { + // TODO(b/70343104): This hardcoded path will be removed once + // addAssetPathInternal is deleted. + final String idmapPath = "/data/resource-cache/" + + path.substring(1).replace('/', '@') + + "@idmap"; + assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/); + } else { + assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib); + } + } catch (IOException e) { + return 0; + } + + final ApkAssets[] newApkAssets = Arrays.copyOf(mApkAssets, count + 1); + newApkAssets[count] = assets; + setApkAssets(newApkAssets, true); + return count + 1; + } + } + + /** + * Ensures that the native implementation has not been destroyed. + * The AssetManager may have been closed, but references to it still exist + * and therefore the native implementation is not destroyed. + */ + private void ensureValidLocked() { + if (mObject == 0) { + throw new RuntimeException("AssetManager has been destroyed"); + } + } + + /** + * Ensures that the AssetManager has not been explicitly closed. If this method passes, + * then this implies that ensureValidLocked() also passes. + */ + private void ensureOpenLocked() { + if (!mOpen) { + throw new RuntimeException("AssetManager has been closed"); + } + } + + /** + * Populates {@code outValue} with the data associated a particular + * resource identifier for the current configuration. + * + * @param resId the resource identifier to load + * @param densityDpi the density bucket for which to load the resource + * @param outValue the typed value in which to put the data + * @param resolveRefs {@code true} to resolve references, {@code false} + * to leave them unresolved + * @return {@code true} if the data was loaded into {@code outValue}, + * {@code false} otherwise + */ + boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, + boolean resolveRefs) { + Preconditions.checkNotNull(outValue, "outValue"); + synchronized (this) { + ensureValidLocked(); + final int cookie = nativeGetResourceValue( + mObject, resId, (short) densityDpi, outValue, resolveRefs); + if (cookie <= 0) { + return false; + } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); + } + return true; + } + } + /** * Retrieves the string value associated with a particular resource * identifier for the current configuration. @@ -156,8 +368,7 @@ public final class AssetManager implements AutoCloseable { * @param resId the resource identifier to load * @return the string value, or {@code null} */ - @Nullable - final CharSequence getResourceText(@StringRes int resId) { + @Nullable CharSequence getResourceText(@StringRes int resId) { synchronized (this) { final TypedValue outValue = mValue; if (getResourceValue(resId, 0, outValue, true)) { @@ -172,15 +383,15 @@ public final class AssetManager implements AutoCloseable { * identifier for the current configuration. * * @param resId the resource identifier to load - * @param bagEntryId + * @param bagEntryId the index into the bag to load * @return the string value, or {@code null} */ - @Nullable - final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { + @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { synchronized (this) { + ensureValidLocked(); final TypedValue outValue = mValue; - final int block = loadResourceBagValue(resId, bagEntryId, outValue, true); - if (block < 0) { + final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue); + if (cookie <= 0) { return null; } @@ -189,52 +400,60 @@ public final class AssetManager implements AutoCloseable { outValue.changingConfigurations); if (outValue.type == TypedValue.TYPE_STRING) { - return mStringBlocks[block].get(outValue.data); + return mApkAssets[cookie - 1].getStringFromPool(outValue.data); } return outValue.coerceToString(); } } + int getResourceArraySize(@ArrayRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceArraySize(mObject, resId); + } + } + /** - * Retrieves the string array associated with a particular resource - * identifier for the current configuration. + * Populates `outData` with array elements of `resId`. `outData` is normally + * used with + * {@link TypedArray}. * - * @param resId the resource identifier of the string array - * @return the string array, or {@code null} + * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES} + * long, + * with the indices of the data representing the type, value, asset cookie, + * resource ID, + * configuration change mask, and density of the element. + * + * @param resId The resource ID of an array resource. + * @param outData The array to populate with data. + * @return The length of the array. + * + * @see TypedArray#STYLE_TYPE + * @see TypedArray#STYLE_DATA + * @see TypedArray#STYLE_ASSET_COOKIE + * @see TypedArray#STYLE_RESOURCE_ID + * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS + * @see TypedArray#STYLE_DENSITY */ - @Nullable - final String[] getResourceStringArray(@ArrayRes int resId) { - return getArrayStringResource(resId); + int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) { + Preconditions.checkNotNull(outData, "outData"); + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceArray(mObject, resId, outData); + } } /** - * Populates {@code outValue} with the data associated a particular - * resource identifier for the current configuration. + * Retrieves the string array associated with a particular resource + * identifier for the current configuration. * - * @param resId the resource identifier to load - * @param densityDpi the density bucket for which to load the resource - * @param outValue the typed value in which to put the data - * @param resolveRefs {@code true} to resolve references, {@code false} - * to leave them unresolved - * @return {@code true} if the data was loaded into {@code outValue}, - * {@code false} otherwise + * @param resId the resource identifier of the string array + * @return the string array, or {@code null} */ - final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, - boolean resolveRefs) { + @Nullable String[] getResourceStringArray(@ArrayRes int resId) { synchronized (this) { - final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs); - if (block < 0) { - return false; - } - - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); - - if (outValue.type == TypedValue.TYPE_STRING) { - outValue.string = mStringBlocks[block].get(outValue.data); - } - return true; + ensureValidLocked(); + return nativeGetResourceStringArray(mObject, resId); } } @@ -244,26 +463,48 @@ public final class AssetManager implements AutoCloseable { * * @param resId the resource id of the string array */ - final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { + @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { synchronized (this) { - final int[] rawInfoArray = getArrayStringInfo(resId); + ensureValidLocked(); + final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId); if (rawInfoArray == null) { return null; } + final int rawInfoArrayLen = rawInfoArray.length; final int infoArrayLen = rawInfoArrayLen / 2; - int block; - int index; final CharSequence[] retArray = new CharSequence[infoArrayLen]; for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) { - block = rawInfoArray[i]; - index = rawInfoArray[i + 1]; - retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null; + int cookie = rawInfoArray[i]; + int index = rawInfoArray[i + 1]; + retArray[j] = (index >= 0 && cookie > 0) + ? mApkAssets[cookie - 1].getStringFromPool(index) : null; } return retArray; } } + @Nullable int[] getResourceIntArray(@ArrayRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceIntArray(mObject, resId); + } + } + + /** + * Get the attributes for a style resource. These are the <item> + * elements in + * a <style> resource. + * @param resId The resource ID of the style + * @return An array of attribute IDs. + */ + @AttrRes int[] getStyleAttributes(@StyleRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetStyleAttributes(mObject, resId); + } + } + /** * Populates {@code outValue} with the data associated with a particular * resource identifier for the current configuration. Resolves theme @@ -277,73 +518,88 @@ public final class AssetManager implements AutoCloseable { * @return {@code true} if the data was loaded into {@code outValue}, * {@code false} otherwise */ - final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, + boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, boolean resolveRefs) { - final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs); - if (block < 0) { - return false; + Preconditions.checkNotNull(outValue, "outValue"); + synchronized (this) { + ensureValidLocked(); + final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue, + resolveRefs); + if (cookie <= 0) { + return false; + } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); + } + return true; } + } - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); + void dumpTheme(long theme, int priority, String tag, String prefix) { + synchronized (this) { + ensureValidLocked(); + nativeThemeDump(mObject, theme, priority, tag, prefix); + } + } - if (outValue.type == TypedValue.TYPE_STRING) { - final StringBlock[] blocks = ensureStringBlocks(); - outValue.string = blocks[block].get(outValue.data); + @Nullable String getResourceName(@AnyRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceName(mObject, resId); } - return true; } - /** - * Ensures the string blocks are loaded. - * - * @return the string blocks - */ - @NonNull - final StringBlock[] ensureStringBlocks() { + @Nullable String getResourcePackageName(@AnyRes int resId) { synchronized (this) { - if (mStringBlocks == null) { - makeStringBlocks(sSystem.mStringBlocks); - } - return mStringBlocks; + ensureValidLocked(); + return nativeGetResourcePackageName(mObject, resId); } } - /*package*/ final void makeStringBlocks(StringBlock[] seed) { - final int seedNum = (seed != null) ? seed.length : 0; - final int num = getStringBlockCount(); - mStringBlocks = new StringBlock[num]; - if (localLOGV) Log.v(TAG, "Making string blocks for " + this - + ": " + num); - for (int i=0; i Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len; + final long len = nativeAssetGetRemainingLength(mAssetNativePtr); + return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len; } + + @Override public final void close() throws IOException { - synchronized (AssetManager.this) { - if (mAsset != 0) { - destroyAsset(mAsset); - mAsset = 0; + if (mAssetNativePtr != 0) { + nativeAssetDestroy(mAssetNativePtr); + mAssetNativePtr = 0; + + synchronized (AssetManager.this) { decRefsLocked(hashCode()); } } } + + @Override public final void mark(int readlimit) { - mMarkPos = seekAsset(mAsset, 0, 0); + mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0); } + + @Override public final void reset() throws IOException { - seekAsset(mAsset, mMarkPos, -1); + nativeAssetSeek(mAssetNativePtr, mMarkPos, -1); } - public final int read(byte[] b) throws IOException { - return readAsset(mAsset, b, 0, b.length); + + @Override + public final int read(@NonNull byte[] b) throws IOException { + Preconditions.checkNotNull(b, "b"); + return nativeAssetRead(mAssetNativePtr, b, 0, b.length); } - public final int read(byte[] b, int off, int len) throws IOException { - return readAsset(mAsset, b, off, len); + + @Override + public final int read(@NonNull byte[] b, int off, int len) throws IOException { + Preconditions.checkNotNull(b, "b"); + return nativeAssetRead(mAssetNativePtr, b, off, len); } + + @Override public final long skip(long n) throws IOException { - long pos = seekAsset(mAsset, 0, 0); - if ((pos+n) > mLength) { - n = mLength-pos; + long pos = nativeAssetSeek(mAssetNativePtr, 0, 0); + if ((pos + n) > mLength) { + n = mLength - pos; } if (n > 0) { - seekAsset(mAsset, n, 0); + nativeAssetSeek(mAssetNativePtr, n, 0); } return n; } - protected void finalize() throws Throwable - { + @Override + protected void finalize() throws Throwable { close(); } - - private long mAsset; - private long mLength; - private long mMarkPos; - } - - /** - * Add an additional set of assets to the asset manager. This can be - * either a directory or ZIP file. Not for use by applications. Returns - * the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public final int addAssetPath(String path) { - return addAssetPathInternal(path, false); - } - - /** - * Add an application assets to the asset manager and loading it as shared library. - * This can be either a directory or ZIP file. Not for use by applications. Returns - * the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public final int addAssetPathAsSharedLibrary(String path) { - return addAssetPathInternal(path, true); - } - - private final int addAssetPathInternal(String path, boolean appAsLib) { - synchronized (this) { - int res = addAssetPathNative(path, appAsLib); - makeStringBlocks(mStringBlocks); - return res; - } - } - - private native final int addAssetPathNative(String path, boolean appAsLib); - - /** - * Add an additional set of assets to the asset manager from an already open - * FileDescriptor. Not for use by applications. - * This does not give full AssetManager functionality for these assets, - * since the origin of the file is not known for purposes of sharing, - * overlay resolution, and other features. However it does allow you - * to do simple access to the contents of the given fd as an apk file. - * Performs a dup of the underlying fd, so you must take care of still closing - * the FileDescriptor yourself (and can do that whenever you want). - * Returns the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public int addAssetFd(FileDescriptor fd, String debugPathName) { - return addAssetFdInternal(fd, debugPathName, false); - } - - private int addAssetFdInternal(FileDescriptor fd, String debugPathName, - boolean appAsLib) { - synchronized (this) { - int res = addAssetFdNative(fd, debugPathName, appAsLib); - makeStringBlocks(mStringBlocks); - return res; - } - } - - private native int addAssetFdNative(FileDescriptor fd, String debugPathName, - boolean appAsLib); - - /** - * Add a set of assets to overlay an already added set of assets. - * - * This is only intended for application resources. System wide resources - * are handled before any Java code is executed. - * - * {@hide} - */ - - public final int addOverlayPath(String idmapPath) { - synchronized (this) { - int res = addOverlayPathNative(idmapPath); - makeStringBlocks(mStringBlocks); - return res; - } - } - - /** - * See addOverlayPath. - * - * {@hide} - */ - public native final int addOverlayPathNative(String idmapPath); - - /** - * Add multiple sets of assets to the asset manager at once. See - * {@link #addAssetPath(String)} for more information. Returns array of - * cookies for each added asset with 0 indicating failure, or null if - * the input array of paths is null. - * {@hide} - */ - public final int[] addAssetPaths(String[] paths) { - if (paths == null) { - return null; - } - - int[] cookies = new int[paths.length]; - for (int i = 0; i < paths.length; i++) { - cookies[i] = addAssetPath(paths[i]); - } - - return cookies; } /** * Determine whether the state in this asset manager is up-to-date with * the files on the filesystem. If false is returned, you need to * instantiate a new AssetManager class to see the new data. - * {@hide} + * @hide */ - public native final boolean isUpToDate(); + public boolean isUpToDate() { + for (ApkAssets apkAssets : getApkAssets()) { + if (!apkAssets.isUpToDate()) { + return false; + } + } + return true; + } /** * Get the locales that this asset manager contains data for. @@ -784,7 +1058,12 @@ public final class AssetManager implements AutoCloseable { * are of the form {@code ll_CC} where {@code ll} is a two letter language code, * and {@code CC} is a two letter country code. */ - public native final String[] getLocales(); + public String[] getLocales() { + synchronized (this) { + ensureValidLocked(); + return nativeGetLocales(mObject, false /*excludeSystem*/); + } + } /** * Same as getLocales(), except that locales that are only provided by the system (i.e. those @@ -794,131 +1073,57 @@ public final class AssetManager implements AutoCloseable { * assets support Cherokee and French, getLocales() would return * [Cherokee, English, French, German], while getNonSystemLocales() would return * [Cherokee, French]. - * {@hide} + * @hide */ - public native final String[] getNonSystemLocales(); - - /** {@hide} */ - public native final Configuration[] getSizeConfigurations(); + public String[] getNonSystemLocales() { + synchronized (this) { + ensureValidLocked(); + return nativeGetLocales(mObject, true /*excludeSystem*/); + } + } /** - * Change the configuation used when retrieving resources. Not for use by - * applications. - * {@hide} + * @hide */ - public native final void setConfiguration(int mcc, int mnc, String locale, - int orientation, int touchscreen, int density, int keyboard, - int keyboardHidden, int navigation, int screenWidth, int screenHeight, - int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, - int screenLayout, int uiMode, int colorMode, int majorVersion); + Configuration[] getSizeConfigurations() { + synchronized (this) { + ensureValidLocked(); + return nativeGetSizeConfigurations(mObject); + } + } /** - * Retrieve the resource identifier for the given resource name. + * Change the configuration used when retrieving resources. Not for use by + * applications. + * @hide */ - /*package*/ native final int getResourceIdentifier(String name, - String defType, - String defPackage); + public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation, + int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, + int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, + int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) { + synchronized (this) { + ensureValidLocked(); + nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density, + keyboard, keyboardHidden, navigation, screenWidth, screenHeight, + smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode, + colorMode, majorVersion); + } + } - /*package*/ native final String getResourceName(int resid); - /*package*/ native final String getResourcePackageName(int resid); - /*package*/ native final String getResourceTypeName(int resid); - /*package*/ native final String getResourceEntryName(int resid); - - private native final long openAsset(String fileName, int accessMode); - private final native ParcelFileDescriptor openAssetFd(String fileName, - long[] outOffsets) throws IOException; - private native final long openNonAssetNative(int cookie, String fileName, - int accessMode); - private native ParcelFileDescriptor openNonAssetFdNative(int cookie, - String fileName, long[] outOffsets) throws IOException; - private native final void destroyAsset(long asset); - private native final int readAssetChar(long asset); - private native final int readAsset(long asset, byte[] b, int off, int len); - private native final long seekAsset(long asset, long offset, int whence); - private native final long getAssetLength(long asset); - private native final long getAssetRemainingLength(long asset); - - /** Returns true if the resource was found, filling in mRetStringBlock and - * mRetData. */ - private native final int loadResourceValue(int ident, short density, TypedValue outValue, - boolean resolve); - /** Returns true if the resource was found, filling in mRetStringBlock and - * mRetData. */ - private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue, - boolean resolve); - /*package*/ static final int STYLE_NUM_ENTRIES = 6; - /*package*/ static final int STYLE_TYPE = 0; - /*package*/ static final int STYLE_DATA = 1; - /*package*/ static final int STYLE_ASSET_COOKIE = 2; - /*package*/ static final int STYLE_RESOURCE_ID = 3; - - /* Offset within typed data array for native changingConfigurations. */ - static final int STYLE_CHANGING_CONFIGURATIONS = 4; - - /*package*/ static final int STYLE_DENSITY = 5; - /*package*/ native static final void applyStyle(long theme, - int defStyleAttr, int defStyleRes, long xmlParser, - int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress); - /*package*/ native static final boolean resolveAttrs(long theme, - int defStyleAttr, int defStyleRes, int[] inValues, - int[] inAttrs, int[] outValues, int[] outIndices); - /*package*/ native final boolean retrieveAttributes( - long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); - /*package*/ native final int getArraySize(int resource); - /*package*/ native final int retrieveArray(int resource, int[] outValues); - private native final int getStringBlockCount(); - private native final long getNativeStringBlock(int block); - - /** - * {@hide} - */ - public native final String getCookieName(int cookie); - - /** - * {@hide} - */ - public native final SparseArray getAssignedPackageIdentifiers(); - - /** - * {@hide} - */ - public native static final int getGlobalAssetCount(); - - /** - * {@hide} - */ - public native static final String getAssetAllocations(); - /** - * {@hide} + * @hide */ - public native static final int getGlobalAssetManagerCount(); - - private native final long newTheme(); - private native final void deleteTheme(long theme); - /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force); - /*package*/ native static final void copyTheme(long dest, long source); - /*package*/ native static final void clearTheme(long theme); - /*package*/ native static final int loadThemeAttributeValue(long theme, int ident, - TypedValue outValue, - boolean resolve); - /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix); - /*package*/ native static final @NativeConfig int getThemeChangingConfigurations(long theme); - - private native final long openXmlAssetNative(int cookie, String fileName); - - private native final String[] getArrayStringResource(int arrayRes); - private native final int[] getArrayStringInfo(int arrayRes); - /*package*/ native final int[] getArrayIntResource(int arrayRes); - /*package*/ native final int[] getStyleAttributes(int themeRes); - - private native final void init(boolean isSystem); - private native final void destroy(); - - private final void incRefsLocked(long id) { + public SparseArray getAssignedPackageIdentifiers() { + synchronized (this) { + ensureValidLocked(); + return nativeGetAssignedPackageIdentifiers(mObject); + } + } + + private void incRefsLocked(long id) { if (DEBUG_REFS) { if (mRefStacks == null) { - mRefStacks = new HashMap(); + mRefStacks = new HashMap<>(); } RuntimeException ex = new RuntimeException(); ex.fillInStackTrace(); @@ -926,16 +1131,117 @@ public final class AssetManager implements AutoCloseable { } mNumRefs++; } - - private final void decRefsLocked(long id) { + + private void decRefsLocked(long id) { if (DEBUG_REFS && mRefStacks != null) { mRefStacks.remove(id); } mNumRefs--; - //System.out.println("Dec streams: mNumRefs=" + mNumRefs - // + " mReleased=" + mReleased); - if (mNumRefs == 0) { - destroy(); + if (mNumRefs == 0 && mObject != 0) { + nativeDestroy(mObject); + mObject = 0; } } + + // AssetManager setup native methods. + private static native long nativeCreate(); + private static native void nativeDestroy(long ptr); + private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, + boolean invalidateCaches); + private static native void nativeSetConfiguration(long ptr, int mcc, int mnc, + @Nullable String locale, int orientation, int touchscreen, int density, int keyboard, + int keyboardHidden, int navigation, int screenWidth, int screenHeight, + int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, + int uiMode, int colorMode, int majorVersion); + private static native @NonNull SparseArray nativeGetAssignedPackageIdentifiers( + long ptr); + + // File native methods. + private static native @Nullable String[] nativeList(long ptr, @NonNull String path) + throws IOException; + private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode); + private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr, + @NonNull String fileName, long[] outOffsets) throws IOException; + private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName, + int accessMode); + private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie, + @NonNull String fileName, @NonNull long[] outOffsets) throws IOException; + private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName); + + // Primitive resource native methods. + private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density, + @NonNull TypedValue outValue, boolean resolveReferences); + private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId, + @NonNull TypedValue outValue); + + private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr, + @StyleRes int resId); + private static native @Nullable String[] nativeGetResourceStringArray(long ptr, + @ArrayRes int resId); + private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr, + @ArrayRes int resId); + private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId); + private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId); + private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId, + @NonNull int[] outValues); + + // Resource name/ID native methods. + private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name, + @Nullable String defType, @Nullable String defPackage); + private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid); + private static native @Nullable String nativeGetResourcePackageName(long ptr, + @AnyRes int resid); + private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid); + private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid); + private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem); + private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr); + + // Style attribute retrieval native methods. + private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, + @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs, + long outValuesAddress, long outIndicesAddress); + private static native boolean nativeResolveAttrs(long ptr, long themePtr, + @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, + @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); + private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr, + @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); + + // Theme related native methods + private static native long nativeThemeCreate(long ptr); + private static native void nativeThemeDestroy(long themePtr); + private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, + boolean force); + static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr); + static native void nativeThemeClear(long themePtr); + private static native int nativeThemeGetAttributeValue(long ptr, long themePtr, + @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve); + private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag, + String prefix); + static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr); + + // AssetInputStream related native methods. + private static native void nativeAssetDestroy(long assetPtr); + private static native int nativeAssetReadChar(long assetPtr); + private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len); + private static native long nativeAssetSeek(long assetPtr, long offset, int whence); + private static native long nativeAssetGetLength(long assetPtr); + private static native long nativeAssetGetRemainingLength(long assetPtr); + + private static native void nativeVerifySystemIdmaps(); + + // Global debug native methods. + /** + * @hide + */ + public static native int getGlobalAssetCount(); + + /** + * @hide + */ + public static native String getAssetAllocations(); + + /** + * @hide + */ + public static native int getGlobalAssetManagerCount(); } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index e173653cd961..8f58891ed556 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -590,7 +590,7 @@ public class Resources { */ @NonNull public int[] getIntArray(@ArrayRes int id) throws NotFoundException { - int[] res = mResourcesImpl.getAssets().getArrayIntResource(id); + int[] res = mResourcesImpl.getAssets().getResourceIntArray(id); if (res != null) { return res; } @@ -613,13 +613,13 @@ public class Resources { @NonNull public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException { final ResourcesImpl impl = mResourcesImpl; - int len = impl.getAssets().getArraySize(id); + int len = impl.getAssets().getResourceArraySize(id); if (len < 0) { throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id)); } TypedArray array = TypedArray.obtain(this, len); - array.mLength = impl.getAssets().retrieveArray(id, array.mData); + array.mLength = impl.getAssets().getResourceArray(id, array.mData); array.mIndices[0] = 0; return array; @@ -1789,8 +1789,7 @@ public class Resources { // out the attributes from the XML file (applying type information // contained in the resources and such). XmlBlock.Parser parser = (XmlBlock.Parser)set; - mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, attrs, - array.mData, array.mIndices); + mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices); array.mXml = parser; diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 3239212adf66..b6a4f3108471 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -168,7 +168,6 @@ public class ResourcesImpl { mDisplayAdjustments = displayAdjustments; mConfiguration.setToDefaults(); updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo()); - mAssets.ensureStringBlocks(); } public DisplayAdjustments getDisplayAdjustments() { @@ -1274,8 +1273,7 @@ public class ResourcesImpl { void applyStyle(int resId, boolean force) { synchronized (mKey) { - AssetManager.applyThemeStyle(mTheme, resId, force); - + mAssets.applyStyleToTheme(mTheme, resId, force); mThemeResId = resId; mKey.append(resId, force); } @@ -1284,7 +1282,7 @@ public class ResourcesImpl { void setTo(ThemeImpl other) { synchronized (mKey) { synchronized (other.mKey) { - AssetManager.copyTheme(mTheme, other.mTheme); + AssetManager.nativeThemeCopy(mTheme, other.mTheme); mThemeResId = other.mThemeResId; mKey.setTo(other.getKey()); @@ -1307,12 +1305,10 @@ public class ResourcesImpl { // out the attributes from the XML file (applying type information // contained in the resources and such). final XmlBlock.Parser parser = (XmlBlock.Parser) set; - AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes, - parser != null ? parser.mParseState : 0, - attrs, attrs.length, array.mDataAddress, array.mIndicesAddress); + mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs, + array.mDataAddress, array.mIndicesAddress); array.mTheme = wrapper; array.mXml = parser; - return array; } } @@ -1329,7 +1325,7 @@ public class ResourcesImpl { } final TypedArray array = TypedArray.obtain(wrapper.getResources(), len); - AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); + mAssets.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); array.mTheme = wrapper; array.mXml = null; return array; @@ -1349,14 +1345,14 @@ public class ResourcesImpl { @Config int getChangingConfigurations() { synchronized (mKey) { final @NativeConfig int nativeChangingConfig = - AssetManager.getThemeChangingConfigurations(mTheme); + AssetManager.nativeThemeGetChangingConfigurations(mTheme); return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig); } } public void dump(int priority, String tag, String prefix) { synchronized (mKey) { - AssetManager.dumpTheme(mTheme, priority, tag, prefix); + mAssets.dumpTheme(mTheme, priority, tag, prefix); } } @@ -1385,13 +1381,13 @@ public class ResourcesImpl { */ void rebase() { synchronized (mKey) { - AssetManager.clearTheme(mTheme); + AssetManager.nativeThemeClear(mTheme); // Reapply the same styles in the same order. for (int i = 0; i < mKey.mCount; i++) { final int resId = mKey.mResId[i]; final boolean force = mKey.mForce[i]; - AssetManager.applyThemeStyle(mTheme, resId, force); + mAssets.applyStyleToTheme(mTheme, resId, force); } } } diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index f33c75168a5f..cbb3c6df0558 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -61,6 +61,15 @@ public class TypedArray { return attrs; } + // STYLE_ prefixed constants are offsets within the typed data array. + static final int STYLE_NUM_ENTRIES = 6; + static final int STYLE_TYPE = 0; + static final int STYLE_DATA = 1; + static final int STYLE_ASSET_COOKIE = 2; + static final int STYLE_RESOURCE_ID = 3; + static final int STYLE_CHANGING_CONFIGURATIONS = 4; + static final int STYLE_DENSITY = 5; + private final Resources mResources; private DisplayMetrics mMetrics; private AssetManager mAssets; @@ -78,7 +87,7 @@ public class TypedArray { private void resize(int len) { mLength = len; - final int dataLen = len * AssetManager.STYLE_NUM_ENTRIES; + final int dataLen = len * STYLE_NUM_ENTRIES; final int indicesLen = len + 1; final VMRuntime runtime = VMRuntime.getRuntime(); if (mDataAddress == 0 || mData.length < dataLen) { @@ -166,9 +175,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -203,9 +212,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -242,14 +251,13 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_STRING) { - final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; + final int cookie = data[index + STYLE_ASSET_COOKIE]; if (cookie < 0) { - return mXml.getPooledString( - data[index+AssetManager.STYLE_DATA]).toString(); + return mXml.getPooledString(data[index + STYLE_DATA]).toString(); } } return null; @@ -274,11 +282,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); + data[index + STYLE_CHANGING_CONFIGURATIONS]); if ((changingConfigs & ~allowedChangingConfigs) != 0) { return null; } @@ -320,14 +328,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA] != 0; + return data[index + STYLE_DATA] != 0; } final TypedValue v = mValue; @@ -359,14 +367,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } final TypedValue v = mValue; @@ -396,16 +404,16 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FLOAT) { - return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]); + return Float.intBitsToFloat(data[index + STYLE_DATA]); } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } final TypedValue v = mValue; @@ -446,15 +454,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_STRING) { final TypedValue value = mValue; if (getValueAt(index, value)) { @@ -498,7 +506,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -533,7 +541,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -564,15 +572,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -612,15 +620,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimension( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -661,15 +668,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelOffset( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -711,15 +717,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index+AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -755,16 +760,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index+AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -795,15 +799,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } return defValue; @@ -833,15 +836,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FRACTION) { - return TypedValue.complexToFraction( - data[index+AssetManager.STYLE_DATA], base, pbase); + return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -874,10 +876,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) { - final int resid = data[index+AssetManager.STYLE_RESOURCE_ID]; + if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) { + final int resid = data[index + STYLE_RESOURCE_ID]; if (resid != 0) { return resid; } @@ -902,10 +904,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { - return data[index + AssetManager.STYLE_DATA]; + if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { + return data[index + STYLE_DATA]; } return defValue; } @@ -939,7 +941,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -975,7 +977,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -1006,7 +1008,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { return mResources.getTextArray(value.resourceId); } return null; @@ -1027,7 +1029,7 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue); + return getValueAt(index * STYLE_NUM_ENTRIES, outValue); } /** @@ -1043,8 +1045,8 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; - return mData[index + AssetManager.STYLE_TYPE]; + index *= STYLE_NUM_ENTRIES; + return mData[index + STYLE_TYPE]; } /** @@ -1063,9 +1065,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; return type != TypedValue.TYPE_NULL; } @@ -1084,11 +1086,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; return type != TypedValue.TYPE_NULL - || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; + || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; } /** @@ -1109,7 +1111,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { return value; } return null; @@ -1181,16 +1183,16 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * AssetManager.STYLE_NUM_ENTRIES; - if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { + final int index = i * STYLE_NUM_ENTRIES; + if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { // Not an attribute, ignore. continue; } // Null the entry so that we can safely call getZzz(). - data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL; + data[index + STYLE_TYPE] = TypedValue.TYPE_NULL; - final int attr = data[index + AssetManager.STYLE_DATA]; + final int attr = data[index + STYLE_DATA]; if (attr == 0) { // Useless data, ignore. continue; @@ -1231,45 +1233,44 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * AssetManager.STYLE_NUM_ENTRIES; - final int type = data[index + AssetManager.STYLE_TYPE]; + final int index = i * STYLE_NUM_ENTRIES; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { continue; } changingConfig |= ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); + data[index + STYLE_CHANGING_CONFIGURATIONS]); } return changingConfig; } private boolean getValueAt(int index, TypedValue outValue) { final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return false; } outValue.type = type; - outValue.data = data[index+AssetManager.STYLE_DATA]; - outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; - outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID]; + outValue.data = data[index + STYLE_DATA]; + outValue.assetCookie = data[index + STYLE_ASSET_COOKIE]; + outValue.resourceId = data[index + STYLE_RESOURCE_ID]; outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); - outValue.density = data[index+AssetManager.STYLE_DENSITY]; + data[index + STYLE_CHANGING_CONFIGURATIONS]); + outValue.density = data[index + STYLE_DENSITY]; outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null; return true; } private CharSequence loadStringValueAt(int index) { final int[] data = mData; - final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; + final int cookie = data[index + STYLE_ASSET_COOKIE]; if (cookie < 0) { if (mXml != null) { - return mXml.getPooledString( - data[index+AssetManager.STYLE_DATA]); + return mXml.getPooledString(data[index + STYLE_DATA]); } return null; } - return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]); + return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]); } /** @hide */ diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java index e6b957414ea8..d4ccffb83ca5 100644 --- a/core/java/android/content/res/XmlBlock.java +++ b/core/java/android/content/res/XmlBlock.java @@ -16,6 +16,7 @@ package android.content.res; +import android.annotation.Nullable; import android.util.TypedValue; import com.android.internal.util.XmlUtils; @@ -33,7 +34,7 @@ import java.io.Reader; * * {@hide} */ -final class XmlBlock { +final class XmlBlock implements AutoCloseable { private static final boolean DEBUG=false; public XmlBlock(byte[] data) { @@ -48,6 +49,7 @@ final class XmlBlock { mStrings = new StringBlock(nativeGetStringBlock(mNative), false); } + @Override public void close() { synchronized (this) { if (mOpen) { @@ -478,13 +480,13 @@ final class XmlBlock { * are doing! The given native object must exist for the entire lifetime * of this newly creating XmlBlock. */ - XmlBlock(AssetManager assets, long xmlBlock) { + XmlBlock(@Nullable AssetManager assets, long xmlBlock) { mAssets = assets; mNative = xmlBlock; mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false); } - private final AssetManager mAssets; + private @Nullable final AssetManager mAssets; private final long mNative; /*package*/ final StringBlock mStrings; private boolean mOpen = true; diff --git a/core/jni/Android.bp b/core/jni/Android.bp index b3f66e9652f6..c6dcb89b589d 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -111,8 +111,8 @@ cc_library_shared { "android_util_AssetManager.cpp", "android_util_Binder.cpp", "android_util_EventLog.cpp", - "android_util_MemoryIntArray.cpp", "android_util_Log.cpp", + "android_util_MemoryIntArray.cpp", "android_util_PathParser.cpp", "android_util_Process.cpp", "android_util_StringBlock.cpp", @@ -189,6 +189,7 @@ cc_library_shared { "android_backup_FileBackupHelperBase.cpp", "android_backup_BackupHelperDispatcher.cpp", "android_app_backup_FullBackup.cpp", + "android_content_res_ApkAssets.cpp", "android_content_res_ObbScanner.cpp", "android_content_res_Configuration.cpp", "android_animation_PropertyValuesHolder.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 6d7fe056acdf..a30799281d11 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -121,6 +121,7 @@ extern int register_android_util_MemoryIntArray(JNIEnv* env); extern int register_android_util_PathParser(JNIEnv* env); extern int register_android_content_StringBlock(JNIEnv* env); extern int register_android_content_XmlBlock(JNIEnv* env); +extern int register_android_content_res_ApkAssets(JNIEnv* env); extern int register_android_graphics_Canvas(JNIEnv* env); extern int register_android_graphics_CanvasProperty(JNIEnv* env); extern int register_android_graphics_ColorFilter(JNIEnv* env); @@ -1340,6 +1341,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_AssetManager), REG_JNI(register_android_content_StringBlock), REG_JNI(register_android_content_XmlBlock), + REG_JNI(register_android_content_res_ApkAssets), REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_Hyphenator), REG_JNI(register_android_text_MeasuredText), diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index dd3e6f02e9fe..c50026ea570e 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include "Utils.h" #include "FontUtils.h" @@ -224,7 +224,8 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b NPE_CHECK_RETURN_ZERO(env, jpath); NativeFamilyBuilder* builder = reinterpret_cast(builderPtr); - AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr); + + Guarded* mgr = AssetManagerForJavaObject(env, jassetMgr); if (NULL == mgr) { builder->axes.clear(); return false; @@ -236,27 +237,33 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b return false; } - Asset* asset; - if (isAsset) { - asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); - } else { - asset = cookie ? mgr->openNonAsset(static_cast(cookie), str.c_str(), - Asset::ACCESS_BUFFER) : mgr->openNonAsset(str.c_str(), Asset::ACCESS_BUFFER); + std::unique_ptr asset; + { + ScopedLock locked_mgr(*mgr); + if (isAsset) { + asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); + } else if (cookie > 0) { + // Valid java cookies are 1-based, but AssetManager cookies are 0-based. + asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast(cookie - 1), + Asset::ACCESS_BUFFER); + } else { + asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER); + } } - if (NULL == asset) { + if (nullptr == asset) { builder->axes.clear(); return false; } const void* buf = asset->getBuffer(false); if (NULL == buf) { - delete asset; builder->axes.clear(); return false; } - sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset)); + sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, + asset.release())); return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); } diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 09e37e1a3de6..49a24a30f77e 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -361,7 +361,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName code->sdkVersion = sdkVersion; code->javaAssetManager = env->NewGlobalRef(jAssetMgr); - code->assetManager = assetManagerForJavaObject(env, jAssetMgr); + code->assetManager = NdkAssetManagerForJavaObject(env, jAssetMgr); if (obbDir != NULL) { dirStr = env->GetStringUTFChars(obbDir, NULL); diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp new file mode 100644 index 000000000000..c0f151b71c93 --- /dev/null +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "android-base/macros.h" +#include "android-base/stringprintf.h" +#include "android-base/unique_fd.h" +#include "androidfw/ApkAssets.h" +#include "utils/misc.h" + +#include "core_jni_helpers.h" +#include "jni.h" +#include "nativehelper/ScopedUtfChars.h" + +using ::android::base::unique_fd; + +namespace android { + +static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboolean system, + jboolean force_shared_lib, jboolean overlay) { + ScopedUtfChars path(env, java_path); + if (path.c_str() == nullptr) { + return 0; + } + + std::unique_ptr apk_assets; + if (overlay) { + apk_assets = ApkAssets::LoadOverlay(path.c_str(), system); + } else if (force_shared_lib) { + apk_assets = ApkAssets::LoadAsSharedLibrary(path.c_str(), system); + } else { + apk_assets = ApkAssets::Load(path.c_str(), system); + } + + if (apk_assets == nullptr) { + std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str()); + jniThrowException(env, "java/io/IOException", error_msg.c_str()); + return 0; + } + return reinterpret_cast(apk_assets.release()); +} + +static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor, + jstring friendly_name, jboolean system, jboolean force_shared_lib) { + ScopedUtfChars friendly_name_utf8(env, friendly_name); + if (friendly_name_utf8.c_str() == nullptr) { + return 0; + } + + int fd = jniGetFDFromFileDescriptor(env, file_descriptor); + if (fd < 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); + return 0; + } + + unique_fd dup_fd(::dup(fd)); + if (dup_fd < 0) { + jniThrowIOException(env, errno); + return 0; + } + + std::unique_ptr apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), + friendly_name_utf8.c_str(), + system, force_shared_lib); + if (apk_assets == nullptr) { + std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d", + friendly_name_utf8.c_str(), dup_fd.get()); + jniThrowException(env, "java/io/IOException", error_msg.c_str()); + return 0; + } + return reinterpret_cast(apk_assets.release()); +} + +static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + delete reinterpret_cast(ptr); +} + +static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + return env->NewStringUTF(apk_assets->GetPath().c_str()); +} + +static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + return reinterpret_cast(apk_assets->GetLoadedArsc()->GetStringPool()); +} + +static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + (void)apk_assets; + return JNI_TRUE; +} + +static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) { + ScopedUtfChars path_utf8(env, file_name); + if (path_utf8.c_str() == nullptr) { + return 0; + } + + const ApkAssets* apk_assets = reinterpret_cast(ptr); + std::unique_ptr asset = apk_assets->Open(path_utf8.c_str(), + Asset::AccessMode::ACCESS_RANDOM); + if (asset == nullptr) { + jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str()); + return 0; + } + + // DynamicRefTable is only needed when looking up resource references. Opening an XML file + // directly from an ApkAssets has no notion of proper resource references. + std::unique_ptr xml_tree = util::make_unique(nullptr /*dynamicRefTable*/); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); + asset.reset(); + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + return reinterpret_cast(xml_tree.release()); +} + +// JNI registration. +static const JNINativeMethod gApkAssetsMethods[] = { + {"nativeLoad", "(Ljava/lang/String;ZZZ)J", (void*)NativeLoad}, + {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZ)J", + (void*)NativeLoadFromFd}, + {"nativeDestroy", "(J)V", (void*)NativeDestroy}, + {"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath}, + {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock}, + {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate}, + {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml}, +}; + +int register_android_content_res_ApkAssets(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods, + arraysize(gApkAssetsMethods)); +} + +} // namespace android diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index c6828c4f60de..557b3ab26e64 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -1,1846 +1,1438 @@ -/* //device/libs/android_runtime/android_util_AssetManager.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ +/* + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #define LOG_TAG "asset" -#include - #include #include #include -#include -#include #include #include +#include +#include #include // for AID_SYSTEM +#include "android-base/logging.h" +#include "android-base/properties.h" +#include "android-base/stringprintf.h" +#include "android_runtime/android_util_AssetManager.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_util_Binder.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" +#include "androidfw/AssetManager2.h" #include "androidfw/AttributeResolution.h" +#include "androidfw/MutexGuard.h" #include "androidfw/ResourceTypes.h" -#include "android_runtime/AndroidRuntime.h" -#include "android_util_Binder.h" #include "core_jni_helpers.h" #include "jni.h" -#include -#include -#include +#include "nativehelper/JNIHelp.h" +#include "nativehelper/ScopedPrimitiveArray.h" +#include "nativehelper/ScopedStringChars.h" +#include "nativehelper/ScopedUtfChars.h" #include "utils/Log.h" -#include "utils/misc.h" #include "utils/String8.h" +#include "utils/misc.h" extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap); extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap); +using ::android::base::StringPrintf; namespace android { -static const bool kThrowOnBadId = false; - // ---------------------------------------------------------------------------- -static struct typedvalue_offsets_t -{ - jfieldID mType; - jfieldID mData; - jfieldID mString; - jfieldID mAssetCookie; - jfieldID mResourceId; - jfieldID mChangingConfigurations; - jfieldID mDensity; +static struct typedvalue_offsets_t { + jfieldID mType; + jfieldID mData; + jfieldID mString; + jfieldID mAssetCookie; + jfieldID mResourceId; + jfieldID mChangingConfigurations; + jfieldID mDensity; } gTypedValueOffsets; -static struct assetfiledescriptor_offsets_t -{ - jfieldID mFd; - jfieldID mStartOffset; - jfieldID mLength; +static struct assetfiledescriptor_offsets_t { + jfieldID mFd; + jfieldID mStartOffset; + jfieldID mLength; } gAssetFileDescriptorOffsets; -static struct assetmanager_offsets_t -{ - jfieldID mObject; +static struct assetmanager_offsets_t { + jfieldID mObject; } gAssetManagerOffsets; -static struct sparsearray_offsets_t -{ - jclass classObject; - jmethodID constructor; - jmethodID put; +static struct { + jfieldID native_ptr; +} gApkAssetsFields; + +static struct sparsearray_offsets_t { + jclass classObject; + jmethodID constructor; + jmethodID put; } gSparseArrayOffsets; -static struct configuration_offsets_t -{ - jclass classObject; - jmethodID constructor; - jfieldID mSmallestScreenWidthDpOffset; - jfieldID mScreenWidthDpOffset; - jfieldID mScreenHeightDpOffset; +static struct configuration_offsets_t { + jclass classObject; + jmethodID constructor; + jfieldID mSmallestScreenWidthDpOffset; + jfieldID mScreenWidthDpOffset; + jfieldID mScreenHeightDpOffset; } gConfigurationOffsets; -jclass g_stringClass = NULL; +jclass g_stringClass = nullptr; // ---------------------------------------------------------------------------- -static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, - const Res_value& value, uint32_t ref, ssize_t block, - uint32_t typeSpecFlags, ResTable_config* config = NULL); - -jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, - const Res_value& value, uint32_t ref, ssize_t block, - uint32_t typeSpecFlags, ResTable_config* config) -{ - env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType); - env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie, - static_cast(table->getTableCookie(block))); - env->SetIntField(outValue, gTypedValueOffsets.mData, value.data); - env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL); - env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref); - env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations, - typeSpecFlags); - if (config != NULL) { - env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density); - } - return block; +// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. +constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { + return cookie != kInvalidCookie ? static_cast(cookie + 1) : -1; } -// This is called by zygote (running as user root) as part of preloadResources. -static void verifySystemIdmaps() -{ - pid_t pid; - char system_id[10]; - - snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM); - - switch (pid = fork()) { - case -1: - ALOGE("failed to fork for idmap: %s", strerror(errno)); - break; - case 0: // child - { - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata; - - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - - capheader.version = _LINUX_CAPABILITY_VERSION; - capheader.pid = 0; - - if (capget(&capheader, &capdata) != 0) { - ALOGE("capget: %s\n", strerror(errno)); - exit(1); - } - - capdata.effective = capdata.permitted; - if (capset(&capheader, &capdata) != 0) { - ALOGE("capset: %s\n", strerror(errno)); - exit(1); - } - - if (setgid(AID_SYSTEM) != 0) { - ALOGE("setgid: %s\n", strerror(errno)); - exit(1); - } - - if (setuid(AID_SYSTEM) != 0) { - ALOGE("setuid: %s\n", strerror(errno)); - exit(1); - } - - // Generic idmap parameters - const char* argv[8]; - int argc = 0; - struct stat st; - - memset(argv, NULL, sizeof(argv)); - argv[argc++] = AssetManager::IDMAP_BIN; - argv[argc++] = "--scan"; - argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; - argv[argc++] = AssetManager::TARGET_APK_PATH; - argv[argc++] = AssetManager::IDMAP_DIR; - - // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, - // use OVERLAY_DIR/ in addition to OVERLAY_DIR. - char subdir[PROP_VALUE_MAX]; - int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir); - if (len > 0) { - String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir; - if (stat(overlayPath.string(), &st) == 0) { - argv[argc++] = overlayPath.string(); - } - } - if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::OVERLAY_DIR; - } - - // Finally, invoke idmap (if any overlay directory exists) - if (argc > 5) { - execv(AssetManager::IDMAP_BIN, (char* const*)argv); - ALOGE("failed to execv for idmap: %s", strerror(errno)); - exit(1); // should never get here - } else { - exit(0); - } - } - break; - default: // parent - waitpid(pid, NULL, 0); - break; - } +constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) { + return cookie > 0 ? static_cast(cookie - 1) : kInvalidCookie; } -// ---------------------------------------------------------------------------- - -// this guy is exported to other jni routines -AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj) -{ - jlong amHandle = env->GetLongField(obj, gAssetManagerOffsets.mObject); - AssetManager* am = reinterpret_cast(amHandle); - if (am != NULL) { - return am; - } - jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); - return NULL; +// This is called by zygote (running as user root) as part of preloadResources. +static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { + switch (pid_t pid = fork()) { + case -1: + PLOG(ERROR) << "failed to fork for idmap"; + break; + + // child + case 0: { + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata; + + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + + capheader.version = _LINUX_CAPABILITY_VERSION; + capheader.pid = 0; + + if (capget(&capheader, &capdata) != 0) { + PLOG(ERROR) << "capget"; + exit(1); + } + + capdata.effective = capdata.permitted; + if (capset(&capheader, &capdata) != 0) { + PLOG(ERROR) << "capset"; + exit(1); + } + + if (setgid(AID_SYSTEM) != 0) { + PLOG(ERROR) << "setgid"; + exit(1); + } + + if (setuid(AID_SYSTEM) != 0) { + PLOG(ERROR) << "setuid"; + exit(1); + } + + // Generic idmap parameters + const char* argv[8]; + int argc = 0; + struct stat st; + + memset(argv, 0, sizeof(argv)); + argv[argc++] = AssetManager::IDMAP_BIN; + argv[argc++] = "--scan"; + argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; + argv[argc++] = AssetManager::TARGET_APK_PATH; + argv[argc++] = AssetManager::IDMAP_DIR; + + // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, + // use OVERLAY_DIR/ in addition to OVERLAY_DIR. + std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY, + ""); + if (!overlay_theme_path.empty()) { + overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path; + if (stat(overlay_theme_path.c_str(), &st) == 0) { + argv[argc++] = overlay_theme_path.c_str(); + } + } + + if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::OVERLAY_DIR; + } + + // Finally, invoke idmap (if any overlay directory exists) + if (argc > 5) { + execv(AssetManager::IDMAP_BIN, (char* const*)argv); + PLOG(ERROR) << "failed to execv for idmap"; + exit(1); // should never get here + } else { + exit(0); + } + } break; + + // parent + default: + waitpid(pid, nullptr, 0); + break; + } } -static jlong android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz, - jstring fileName, jint mode) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openAsset in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Empty file name"); - return -1; - } - - if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM - && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return -1; - } - - Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return -1; - } - - //printf("Created Asset Stream: %p\n", a); - - return reinterpret_cast(a); +static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref, + uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType); + env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie, + ApkAssetsCookieToJavaCookie(cookie)); + env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data); + env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr); + env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref); + env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags); + if (config != nullptr) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density); + } + return static_cast(ApkAssetsCookieToJavaCookie(cookie)); } -static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets) -{ - off64_t startOffset, length; - int fd = a->openFileDescriptor(&startOffset, &length); - delete a; - - if (fd < 0) { - jniThrowException(env, "java/io/FileNotFoundException", - "This file can not be opened as a file descriptor; it is probably compressed"); - return NULL; - } - - jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0); - if (offsets == NULL) { - close(fd); - return NULL; - } - - offsets[0] = startOffset; - offsets[1] = length; - - env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0); +// ---------------------------------------------------------------------------- - jobject fileDesc = jniCreateFileDescriptor(env, fd); - if (fileDesc == NULL) { - close(fd); - return NULL; - } +// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance. +struct GuardedAssetManager : public ::AAssetManager { + Guarded guarded_assetmanager; +}; - return newParcelFileDescriptor(env, fileDesc); +::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { + jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject); + ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle); + if (am == nullptr) { + jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); + return nullptr; + } + return am; } -static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz, - jstring fileName, jlongArray outOffsets) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ALOGV("openAssetFd in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - //printf("Created Asset Stream: %p\n", a); - - return returnParcelFileDescriptor(env, a, outOffsets); +Guarded* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) { + if (assetmanager == nullptr) { + return nullptr; + } + return &reinterpret_cast(assetmanager)->guarded_assetmanager; } -static jlong android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName, - jint mode) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return -1; - } - - if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM - && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return -1; - } - - Asset* a = cookie - ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), - (Asset::AccessMode)mode) - : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return -1; - } - - //printf("Created Asset Stream: %p\n", a); - - return reinterpret_cast(a); +Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { + return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager)); } -static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName, - jlongArray outOffsets) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ALOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - Asset* a = cookie - ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), Asset::ACCESS_RANDOM) - : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - //printf("Created Asset Stream: %p\n", a); - - return returnParcelFileDescriptor(env, a, outOffsets); +static Guarded& AssetManagerFromLong(jlong ptr) { + return *AssetManagerForNdkAssetManager(reinterpret_cast(ptr)); } -static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz, - jstring fileName) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - AssetDir* dir = am->openDir(fileName8.c_str()); - - if (dir == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - size_t N = dir->getFileCount(); - - jobjectArray array = env->NewObjectArray(dir->getFileCount(), - g_stringClass, NULL); - if (array == NULL) { - delete dir; - return NULL; - } - - for (size_t i=0; igetFileName(i); - jstring str = env->NewStringUTF(name.string()); - if (str == NULL) { - delete dir; - return NULL; - } - env->SetObjectArrayElement(array, i, str); - env->DeleteLocalRef(str); - } - - delete dir; - - return array; +static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr asset, + jlongArray out_offsets) { + off64_t start_offset, length; + int fd = asset->openFileDescriptor(&start_offset, &length); + asset.reset(); + + if (fd < 0) { + jniThrowException(env, "java/io/FileNotFoundException", + "This file can not be opened as a file descriptor; it is probably " + "compressed"); + return nullptr; + } + + jlong* offsets = reinterpret_cast(env->GetPrimitiveArrayCritical(out_offsets, 0)); + if (offsets == nullptr) { + close(fd); + return nullptr; + } + + offsets[0] = start_offset; + offsets[1] = length; + + env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0); + + jobject file_desc = jniCreateFileDescriptor(env, fd); + if (file_desc == nullptr) { + close(fd); + return nullptr; + } + return newParcelFileDescriptor(env, file_desc); } -static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - //printf("Destroying Asset Stream: %p\n", a); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return; - } - - delete a; +static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) { + return Asset::getGlobalCount(); } -static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - uint8_t b; - ssize_t res = a->read(&b, 1); - return res == 1 ? b : -1; +static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) { + String8 alloc = Asset::getAssetAllocations(); + if (alloc.length() <= 0) { + return nullptr; + } + return env->NewStringUTF(alloc.string()); } -static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz, - jlong assetHandle, jbyteArray bArray, - jint off, jint len) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL || bArray == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - if (len == 0) { - return 0; - } - - jsize bLen = env->GetArrayLength(bArray); - if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); - return -1; - } - - jbyte* b = env->GetByteArrayElements(bArray, NULL); - ssize_t res = a->read(b+off, len); - env->ReleaseByteArrayElements(bArray, b, 0); - - if (res > 0) return static_cast(res); - - if (res < 0) { - jniThrowException(env, "java/io/IOException", ""); - } - return -1; +static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) { + // TODO(adamlesinski): Switch to AssetManager2. + return AssetManager::getGlobalCount(); } -static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz, - jlong assetHandle, - jlong offset, jint whence) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - return a->seek( - offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)); +static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) { + // AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and + // AssetManager2 in a contiguous block (GuardedAssetManager). + return reinterpret_cast(new GuardedAssetManager()); } -static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - return a->getLength(); +static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + delete reinterpret_cast(ptr); } -static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; +static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jobjectArray apk_assets_array, jboolean invalidate_caches) { + const jsize apk_assets_len = env->GetArrayLength(apk_assets_array); + std::vector apk_assets; + apk_assets.reserve(apk_assets_len); + for (jsize i = 0; i < apk_assets_len; i++) { + jobject obj = env->GetObjectArrayElement(apk_assets_array, i); + if (obj == nullptr) { + std::string msg = StringPrintf("ApkAssets at index %d is null", i); + jniThrowNullPointerException(env, msg.c_str()); + return; } - return a->getRemainingLength(); -} - -static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz, - jstring path, jboolean appAsLib) -{ - ScopedUtfChars path8(env, path); - if (path8.c_str() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; + jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr); + if (env->ExceptionCheck()) { + return; } + apk_assets.push_back(reinterpret_cast(apk_assets_native_ptr)); + } - int32_t cookie; - bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib); - - return (res) ? static_cast(cookie) : 0; + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + assetmanager->SetApkAssets(apk_assets, invalidate_caches); } -static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz, - jstring idmapPath) -{ - ScopedUtfChars idmapPath8(env, idmapPath); - if (idmapPath8.c_str() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - int32_t cookie; - bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie); - - return (res) ? (jint)cookie : 0; +static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc, + jstring locale, jint orientation, jint touchscreen, jint density, + jint keyboard, jint keyboard_hidden, jint navigation, + jint screen_width, jint screen_height, + jint smallest_screen_width_dp, jint screen_width_dp, + jint screen_height_dp, jint screen_layout, jint ui_mode, + jint color_mode, jint major_version) { + ResTable_config configuration; + memset(&configuration, 0, sizeof(configuration)); + configuration.mcc = static_cast(mcc); + configuration.mnc = static_cast(mnc); + configuration.orientation = static_cast(orientation); + configuration.touchscreen = static_cast(touchscreen); + configuration.density = static_cast(density); + configuration.keyboard = static_cast(keyboard); + configuration.inputFlags = static_cast(keyboard_hidden); + configuration.navigation = static_cast(navigation); + configuration.screenWidth = static_cast(screen_width); + configuration.screenHeight = static_cast(screen_height); + configuration.smallestScreenWidthDp = static_cast(smallest_screen_width_dp); + configuration.screenWidthDp = static_cast(screen_width_dp); + configuration.screenHeightDp = static_cast(screen_height_dp); + configuration.screenLayout = static_cast(screen_layout); + configuration.uiMode = static_cast(ui_mode); + configuration.colorMode = static_cast(color_mode); + configuration.sdkVersion = static_cast(major_version); + + if (locale != nullptr) { + ScopedUtfChars locale_utf8(env, locale); + CHECK(locale_utf8.c_str() != nullptr); + configuration.setBcp47Locale(locale_utf8.c_str()); + } + + // Constants duplicated from Java class android.content.res.Configuration. + static const jint kScreenLayoutRoundMask = 0x300; + static const jint kScreenLayoutRoundShift = 8; + + // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer + // in C++. We must extract the round qualifier out of the Java screenLayout and put it + // into screenLayout2. + configuration.screenLayout2 = + static_cast((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + assetmanager->SetConfiguration(configuration); } -static jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz, - jobject fileDescriptor, jstring debugPathName, - jboolean appAsLib) -{ - ScopedUtfChars debugPathName8(env, debugPathName); +static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); - int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (fd < 0) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); - return 0; - } + jobject sparse_array = + env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor); - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } + if (sparse_array == nullptr) { + // An exception is pending. + return nullptr; + } - int dupfd = ::dup(fd); - if (dupfd < 0) { - jniThrowIOException(env, errno); - return 0; + assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) { + jstring jpackage_name = env->NewStringUTF(package_name.c_str()); + if (jpackage_name == nullptr) { + // An exception is pending. + return; } - int32_t cookie; - bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib); - - return (res) ? static_cast(cookie) : 0; -} - -static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_TRUE; - } - return am->isUpToDate() ? JNI_TRUE : JNI_FALSE; + env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast(package_id), + jpackage_name); + }); + return sparse_array; } -static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales) -{ - Vector locales; - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; +static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) { + ScopedUtfChars path_utf8(env, path); + if (path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + std::vector all_file_paths; + { + StringPiece normalized_path = path_utf8.c_str(); + if (normalized_path.data()[0] == '/') { + normalized_path = normalized_path.substr(1); + } + std::string root_path = StringPrintf("assets/%s", normalized_path.data()); + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + for (const ApkAssets* assets : assetmanager->GetApkAssets()) { + assets->ForEachFile(root_path, [&](const StringPiece& file_path, FileType type) { + if (type == FileType::kFileTypeRegular) { + all_file_paths.push_back(file_path.to_string()); + } + }); } + } - am->getLocales(&locales, includeSystemLocales); + jobjectArray array = env->NewObjectArray(all_file_paths.size(), g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } - const int N = locales.size(); + jsize index = 0; + for (const std::string& file_path : all_file_paths) { + jstring java_string = env->NewStringUTF(file_path.c_str()); - jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL); - if (result == NULL) { - return NULL; + // Check for errors creating the strings (if malformed or no memory). + if (env->ExceptionCheck()) { + return nullptr; } - for (int i=0; iNewStringUTF(locales[i].string()); - if (str == NULL) { - return NULL; - } - env->SetObjectArrayElement(result, i, str); - env->DeleteLocalRef(str); - } + env->SetObjectArrayElement(array, index++, java_string); - return result; + // If we have a large amount of string in our array, we might overflow the + // local reference table of the VM. + env->DeleteLocalRef(java_string); + } + return array; } -static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz) -{ - return getLocales(env, clazz, true /* include system locales */); +static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, + jint access_mode) { + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && + access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset = + assetmanager->Open(asset_path_utf8.c_str(), static_cast(access_mode)); + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + return reinterpret_cast(asset.release()); } -static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz) -{ - return getLocales(env, clazz, false /* don't include system locales */); +static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, + jlongArray out_offsets) { + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return nullptr; + } + return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); } -static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) { - jobject result = env->NewObject(gConfigurationOffsets.classObject, - gConfigurationOffsets.constructor); - if (result == NULL) { - return NULL; - } - - env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, - config.smallestScreenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); - - return result; +static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path, jint access_mode) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && + access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, + static_cast(access_mode)); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), + static_cast(access_mode)); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + return reinterpret_cast(asset.release()); } -static jobjectArray getSizeConfigurationsInternal(JNIEnv* env, - const Vector& configs) { - const int N = configs.size(); - jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL); - if (result == NULL) { - return NULL; - } - - for (int i=0; iDeleteLocalRef(result); - return NULL; - } - - env->SetObjectArrayElement(result, i, config); - env->DeleteLocalRef(config); - } - - return result; +static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path, jlongArray out_offsets) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return nullptr; + } + return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); } -static jobjectArray android_content_AssetManager_getSizeConfigurations(JNIEnv* env, jobject clazz) { - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - const ResTable& res(am->getResources()); - Vector configs; - res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */); - - return getSizeConfigurationsInternal(env, configs); +static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + + // May be nullptr. + const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie); + + std::unique_ptr xml_tree = util::make_unique(dynamic_ref_table); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); + asset.reset(); + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + return reinterpret_cast(xml_tree.release()); } -static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz, - jint mcc, jint mnc, - jstring locale, jint orientation, - jint touchscreen, jint density, - jint keyboard, jint keyboardHidden, - jint navigation, - jint screenWidth, jint screenHeight, - jint smallestScreenWidthDp, - jint screenWidthDp, jint screenHeightDp, - jint screenLayout, jint uiMode, - jint colorMode, jint sdkVersion) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return; - } - - ResTable_config config; - memset(&config, 0, sizeof(config)); - - const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL; - - // Constants duplicated from Java class android.content.res.Configuration. - static const jint kScreenLayoutRoundMask = 0x300; - static const jint kScreenLayoutRoundShift = 8; - - config.mcc = (uint16_t)mcc; - config.mnc = (uint16_t)mnc; - config.orientation = (uint8_t)orientation; - config.touchscreen = (uint8_t)touchscreen; - config.density = (uint16_t)density; - config.keyboard = (uint8_t)keyboard; - config.inputFlags = (uint8_t)keyboardHidden; - config.navigation = (uint8_t)navigation; - config.screenWidth = (uint16_t)screenWidth; - config.screenHeight = (uint16_t)screenHeight; - config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp; - config.screenWidthDp = (uint16_t)screenWidthDp; - config.screenHeightDp = (uint16_t)screenHeightDp; - config.screenLayout = (uint8_t)screenLayout; - config.uiMode = (uint8_t)uiMode; - config.colorMode = (uint8_t)colorMode; - config.sdkVersion = (uint16_t)sdkVersion; - config.minorVersion = 0; - - // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer - // in C++. We must extract the round qualifier out of the Java screenLayout and put it - // into screenLayout2. - config.screenLayout2 = - (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); - - am->setConfiguration(config, locale8); - - if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8); +static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jshort density, jobject typed_value, + jboolean resolve_references) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Res_value value; + ResTable_config selected_config; + uint32_t flags; + ApkAssetsCookie cookie = + assetmanager->GetResource(static_cast(resid), false /*may_be_bag*/, + static_cast(density), &value, &selected_config, &flags); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t ref = static_cast(resid); + if (resolve_references) { + cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + } + return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value); } -static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz, - jstring name, - jstring defType, - jstring defPackage) -{ - ScopedStringChars name16(env, name); - if (name16.get() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - const char16_t* defType16 = reinterpret_cast(defType) - ? reinterpret_cast(env->GetStringChars(defType, NULL)) - : NULL; - jsize defTypeLen = defType - ? env->GetStringLength(defType) : 0; - const char16_t* defPackage16 = reinterpret_cast(defPackage) - ? reinterpret_cast(env->GetStringChars(defPackage, - NULL)) - : NULL; - jsize defPackageLen = defPackage - ? env->GetStringLength(defPackage) : 0; - - jint ident = am->getResources().identifierForName( - reinterpret_cast(name16.get()), name16.size(), - defType16, defTypeLen, defPackage16, defPackageLen); - - if (defPackage16) { - env->ReleaseStringChars(defPackage, - reinterpret_cast(defPackage16)); - } - if (defType16) { - env->ReleaseStringChars(defType, - reinterpret_cast(defType16)); - } - - return ident; +static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jint bag_entry_id, jobject typed_value) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t type_spec_flags = bag->type_spec_flags; + ApkAssetsCookie cookie = kInvalidCookie; + const Res_value* bag_value = nullptr; + for (const ResolvedBag::Entry& entry : bag) { + if (entry.key == static_cast(bag_entry_id)) { + cookie = entry.cookie; + bag_value = &entry.value; + + // Keep searching (the old implementation did that). + } + } + + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + Res_value value = *bag_value; + uint32_t ref = static_cast(resid); + ResTable_config selected_config; + cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value); } -static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - String16 str; - if (name.package != NULL) { - str.setTo(name.package, name.packageLen); - } - if (name.type8 != NULL || name.type != NULL) { - if (str.size() > 0) { - char16_t div = ':'; - str.append(&div, 1); - } - if (name.type8 != NULL) { - str.append(String16(name.type8, name.typeLen)); - } else { - str.append(name.type, name.typeLen); - } - } - if (name.name8 != NULL || name.name != NULL) { - if (str.size() > 0) { - char16_t div = '/'; - str.append(&div, 1); - } - if (name.name8 != NULL) { - str.append(String16(name.name8, name.nameLen)); - } else { - str.append(name.name, name.nameLen); - } - } - - return env->NewString((const jchar*)str.string(), str.size()); +static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count); + if (env->ExceptionCheck()) { + return nullptr; + } + + for (uint32_t i = 0; i < bag->entry_count; i++) { + jint attr_resid = bag->entries[i].key; + env->SetIntArrayRegion(array, i, 1, &attr_resid); + } + return array; } -static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - if (name.package != NULL) { - return env->NewString((const jchar*)name.package, name.packageLen); - } - - return NULL; +static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } + + for (uint32_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + + // Resolve any references to their final value. + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return nullptr; + } + + if (value.dataType == Res_value::TYPE_STRING) { + const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie]; + const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool(); + + jstring java_string = nullptr; + size_t str_len; + const char* str_utf8 = pool->string8At(value.data, &str_len); + if (str_utf8 != nullptr) { + java_string = env->NewStringUTF(str_utf8); + } else { + const char16_t* str_utf16 = pool->stringAt(value.data, &str_len); + java_string = env->NewString(reinterpret_cast(str_utf16), str_len); + } + + // Check for errors creating the strings (if malformed or no memory). + if (env->ExceptionCheck()) { + return nullptr; + } + + env->SetObjectArrayElement(array, i, java_string); + + // If we have a large amount of string in our array, we might overflow the + // local reference table of the VM. + env->DeleteLocalRef(java_string); + } + } + return array; } -static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - if (name.type8 != NULL) { - return env->NewStringUTF(name.type8); - } - - if (name.type != NULL) { - return env->NewString((const jchar*)name.type, name.typeLen); - } +static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count * 2); + if (array == nullptr) { + return nullptr; + } + + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); + if (buffer == nullptr) { + return nullptr; + } + + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); + return nullptr; + } + + jint string_index = -1; + if (value.dataType == Res_value::TYPE_STRING) { + string_index = static_cast(value.data); + } + + buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie); + buffer[(i * 2) + 1] = string_index; + } + env->ReleasePrimitiveArrayCritical(array, buffer, 0); + return array; +} - return NULL; +static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count); + if (array == nullptr) { + return nullptr; + } + + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); + if (buffer == nullptr) { + return nullptr; + } + + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); + return nullptr; + } + + if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) { + buffer[i] = static_cast(value.data); + } + } + env->ReleasePrimitiveArrayCritical(array, buffer, 0); + return array; } -static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } +static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return -1; + } + return static_cast(bag->entry_count); +} - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } +static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jintArray out_data) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return -1; + } - if (name.name8 != NULL) { - return env->NewStringUTF(name.name8); - } + const jsize out_data_length = env->GetArrayLength(out_data); + if (env->ExceptionCheck()) { + return -1; + } - if (name.name != NULL) { - return env->NewString((const jchar*)name.name, name.nameLen); - } + if (static_cast(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough"); + return -1; + } - return NULL; + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(out_data, nullptr)); + if (buffer == nullptr) { + return -1; + } + + jint* cursor = buffer; + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + selected_config.density = 0; + uint32_t flags = bag->type_spec_flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT); + return -1; + } + + // Deal with the special @null value -- it turns back to TYPE_NULL. + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + } + + cursor[STYLE_TYPE] = static_cast(value.dataType); + cursor[STYLE_DATA] = static_cast(value.data); + cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + cursor[STYLE_RESOURCE_ID] = static_cast(ref); + cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast(flags); + cursor[STYLE_DENSITY] = static_cast(selected_config.density); + cursor += STYLE_NUM_ENTRIES; + } + env->ReleasePrimitiveArrayCritical(out_data, buffer, 0); + return static_cast(bag->entry_count); } -static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz, - jint ident, - jshort density, - jobject outValue, - jboolean resolve) -{ - if (outValue == NULL) { - jniThrowNullPointerException(env, "outValue"); - return 0; - } - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - Res_value value; - ResTable_config config; - uint32_t typeSpecFlags; - ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - uint32_t ref = ident; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - } - if (block >= 0) { - return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config); - } - - return static_cast(block); +static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name, + jstring def_type, jstring def_package) { + ScopedUtfChars name_utf8(env, name); + if (name_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + std::string type; + if (def_type != nullptr) { + ScopedUtfChars type_utf8(env, def_type); + CHECK(type_utf8.c_str() != nullptr); + type = type_utf8.c_str(); + } + + std::string package; + if (def_package != nullptr) { + ScopedUtfChars package_utf8(env, def_package); + CHECK(package_utf8.c_str() != nullptr); + package = package_utf8.c_str(); + } + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + return static_cast(assetmanager->GetResourceId(name_utf8.c_str(), type, package)); } -static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz, - jint ident, jint bagEntryId, - jobject outValue, jboolean resolve) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); +static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } - // Now lock down the resource object and start pulling stuff from it. - res.lock(); + std::string result; + if (name.package != nullptr) { + result.append(name.package, name.package_len); + } - ssize_t block = -1; - Res_value value; - - const ResTable::bag_entry* entry = NULL; - uint32_t typeSpecFlags; - ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags); - - for (ssize_t i=0; imap.name.ident) { - block = entry->stringBlock; - value = entry->map.value; - } - entry++; + if (name.type != nullptr || name.type16 != nullptr) { + if (!result.empty()) { + result += ":"; } - res.unlock(); - - if (block < 0) { - return static_cast(block); + if (name.type != nullptr) { + result.append(name.type, name.type_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len)); } + } - uint32_t ref = ident; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } + if (name.entry != nullptr || name.entry16 != nullptr) { + if (!result.empty()) { + result += "/"; } - if (block >= 0) { - return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags); - } - - return static_cast(block); -} -static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; + if (name.entry != nullptr) { + result.append(name.entry, name.entry_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); } - return am->getResources().getTableCount(); + } + return env->NewStringUTF(result.c_str()); } -static jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz, - jint block) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - return reinterpret_cast(am->getResources().getTableStringBlock(block)); +static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.package != nullptr) { + return env->NewStringUTF(name.package); + } + return nullptr; } -static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz, - jint cookie) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - String8 name(am->getAssetPath(static_cast(cookie))); - if (name.length() == 0) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name"); - return NULL; - } - jstring str = env->NewStringUTF(name.string()); - return str; +static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.type != nullptr) { + return env->NewStringUTF(name.type); + } else if (name.type16 != nullptr) { + return env->NewString(reinterpret_cast(name.type16), name.type_len); + } + return nullptr; } -static jobject android_content_AssetManager_getAssignedPackageIdentifiers(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - const ResTable& res = am->getResources(); - - jobject sparseArray = env->NewObject(gSparseArrayOffsets.classObject, - gSparseArrayOffsets.constructor); - const size_t N = res.getBasePackageCount(); - for (size_t i = 0; i < N; i++) { - const String16 name = res.getBasePackageName(i); - env->CallVoidMethod( - sparseArray, gSparseArrayOffsets.put, - static_cast(res.getBasePackageId(i)), - env->NewString(reinterpret_cast(name.string()), - name.size())); - } - return sparseArray; +static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.entry != nullptr) { + return env->NewStringUTF(name.entry); + } else if (name.entry16 != nullptr) { + return env->NewString(reinterpret_cast(name.entry16), name.entry_len); + } + return nullptr; } -static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - return reinterpret_cast(new ResTable::Theme(am->getResources())); +static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr, + jboolean exclude_system) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::set locales = + assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/); + + jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } + + size_t idx = 0; + for (const std::string& locale : locales) { + jstring java_string = env->NewStringUTF(locale.c_str()); + if (java_string == nullptr) { + return nullptr; + } + env->SetObjectArrayElement(array, idx++, java_string); + env->DeleteLocalRef(java_string); + } + return array; } -static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz, - jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - delete theme; +static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) { + jobject result = + env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor); + if (result == nullptr) { + return nullptr; + } + + env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, + config.smallestScreenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); + return result; } -static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz, - jlong themeHandle, - jint styleRes, - jboolean force) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - theme->applyStyle(styleRes, force ? true : false); -} +static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::set configurations = + assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/); -static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz, - jlong destHandle, jlong srcHandle) -{ - ResTable::Theme* dest = reinterpret_cast(destHandle); - ResTable::Theme* src = reinterpret_cast(srcHandle); - dest->setTo(*src); -} - -static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - theme->clear(); -} + jobjectArray array = + env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr); + if (array == nullptr) { + return nullptr; + } -static jint android_content_AssetManager_loadThemeAttributeValue( - JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - const ResTable& res(theme->getResTable()); - - Res_value value; - // XXX value could be different in different configs! - uint32_t typeSpecFlags = 0; - ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags); - uint32_t ref = 0; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } + size_t idx = 0; + for (const ResTable_config& configuration : configurations) { + jobject java_configuration = ConstructConfigurationObject(env, configuration); + if (java_configuration == nullptr) { + return nullptr; } - return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block; -} -static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz, - jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - return theme->getChangingConfigurations(); + env->SetObjectArrayElement(array, idx++, java_configuration); + env->DeleteLocalRef(java_configuration); + } + return array; } -static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz, - jlong themeHandle, jint pri, - jstring tag, jstring prefix) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - const ResTable& res(theme->getResTable()); - (void)res; - - // XXX Need to use params. - theme->dumpToLog(); +static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr, + jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); + uint32_t* out_values = reinterpret_cast(out_values_ptr); + uint32_t* out_indices = reinterpret_cast(out_indices_ptr); + + jsize attrs_len = env->GetArrayLength(java_attrs); + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return; + } + + ApplyStyle(theme, xml_parser, static_cast(def_style_attr), + static_cast(def_style_resid), reinterpret_cast(attrs), attrs_len, + out_values, out_indices); + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); } -static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz, - jlong themeToken, - jint defStyleAttr, - jint defStyleRes, - jintArray inValues, - jintArray attrs, - jintArray outValues, - jintArray outIndices) -{ - if (themeToken == 0) { - jniThrowNullPointerException(env, "theme token"); - return JNI_FALSE; - } - if (attrs == NULL) { - jniThrowNullPointerException(env, "attrs"); - return JNI_FALSE; - } - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - const jsize NI = env->GetArrayLength(attrs); - const jsize NV = env->GetArrayLength(outValues); - if (NV < (NI*STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); - return JNI_FALSE; - } - - jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); - if (src == NULL) { +static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint def_style_attr, jint def_style_resid, jintArray java_values, + jintArray java_attrs, jintArray out_java_values, + jintArray out_java_indices) { + const jsize attrs_len = env->GetArrayLength(java_attrs); + const jsize out_values_len = env->GetArrayLength(out_java_values); + if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); + return JNI_FALSE; + } + + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return JNI_FALSE; + } + + jint* values = nullptr; + jsize values_len = 0; + if (java_values != nullptr) { + values_len = env->GetArrayLength(java_values); + values = reinterpret_cast(env->GetPrimitiveArrayCritical(java_values, nullptr)); + if (values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return JNI_FALSE; + } + } + + jint* out_values = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); + if (out_values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + return JNI_FALSE; + } + + jint* out_indices = nullptr; + if (out_java_indices != nullptr) { + jsize out_indices_len = env->GetArrayLength(out_java_indices); + if (out_indices_len > attrs_len) { + out_indices = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); + if (out_indices == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); return JNI_FALSE; - } - - jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0); - const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues); + } + } + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + bool result = ResolveAttrs( + theme, static_cast(def_style_attr), static_cast(def_style_resid), + reinterpret_cast(values), values_len, reinterpret_cast(attrs), + attrs_len, reinterpret_cast(out_values), reinterpret_cast(out_indices)); + if (out_indices != nullptr) { + env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); + } + + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return result ? JNI_TRUE : JNI_FALSE; +} - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - if (baseDest == NULL) { - env->ReleasePrimitiveArrayCritical(attrs, src, 0); +static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jlong xml_parser_ptr, jintArray java_attrs, + jintArray out_java_values, jintArray out_java_indices) { + const jsize attrs_len = env->GetArrayLength(java_attrs); + const jsize out_values_len = env->GetArrayLength(out_java_values); + if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); + return JNI_FALSE; + } + + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return JNI_FALSE; + } + + jint* out_values = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); + if (out_values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return JNI_FALSE; + } + + jint* out_indices = nullptr; + if (out_java_indices != nullptr) { + jsize out_indices_len = env->GetArrayLength(out_java_indices); + if (out_indices_len > attrs_len) { + out_indices = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); + if (out_indices == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); return JNI_FALSE; + } } + } - jint* indices = NULL; - if (outIndices != NULL) { - if (env->GetArrayLength(outIndices) > NI) { - indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); - } - } + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); - ResTable::Theme* theme = reinterpret_cast(themeToken); - bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes, - (uint32_t*) srcValues, NSV, - (uint32_t*) src, NI, - (uint32_t*) baseDest, - (uint32_t*) indices); + bool result = RetrieveAttributes(assetmanager.get(), xml_parser, + reinterpret_cast(attrs), attrs_len, + reinterpret_cast(out_values), + reinterpret_cast(out_indices)); - if (indices != NULL) { - env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); - } - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0); - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return result ? JNI_TRUE : JNI_FALSE; + if (out_indices != nullptr) { + env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); + } + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return result ? JNI_TRUE : JNI_FALSE; } -static void android_content_AssetManager_applyStyle(JNIEnv* env, jobject, jlong themeToken, - jint defStyleAttr, jint defStyleRes, jlong xmlParserToken, jintArray attrsObj, jint length, - jlong outValuesAddress, jlong outIndicesAddress) { - jint* attrs = env->GetIntArrayElements(attrsObj, 0); - ResTable::Theme* theme = reinterpret_cast(themeToken); - ResXMLParser* xmlParser = reinterpret_cast(xmlParserToken); - uint32_t* outValues = reinterpret_cast(static_cast(outValuesAddress)); - uint32_t* outIndices = reinterpret_cast(static_cast(outIndicesAddress)); - ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes, - reinterpret_cast(attrs), length, outValues, outIndices); - env->ReleaseIntArrayElements(attrsObj, attrs, JNI_ABORT); +static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + return reinterpret_cast(assetmanager->NewTheme().release()); } -static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz, - jlong xmlParserToken, - jintArray attrs, - jintArray outValues, - jintArray outIndices) -{ - if (xmlParserToken == 0) { - jniThrowNullPointerException(env, "xmlParserToken"); - return JNI_FALSE; - } - if (attrs == NULL) { - jniThrowNullPointerException(env, "attrs"); - return JNI_FALSE; - } - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_FALSE; - } - const ResTable& res(am->getResources()); - ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken; - - const jsize NI = env->GetArrayLength(attrs); - const jsize NV = env->GetArrayLength(outValues); - if (NV < (NI*STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); - return JNI_FALSE; - } - - jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); - if (src == NULL) { - return JNI_FALSE; - } - - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - if (baseDest == NULL) { - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return JNI_FALSE; - } - - jint* indices = NULL; - if (outIndices != NULL) { - if (env->GetArrayLength(outIndices) > NI) { - indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); - } - } - - bool result = RetrieveAttributes(&res, xmlParser, - (uint32_t*) src, NI, - (uint32_t*) baseDest, - (uint32_t*) indices); - - if (indices != NULL) { - env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); - } - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return result ? JNI_TRUE : JNI_FALSE; +static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { + delete reinterpret_cast(theme_ptr); } -static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz, - jint id) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - res.lock(); - const ResTable::bag_entry* defStyleEnt = NULL; - ssize_t bagOff = res.getBagLocked(id, &defStyleEnt); - res.unlock(); - - return static_cast(bagOff); +static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint resid, jboolean force) { + // AssetManager is accessed via the theme, so grab an explicit lock here. + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + theme->ApplyStyle(static_cast(resid), force); + + // TODO(adamlesinski): Consider surfacing exception when result is failure. + // CTS currently expects no exceptions from this method. + // std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid); + // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str()); } -static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz, - jint id, - jintArray outValues) -{ - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_FALSE; - } - const ResTable& res(am->getResources()); - ResTable_config config; - Res_value value; - ssize_t block; - - const jsize NV = env->GetArrayLength(outValues); - - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - jint* dest = baseDest; - if (dest == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", ""); - return JNI_FALSE; - } - - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - - const ResTable::bag_entry* arrayEnt = NULL; - uint32_t arrayTypeSetFlags = 0; - ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags); - const ResTable::bag_entry* endArrayEnt = arrayEnt + - (bagOff >= 0 ? bagOff : 0); - - int i = 0; - uint32_t typeSetFlags; - while (i < NV && arrayEnt < endArrayEnt) { - block = arrayEnt->stringBlock; - typeSetFlags = arrayTypeSetFlags; - config.density = 0; - value = arrayEnt->map.value; - - uint32_t resid = 0; - if (value.dataType != Res_value::TYPE_NULL) { - // Take care of resolving the found resource to its final value. - //printf("Resolving attribute reference\n"); - ssize_t newBlock = res.resolveReference(&value, block, &resid, - &typeSetFlags, &config); - if (kThrowOnBadId) { - if (newBlock == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return JNI_FALSE; - } - } - if (newBlock >= 0) block = newBlock; - } - - // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; - } - - //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data); - - // Write the final value back to Java. - dest[STYLE_TYPE] = value.dataType; - dest[STYLE_DATA] = value.data; - dest[STYLE_ASSET_COOKIE] = reinterpret_cast(res.getTableCookie(block)); - dest[STYLE_RESOURCE_ID] = resid; - dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags; - dest[STYLE_DENSITY] = config.density; - dest += STYLE_NUM_ENTRIES; - i+= STYLE_NUM_ENTRIES; - arrayEnt++; - } - - i /= STYLE_NUM_ENTRIES; - - res.unlock(); - - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - - return i; +static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr, + jlong src_theme_ptr) { + Theme* dst_theme = reinterpret_cast(dst_theme_ptr); + Theme* src_theme = reinterpret_cast(src_theme_ptr); + if (!dst_theme->SetTo(*src_theme)) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "Themes are from different AssetManagers"); + } } -static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return 0; - } - - int32_t assetCookie = static_cast(cookie); - Asset* a = assetCookie - ? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER) - : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return 0; - } - - const DynamicRefTable* dynamicRefTable = - am->getResources().getDynamicRefTableForCookie(assetCookie); - ResXMLTree* block = new ResXMLTree(dynamicRefTable); - status_t err = block->setTo(a->getBuffer(true), a->getLength(), true); - a->close(); - delete a; - - if (err != NO_ERROR) { - jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); - return 0; - } - - return reinterpret_cast(block); +static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { + reinterpret_cast(theme_ptr)->Clear(); } -static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N * 2); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - for (size_t i = 0, j = 0; ((ssize_t)i)map.value; - - // Take care of resolving the found resource to its final value. - stringBlock = res.resolveReference(&value, bag->stringBlock, NULL); - if (value.dataType == Res_value::TYPE_STRING) { - stringIndex = value.data; - } - - if (kThrowOnBadId) { - if (stringBlock == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - - //todo: It might be faster to allocate a C array to contain - // the blocknums and indices, put them in there and then - // do just one SetIntArrayRegion() - env->SetIntArrayRegion(array, j, 1, &stringBlock); - env->SetIntArrayRegion(array, j + 1, 1, &stringIndex); - j = j + 2; - } - res.unlockBag(startOfBag); - return array; +static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint resid, jobject typed_value, + jboolean resolve_references) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie = theme->GetAttribute(static_cast(resid), &value, &flags); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t ref = 0u; + if (resolve_references) { + ResTable_config selected_config; + cookie = + theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + } + return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value); } -static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL); - if (env->ExceptionCheck()) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - size_t strLen = 0; - for (size_t i=0; ((ssize_t)i)map.value; - jstring str = NULL; - - // Take care of resolving the found resource to its final value. - ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - if (value.dataType == Res_value::TYPE_STRING) { - const ResStringPool* pool = res.getTableStringBlock(block); - const char* str8 = pool->string8At(value.data, &strLen); - if (str8 != NULL) { - str = env->NewStringUTF(str8); - } else { - const char16_t* str16 = pool->stringAt(value.data, &strLen); - str = env->NewString(reinterpret_cast(str16), - strLen); - } - - // If one of our NewString{UTF} calls failed due to memory, an - // exception will be pending. - if (env->ExceptionCheck()) { - res.unlockBag(startOfBag); - return NULL; - } - - env->SetObjectArrayElement(array, i, str); - - // str is not NULL at that point, otherwise ExceptionCheck would have been true. - // If we have a large amount of strings in our array, we might - // overflow the local reference table of the VM. - env->DeleteLocalRef(str); - } - } - res.unlockBag(startOfBag); - return array; +static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint priority, jstring tag, jstring prefix) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + (void) theme; + (void) priority; + (void) tag; + (void) prefix; } -static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - for (size_t i=0; ((ssize_t)i)map.value; - - // Take care of resolving the found resource to its final value. - ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - if (value.dataType >= Res_value::TYPE_FIRST_INT - && value.dataType <= Res_value::TYPE_LAST_INT) { - int intVal = value.data; - env->SetIntArrayRegion(array, i, 1, &intVal); - } - } - res.unlockBag(startOfBag); - return array; +static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/, + jlong theme_ptr) { + Theme* theme = reinterpret_cast(theme_ptr); + return static_cast(theme->GetChangingConfigurations()); } -static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz, - jint styleId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(styleId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } +static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + delete reinterpret_cast(asset_ptr); +} - const ResTable::bag_entry* bag = startOfBag; - for (size_t i=0; ((ssize_t)i)map.name.ident; - env->SetIntArrayRegion(array, i, 1, &resourceId); - } - res.unlockBag(startOfBag); - return array; +static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + uint8_t b; + ssize_t res = asset->read(&b, sizeof(b)); + return res == sizeof(b) ? static_cast(b) : -1; } -static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem) -{ - if (isSystem) { - verifySystemIdmaps(); - } - AssetManager* am = new AssetManager(); - if (am == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", ""); - return; - } +static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer, + jint offset, jint len) { + if (len == 0) { + return 0; + } - am->addDefaultAssets(); + jsize buffer_len = env->GetArrayLength(java_buffer); + if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len || + (offset + len) > buffer_len) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); + return -1; + } - ALOGV("Created AssetManager %p for Java object %p\n", am, clazz); - env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast(am)); -} + Asset* asset = reinterpret_cast(asset_ptr); -static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz) -{ - AssetManager* am = (AssetManager*) - (env->GetLongField(clazz, gAssetManagerOffsets.mObject)); - ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz); - if (am != NULL) { - delete am; - env->SetLongField(clazz, gAssetManagerOffsets.mObject, 0); - } -} + ScopedByteArrayRW byte_array(env, java_buffer); + if (byte_array.get() == nullptr) { + return -1; + } -static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz) -{ - return Asset::getGlobalCount(); + ssize_t res = asset->read(byte_array.get(), buffer_len); + if (res < 0) { + jniThrowException(env, "java/io/IOException", ""); + return -1; + } + return res > 0 ? static_cast(res) : -1; } -static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz) -{ - String8 alloc = Asset::getAssetAllocations(); - if (alloc.length() <= 0) { - return NULL; - } +static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset, + jint whence) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->seek( + static_cast(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)))); +} - jstring str = env->NewStringUTF(alloc.string()); - return str; +static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->getLength()); } -static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz) -{ - return AssetManager::getGlobalCount(); +static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->getRemainingLength()); } // ---------------------------------------------------------------------------- -/* - * JNI registration. - */ +// JNI registration. static const JNINativeMethod gAssetManagerMethods[] = { - /* name, signature, funcPtr */ - - // Basic asset stuff. - { "openAsset", "(Ljava/lang/String;I)J", - (void*) android_content_AssetManager_openAsset }, - { "openAssetFd", "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*) android_content_AssetManager_openAssetFd }, - { "openNonAssetNative", "(ILjava/lang/String;I)J", - (void*) android_content_AssetManager_openNonAssetNative }, - { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*) android_content_AssetManager_openNonAssetFdNative }, - { "list", "(Ljava/lang/String;)[Ljava/lang/String;", - (void*) android_content_AssetManager_list }, - { "destroyAsset", "(J)V", - (void*) android_content_AssetManager_destroyAsset }, - { "readAssetChar", "(J)I", - (void*) android_content_AssetManager_readAssetChar }, - { "readAsset", "(J[BII)I", - (void*) android_content_AssetManager_readAsset }, - { "seekAsset", "(JJI)J", - (void*) android_content_AssetManager_seekAsset }, - { "getAssetLength", "(J)J", - (void*) android_content_AssetManager_getAssetLength }, - { "getAssetRemainingLength", "(J)J", - (void*) android_content_AssetManager_getAssetRemainingLength }, - { "addAssetPathNative", "(Ljava/lang/String;Z)I", - (void*) android_content_AssetManager_addAssetPath }, - { "addAssetFdNative", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)I", - (void*) android_content_AssetManager_addAssetFd }, - { "addOverlayPathNative", "(Ljava/lang/String;)I", - (void*) android_content_AssetManager_addOverlayPath }, - { "isUpToDate", "()Z", - (void*) android_content_AssetManager_isUpToDate }, - - // Resources. - { "getLocales", "()[Ljava/lang/String;", - (void*) android_content_AssetManager_getLocales }, - { "getNonSystemLocales", "()[Ljava/lang/String;", - (void*) android_content_AssetManager_getNonSystemLocales }, - { "getSizeConfigurations", "()[Landroid/content/res/Configuration;", - (void*) android_content_AssetManager_getSizeConfigurations }, - { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIIII)V", - (void*) android_content_AssetManager_setConfiguration }, - { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", - (void*) android_content_AssetManager_getResourceIdentifier }, - { "getResourceName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceName }, - { "getResourcePackageName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourcePackageName }, - { "getResourceTypeName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceTypeName }, - { "getResourceEntryName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceEntryName }, - { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadResourceValue }, - { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadResourceBagValue }, - { "getStringBlockCount","()I", - (void*) android_content_AssetManager_getStringBlockCount }, - { "getNativeStringBlock","(I)J", - (void*) android_content_AssetManager_getNativeStringBlock }, - { "getCookieName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getCookieName }, - { "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;", - (void*) android_content_AssetManager_getAssignedPackageIdentifiers }, - - // Themes. - { "newTheme", "()J", - (void*) android_content_AssetManager_newTheme }, - { "deleteTheme", "(J)V", - (void*) android_content_AssetManager_deleteTheme }, - { "applyThemeStyle", "(JIZ)V", - (void*) android_content_AssetManager_applyThemeStyle }, - { "copyTheme", "(JJ)V", - (void*) android_content_AssetManager_copyTheme }, - { "clearTheme", "(J)V", - (void*) android_content_AssetManager_clearTheme }, - { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadThemeAttributeValue }, - { "getThemeChangingConfigurations", "(J)I", - (void*) android_content_AssetManager_getThemeChangingConfigurations }, - { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V", - (void*) android_content_AssetManager_dumpTheme }, - { "applyStyle","(JIIJ[IIJJ)V", - (void*) android_content_AssetManager_applyStyle }, - { "resolveAttrs","(JII[I[I[I[I)Z", - (void*) android_content_AssetManager_resolveAttrs }, - { "retrieveAttributes","(J[I[I[I)Z", - (void*) android_content_AssetManager_retrieveAttributes }, - { "getArraySize","(I)I", - (void*) android_content_AssetManager_getArraySize }, - { "retrieveArray","(I[I)I", - (void*) android_content_AssetManager_retrieveArray }, - - // XML files. - { "openXmlAssetNative", "(ILjava/lang/String;)J", - (void*) android_content_AssetManager_openXmlAssetNative }, - - // Arrays. - { "getArrayStringResource","(I)[Ljava/lang/String;", - (void*) android_content_AssetManager_getArrayStringResource }, - { "getArrayStringInfo","(I)[I", - (void*) android_content_AssetManager_getArrayStringInfo }, - { "getArrayIntResource","(I)[I", - (void*) android_content_AssetManager_getArrayIntResource }, - { "getStyleAttributes","(I)[I", - (void*) android_content_AssetManager_getStyleAttributes }, - - // Bookkeeping. - { "init", "(Z)V", - (void*) android_content_AssetManager_init }, - { "destroy", "()V", - (void*) android_content_AssetManager_destroy }, - { "getGlobalAssetCount", "()I", - (void*) android_content_AssetManager_getGlobalAssetCount }, - { "getAssetAllocations", "()Ljava/lang/String;", - (void*) android_content_AssetManager_getAssetAllocations }, - { "getGlobalAssetManagerCount", "()I", - (void*) android_content_AssetManager_getGlobalAssetManagerCount }, + // AssetManager setup methods. + {"nativeCreate", "()J", (void*)NativeCreate}, + {"nativeDestroy", "(J)V", (void*)NativeDestroy}, + {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets}, + {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V", + (void*)NativeSetConfiguration}, + {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;", + (void*)NativeGetAssignedPackageIdentifiers}, + + // AssetManager file methods. + {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList}, + {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset}, + {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*)NativeOpenAssetFd}, + {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset}, + {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*)NativeOpenNonAssetFd}, + {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset}, + + // AssetManager resource methods. + {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue}, + {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I", + (void*)NativeGetResourceBagValue}, + {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes}, + {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;", + (void*)NativeGetResourceStringArray}, + {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo}, + {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray}, + {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize}, + {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray}, + + // AssetManager resource name/ID methods. + {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + (void*)NativeGetResourceIdentifier}, + {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName}, + {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName}, + {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName}, + {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName}, + {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales}, + {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;", + (void*)NativeGetSizeConfigurations}, + + // Style attribute related methods. + {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle}, + {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs}, + {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes}, + + // Theme related methods. + {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate}, + {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy}, + {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle}, + {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy}, + {"nativeThemeClear", "(J)V", (void*)NativeThemeClear}, + {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I", + (void*)NativeThemeGetAttributeValue}, + {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump}, + {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations}, + + // AssetInputStream methods. + {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy}, + {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar}, + {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead}, + {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek}, + {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength}, + {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength}, + + // System/idmap related methods. + {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps}, + + // Global management/debug methods. + {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount}, + {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations}, + {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount}, }; -int register_android_content_AssetManager(JNIEnv* env) -{ - jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); - gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); - gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); - gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string", - "Ljava/lang/CharSequence;"); - gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); - gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); - gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue, - "changingConfigurations", "I"); - gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); - - jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); - gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd", - "Landroid/os/ParcelFileDescriptor;"); - gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); - gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); - - jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); - gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); - - jclass stringClass = FindClassOrDie(env, "java/lang/String"); - g_stringClass = MakeGlobalRefOrDie(env, stringClass); - - jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); - gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); - gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, - "", "()V"); - gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", - "(ILjava/lang/Object;)V"); - - jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); - gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); - gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, - "", "()V"); - gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, - "smallestScreenWidthDp", "I"); - gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, - "screenWidthDp", "I"); - gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass, - "screenHeightDp", "I"); - - return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, - NELEM(gAssetManagerMethods)); +int register_android_content_AssetManager(JNIEnv* env) { + jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets"); + gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J"); + + jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); + gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); + gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); + gTypedValueOffsets.mString = + GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;"); + gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); + gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); + gTypedValueOffsets.mChangingConfigurations = + GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I"); + gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); + + jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); + gAssetFileDescriptorOffsets.mFd = + GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;"); + gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); + gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); + + jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); + gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); + + jclass stringClass = FindClassOrDie(env, "java/lang/String"); + g_stringClass = MakeGlobalRefOrDie(env, stringClass); + + jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); + gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); + gSparseArrayOffsets.constructor = + GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "", "()V"); + gSparseArrayOffsets.put = + GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V"); + + jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); + gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); + gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "", "()V"); + gConfigurationOffsets.mSmallestScreenWidthDpOffset = + GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I"); + gConfigurationOffsets.mScreenWidthDpOffset = + GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I"); + gConfigurationOffsets.mScreenHeightDpOffset = + GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I"); + + return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, + NELEM(gAssetManagerMethods)); } }; // namespace android diff --git a/core/jni/include/android_runtime/android_util_AssetManager.h b/core/jni/include/android_runtime/android_util_AssetManager.h index 8dd933707a6a..2c1e3579eb92 100644 --- a/core/jni/include/android_runtime/android_util_AssetManager.h +++ b/core/jni/include/android_runtime/android_util_AssetManager.h @@ -14,17 +14,20 @@ * limitations under the License. */ -#ifndef android_util_AssetManager_H -#define android_util_AssetManager_H +#ifndef ANDROID_RUNTIME_ASSETMANAGER_H +#define ANDROID_RUNTIME_ASSETMANAGER_H -#include +#include "androidfw/AssetManager2.h" +#include "androidfw/MutexGuard.h" #include "jni.h" namespace android { -extern AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject assetMgr); +extern AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); +extern Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); +extern Guarded* AssetManagerForNdkAssetManager(AAssetManager* assetmanager); -} +} // namespace android -#endif +#endif // ANDROID_RUNTIME_ASSETMANAGER_H diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 415d3e36adf9..2fc8e952707b 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -265,8 +265,6 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, FindEntryResult* out_entry) { - ATRACE_CALL(); - // Might use this if density_override != 0. ResTable_config density_override_config; @@ -429,9 +427,7 @@ ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_valu for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && in_out_value->data != 0u && iteration < kMaxIterations; iteration++) { - if (out_last_reference != nullptr) { - *out_last_reference = in_out_value->data; - } + *out_last_reference = in_out_value->data; uint32_t new_flags = 0u; cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/, in_out_value, in_out_selected_config, &new_flags); diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index 60e3845d98a9..f912af4f7190 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -20,13 +20,18 @@ #include +#include "androidfw/AssetManager2.h" #include "androidfw/AttributeFinder.h" -#include "androidfw/ResourceTypes.h" constexpr bool kDebugStyles = false; namespace android { +// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. +static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { + return cookie != kInvalidCookie ? static_cast(cookie + 1) : static_cast(-1); +} + class XmlAttributeFinder : public BackTrackingAttributeFinder { public: @@ -44,58 +49,53 @@ class XmlAttributeFinder }; class BagAttributeFinder - : public BackTrackingAttributeFinder { + : public BackTrackingAttributeFinder { public: - BagAttributeFinder(const ResTable::bag_entry* start, - const ResTable::bag_entry* end) - : BackTrackingAttributeFinder(start, end) {} + BagAttributeFinder(const ResolvedBag* bag) + : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr, + bag != nullptr ? bag->entries + bag->entry_count : nullptr) { + } - inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const { - return entry->map.name.ident; + inline uint32_t GetAttribute(const ResolvedBag::Entry* entry) const { + return entry->key; } }; -bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, - uint32_t def_style_res, uint32_t* src_values, - size_t src_values_length, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, - uint32_t* out_indices) { +bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, + uint32_t* src_values, size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr, def_style_res); } - const ResTable& res = theme->getResTable(); + AssetManager2* assetmanager = theme->GetAssetManager(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_bag_type_set_flags = 0; + uint32_t def_style_flags = 0u; if (def_style_attr != 0) { Res_value value; - if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) { + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { if (value.dataType == Res_value::TYPE_REFERENCE) { def_style_res = value.data; } } } - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - // Retrieve the default style bag, if requested. - const ResTable::bag_entry* def_style_start = nullptr; - uint32_t def_style_type_set_flags = 0; - ssize_t bag_off = def_style_res != 0 - ? res.getBagLocked(def_style_res, &def_style_start, - &def_style_type_set_flags) - : -1; - def_style_type_set_flags |= def_style_bag_type_set_flags; - const ResTable::bag_entry* const def_style_end = - def_style_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end); + const ResolvedBag* default_style_bag = nullptr; + if (def_style_res != 0) { + default_style_bag = assetmanager->GetBag(def_style_res); + if (default_style_bag != nullptr) { + def_style_flags |= default_style_bag->type_spec_flags; + } + } + + BagAttributeFinder def_style_attr_finder(default_style_bag); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -106,7 +106,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ssize_t block = -1; + ApkAssetsCookie cookie = kInvalidCookie; uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; @@ -122,15 +122,14 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, value.dataType = Res_value::TYPE_ATTRIBUTE; value.data = src_values[ii]; if (kDebugStyles) { - ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, - value.data); + ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data); } } else { - const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident); - if (def_style_entry != def_style_end) { - block = def_style_entry->stringBlock; - type_set_flags = def_style_type_set_flags; - value = def_style_entry->map.value; + const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident); + if (entry != def_style_attr_finder.end()) { + cookie = entry->cookie; + type_set_flags = def_style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -140,22 +139,26 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ssize_t new_block = - theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); - if (new_block >= 0) block = new_block; + ApkAssetsCookie new_cookie = + theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } if (kDebugStyles) { ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data); } } else if (value.data != Res_value::DATA_NULL_EMPTY) { - // If we still don't have a value for this attribute, try to find - // it in the theme! - ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); - if (new_block >= 0) { + // If we still don't have a value for this attribute, try to find it in the theme! + ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); + if (new_cookie != kInvalidCookie) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); - if (new_block >= 0) block = new_block; + new_cookie = + assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } if (kDebugStyles) { ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -169,7 +172,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = -1; + cookie = kInvalidCookie; } if (kDebugStyles) { @@ -179,9 +182,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != -1 ? static_cast(res.getTableCookie(block)) - : static_cast(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -195,90 +196,80 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, out_values += STYLE_NUM_ENTRIES; } - res.unlock(); - if (out_indices != nullptr) { out_indices[0] = indices_idx; } return true; } -void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { - ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", - theme, def_style_attr, def_style_res, xml_parser); + ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, + def_style_attr, def_style_resid, xml_parser); } - const ResTable& res = theme->getResTable(); + AssetManager2* assetmanager = theme->GetAssetManager(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_bag_type_set_flags = 0; + uint32_t def_style_flags = 0u; if (def_style_attr != 0) { Res_value value; - if (theme->getAttribute(def_style_attr, &value, - &def_style_bag_type_set_flags) >= 0) { + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { if (value.dataType == Res_value::TYPE_REFERENCE) { - def_style_res = value.data; + def_style_resid = value.data; } } } - // Retrieve the style class associated with the current XML tag. - int style = 0; - uint32_t style_bag_type_set_flags = 0; + // Retrieve the style resource ID associated with the current XML tag's style attribute. + uint32_t style_resid = 0u; + uint32_t style_flags = 0u; if (xml_parser != nullptr) { ssize_t idx = xml_parser->indexOfStyle(); if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) { if (value.dataType == value.TYPE_ATTRIBUTE) { - if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) { + // Resolve the attribute with out theme. + if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) { value.dataType = Res_value::TYPE_NULL; } } + if (value.dataType == value.TYPE_REFERENCE) { - style = value.data; + style_resid = value.data; } } } - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - // Retrieve the default style bag, if requested. - const ResTable::bag_entry* def_style_attr_start = nullptr; - uint32_t def_style_type_set_flags = 0; - ssize_t bag_off = def_style_res != 0 - ? res.getBagLocked(def_style_res, &def_style_attr_start, - &def_style_type_set_flags) - : -1; - def_style_type_set_flags |= def_style_bag_type_set_flags; - const ResTable::bag_entry* const def_style_attr_end = - def_style_attr_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder def_style_attr_finder(def_style_attr_start, - def_style_attr_end); + const ResolvedBag* default_style_bag = nullptr; + if (def_style_resid != 0) { + default_style_bag = assetmanager->GetBag(def_style_resid); + if (default_style_bag != nullptr) { + def_style_flags |= default_style_bag->type_spec_flags; + } + } + + BagAttributeFinder def_style_attr_finder(default_style_bag); // Retrieve the style class bag, if requested. - const ResTable::bag_entry* style_attr_start = nullptr; - uint32_t style_type_set_flags = 0; - bag_off = - style != 0 - ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags) - : -1; - style_type_set_flags |= style_bag_type_set_flags; - const ResTable::bag_entry* const style_attr_end = - style_attr_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end); + const ResolvedBag* xml_style_bag = nullptr; + if (style_resid != 0) { + xml_style_bag = assetmanager->GetBag(style_resid); + if (xml_style_bag != nullptr) { + style_flags |= xml_style_bag->type_spec_flags; + } + } + + BagAttributeFinder xml_style_attr_finder(xml_style_bag); // Retrieve the XML attributes, if requested. - static const ssize_t kXmlBlock = 0x10000000; XmlAttributeFinder xml_attr_finder(xml_parser); - const size_t xml_attr_end = - xml_parser != nullptr ? xml_parser->getAttributeCount() : 0; // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -289,8 +280,8 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ssize_t block = kXmlBlock; - uint32_t type_set_flags = 0; + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0u; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -302,7 +293,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s // Walk through the xml attributes looking for the requested attribute. const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); - if (xml_attr_idx != xml_attr_end) { + if (xml_attr_idx != xml_attr_finder.end()) { // We found the attribute we were looking for. xml_parser->getAttributeValue(xml_attr_idx, &value); if (kDebugStyles) { @@ -312,12 +303,12 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the style class values looking for the requested attribute. - const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident); - if (style_attr_entry != style_attr_end) { + const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident); + if (entry != xml_style_attr_finder.end()) { // We found the attribute we were looking for. - block = style_attr_entry->stringBlock; - type_set_flags = style_type_set_flags; - value = style_attr_entry->map.value; + cookie = entry->cookie; + type_set_flags = style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -326,25 +317,25 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the default style values looking for the requested attribute. - const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident); - if (def_style_attr_entry != def_style_attr_end) { + const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident); + if (entry != def_style_attr_finder.end()) { // We found the attribute we were looking for. - block = def_style_attr_entry->stringBlock; - type_set_flags = style_type_set_flags; - value = def_style_attr_entry->map.value; + cookie = entry->cookie; + type_set_flags = def_style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } } } - uint32_t resid = 0; + uint32_t resid = 0u; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ssize_t new_block = - theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); - if (new_block >= 0) { - block = new_block; + ApkAssetsCookie new_cookie = + theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; } if (kDebugStyles) { @@ -352,14 +343,15 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s } } else if (value.data != Res_value::DATA_NULL_EMPTY) { // If we still don't have a value for this attribute, try to find it in the theme! - ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); - if (new_block >= 0) { + ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); + if (new_cookie != kInvalidCookie) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); - if (new_block >= 0) { - block = new_block; + new_cookie = + assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; } if (kDebugStyles) { @@ -375,7 +367,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = kXmlBlock; + cookie = kInvalidCookie; } if (kDebugStyles) { @@ -385,9 +377,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != kXmlBlock ? static_cast(res.getTableCookie(block)) - : static_cast(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -402,36 +392,28 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s out_values += STYLE_NUM_ENTRIES; } - res.unlock(); - // out_indices must NOT be nullptr. out_indices[0] = indices_idx; } -bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, - uint32_t* attrs, size_t attrs_length, - uint32_t* out_values, uint32_t* out_indices) { +bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { ResTable_config config; Res_value value; int indices_idx = 0; - // Now lock down the resource object and start pulling stuff from it. - res->lock(); - // Retrieve the XML attributes, if requested. const size_t xml_attr_count = xml_parser->getAttributeCount(); size_t ix = 0; uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix); - static const ssize_t kXmlBlock = 0x10000000; - // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - ssize_t block = kXmlBlock; - uint32_t type_set_flags = 0; + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0u; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -450,28 +432,27 @@ bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, cur_xml_attr = xml_parser->getAttributeNameResID(ix); } - uint32_t resid = 0; + uint32_t resid = 0u; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - // printf("Resolving attribute reference\n"); - ssize_t new_block = res->resolveReference(&value, block, &resid, - &type_set_flags, &config); - if (new_block >= 0) block = new_block; + ApkAssetsCookie new_cookie = + assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } } // Deal with the special @null value -- it turns back to TYPE_NULL. if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = kXmlBlock; + cookie = kInvalidCookie; } // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != kXmlBlock ? static_cast(res->getTableCookie(block)) - : static_cast(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -485,8 +466,6 @@ bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, out_values += STYLE_NUM_ENTRIES; } - res->unlock(); - if (out_indices != nullptr) { out_indices[0] = indices_idx; } diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 28548e27baf0..e08848f891f6 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -324,8 +324,6 @@ bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_i bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, FindEntryResult* out_entry) const { - ATRACE_CALL(); - // If the type IDs are offset in this package, we need to take that into account when searching // for a type. const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h index f281921824e7..03fad4947dfe 100644 --- a/libs/androidfw/include/androidfw/AttributeFinder.h +++ b/libs/androidfw/include/androidfw/AttributeFinder.h @@ -58,6 +58,7 @@ class BackTrackingAttributeFinder { BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end); Iterator Find(uint32_t attr); + inline Iterator end(); private: void JumpToClosestAttribute(uint32_t package_id); @@ -201,6 +202,11 @@ Iterator BackTrackingAttributeFinder::Find(uint32_t attr) { return end_; } +template +Iterator BackTrackingAttributeFinder::end() { + return end_; +} + } // namespace android #endif // ANDROIDFW_ATTRIBUTE_FINDER_H diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h index 69b760414846..35ef98d8c704 100644 --- a/libs/androidfw/include/androidfw/AttributeResolution.h +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -17,7 +17,8 @@ #ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H #define ANDROIDFW_ATTRIBUTERESOLUTION_H -#include +#include "androidfw/AssetManager2.h" +#include "androidfw/ResourceTypes.h" namespace android { @@ -42,19 +43,19 @@ enum { // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, +bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid, uint32_t* src_values, size_t src_values_length, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` is NOT optional and must NOT be nullptr. -void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs, +bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); } // namespace android diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 965e2dbd2fb2..1775f5070f4e 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -45,16 +45,17 @@ struct FindEntryResult { // A pointer to the resource table entry for this resource. // If the size of the entry is > sizeof(ResTable_entry), it can be cast to // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry = nullptr; + const ResTable_entry* entry; - // The configuration for which the resulting entry was defined. - const ResTable_config* config = nullptr; + // The configuration for which the resulting entry was defined. This points to a structure that + // is already swapped to host endianness. + const ResTable_config* config; - // Stores the resulting bitmask of configuration axis with which the resource value varies. - uint32_t type_flags = 0u; + // The bitmask of configuration axis with which the resource value varies. + uint32_t type_flags; // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table = nullptr; + const DynamicRefTable* dynamic_ref_table; // The string pool reference to the type's name. This uses a different string pool than // the global string pool, but this is hidden from the caller. diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h new file mode 100644 index 000000000000..64924f433245 --- /dev/null +++ b/libs/androidfw/include/androidfw/MutexGuard.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROIDFW_MUTEXGUARD_H +#define ANDROIDFW_MUTEXGUARD_H + +#include +#include + +#include "android-base/macros.h" + +namespace android { + +template +class ScopedLock; + +// Owns the guarded object and protects access to it via a mutex. +// The guarded object is inaccessible via this class. +// The mutex is locked and the object accessed via the ScopedLock class. +// +// NOTE: The template parameter T should not be a raw pointer, since ownership +// is ambiguous and error-prone. Instead use an std::unique_ptr<>. +// +// Example use: +// +// Guarded shared_string("hello"); +// { +// ScopedLock locked_string(shared_string); +// *locked_string += " world"; +// } +// +template +class Guarded { + static_assert(!std::is_pointer::value, "T must not be a raw pointer"); + + public: + explicit Guarded() : guarded_() { + } + + template + explicit Guarded(const T& guarded, + typename std::enable_if::value>::type = void()) + : guarded_(guarded) { + } + + template + explicit Guarded(T&& guarded, + typename std::enable_if::value>::type = void()) + : guarded_(std::move(guarded)) { + } + + private: + friend class ScopedLock; + + DISALLOW_COPY_AND_ASSIGN(Guarded); + + std::mutex lock_; + T guarded_; +}; + +template +class ScopedLock { + public: + explicit ScopedLock(Guarded& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) { + } + + T& operator*() { + return guarded_; + } + + T* operator->() { + return &guarded_; + } + + T* get() { + return &guarded_; + } + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedLock); + + std::lock_guard lock_; + T& guarded_; +}; + +} // namespace android + +#endif // ANDROIDFW_MUTEXGUARD_H diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index 2d73ce8f8ee3..cc3053798e7b 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -21,6 +21,7 @@ #include "android-base/file.h" #include "android-base/logging.h" #include "android-base/macros.h" +#include "androidfw/AssetManager2.h" #include "TestHelpers.h" #include "data/styles/R.h" @@ -32,15 +33,14 @@ namespace android { class AttributeResolutionTest : public ::testing::Test { public: virtual void SetUp() override { - std::string contents; - ASSERT_TRUE(ReadFileFromZipToString( - GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, table_.add(contents.data(), contents.size(), - 1 /*cookie*/, true /*copyData*/)); + styles_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + ASSERT_NE(nullptr, styles_assets_); + assetmanager_.SetApkAssets({styles_assets_.get()}); } protected: - ResTable table_; + std::unique_ptr styles_assets_; + AssetManager2 assetmanager_; }; class AttributeResolutionXmlTest : public AttributeResolutionTest { @@ -48,13 +48,12 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { virtual void SetUp() override { AttributeResolutionTest::SetUp(); - std::string contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", - "res/layout/layout.xml", &contents)); + std::unique_ptr asset = + assetmanager_.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); + ASSERT_NE(nullptr, asset); - ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(), - true /*copyData*/)); + ASSERT_EQ(NO_ERROR, + xml_parser_.setTo(asset->getBuffer(true), asset->getLength(), true /*copyData*/)); // Skip to the first tag. while (xml_parser_.next() != ResXMLParser::START_TAG) { @@ -66,14 +65,14 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { }; TEST_F(AttributeResolutionTest, Theme) { - ResTable::Theme theme(table_); - ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); + std::unique_ptr theme = assetmanager_.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_empty}}; std::array values; - ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/, + ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/, nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(), attrs.size(), values.data(), nullptr /*out_indices*/)); @@ -126,8 +125,8 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { R::attr::attr_four, R::attr::attr_empty}}; std::array values; - ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(), - nullptr /*out_indices*/)); + ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(), + values.data(), nullptr /*out_indices*/)); uint32_t* values_cursor = values.data(); EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]); @@ -171,15 +170,15 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { } TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) { - ResTable::Theme theme(table_); - ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); + std::unique_ptr theme = assetmanager_.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}}; std::array values; std::array indices; - ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(), + ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), attrs.size(), values.data(), indices.data()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index 7149beef797f..a8abcb5df86c 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -33,12 +33,12 @@ void GetResourceBenchmarkOld(const std::vector& paths, const ResTab } } + // Make sure to force creation of the ResTable first, or else the configuration doesn't get set. + const ResTable& table = assetmanager.getResources(true); if (config != nullptr) { assetmanager.setConfiguration(*config); } - const ResTable& table = assetmanager.getResources(true); - Res_value value; ResTable_config selected_config; uint32_t flags; diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp index 98e9a42d944d..e70d5ea0d566 100644 --- a/native/android/asset_manager.cpp +++ b/native/android/asset_manager.cpp @@ -18,9 +18,11 @@ #include #include +#include #include #include #include +#include #include #include "jni.h" @@ -35,21 +37,20 @@ using namespace android; // ----- struct AAssetDir { - AssetDir* mAssetDir; + std::unique_ptr mAssetDir; size_t mCurFileIndex; String8 mCachedFileName; - explicit AAssetDir(AssetDir* dir) : mAssetDir(dir), mCurFileIndex(0) { } - ~AAssetDir() { delete mAssetDir; } + explicit AAssetDir(std::unique_ptr dir) : + mAssetDir(std::move(dir)), mCurFileIndex(0) { } }; // ----- struct AAsset { - Asset* mAsset; + std::unique_ptr mAsset; - explicit AAsset(Asset* asset) : mAsset(asset) { } - ~AAsset() { delete mAsset; } + explicit AAsset(std::unique_ptr asset) : mAsset(std::move(asset)) { } }; // -------------------- Public native C API -------------------- @@ -104,19 +105,18 @@ AAsset* AAssetManager_open(AAssetManager* amgr, const char* filename, int mode) return NULL; } - AssetManager* mgr = static_cast(amgr); - Asset* asset = mgr->open(filename, amMode); - if (asset == NULL) { - return NULL; + ScopedLock locked_mgr(*AssetManagerForNdkAssetManager(amgr)); + std::unique_ptr asset = locked_mgr->Open(filename, amMode); + if (asset == nullptr) { + return nullptr; } - - return new AAsset(asset); + return new AAsset(std::move(asset)); } AAssetDir* AAssetManager_openDir(AAssetManager* amgr, const char* dirName) { - AssetManager* mgr = static_cast(amgr); - return new AAssetDir(mgr->openDir(dirName)); + ScopedLock locked_mgr(*AssetManagerForNdkAssetManager(amgr)); + return new AAssetDir(locked_mgr->OpenDir(dirName)); } /** diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index b32be736533b..52d0e08e4e7f 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -24,8 +24,9 @@ #include #include +#include #include -#include +#include #include #include @@ -1664,18 +1665,22 @@ nFileA3DCreateFromAssetStream(JNIEnv *_env, jobject _this, jlong con, jlong nati static jlong nFileA3DCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path) { - AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); + Guarded* mgr = AssetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); - if (asset == nullptr) { - return 0; + std::unique_ptr asset; + { + ScopedLock locked_mgr(*mgr); + asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; + } } - jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset); + jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset.release()); return id; } @@ -1752,22 +1757,25 @@ static jlong nFontCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path, jfloat fontSize, jint dpi) { - AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); + Guarded* mgr = AssetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); - if (asset == nullptr) { - return 0; + std::unique_ptr asset; + { + ScopedLock locked_mgr(*mgr); + asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; + } } jlong id = (jlong)(uintptr_t)rsFontCreateFromMemory((RsContext)con, str.c_str(), str.length(), fontSize, dpi, asset->getBuffer(false), asset->getLength()); - delete asset; return id; } -- cgit v1.2.3-59-g8ed1b From 392132748416719e3df427e6ac8dc11af194342c Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 8 Jan 2018 17:38:30 -0800 Subject: libandroidfw: Improve performance of AssetManager2 AssetManager2 relied on creating a list of configurations present in the resource table so as to avoid copying and converting ResTable_config's from the APK on every resource retrieval. ResTable, however, had a better optimization that pruned the configurations that didn't match the currently set configuration. This vastly reduced the number of ResTable_configs to test. In this CL, AssetManager2 follows suite with this optimization and only maintains the filtered ResTable_configs, falling back to the slow path when the configuration is overridden. Test: mma frameworks/base/libs/androidfw Test: adb sync system data Test: adb shell /data/benchmarktest64/libandroidfw_benchmarks/libandroidfw_benchmarks Change-Id: I42d53f703623cf7c3f7cbcf194f5026ff540faf4 --- libs/androidfw/Android.bp | 1 + libs/androidfw/AssetManager2.cpp | 273 +++++++++++++++++------ libs/androidfw/LoadedArsc.cpp | 261 +++++++--------------- libs/androidfw/include/androidfw/AssetManager2.h | 66 ++++-- libs/androidfw/include/androidfw/LoadedArsc.h | 111 +++++---- libs/androidfw/tests/ApkAssets_test.cpp | 78 +++---- libs/androidfw/tests/LoadedArsc_test.cpp | 169 +++++++------- libs/androidfw/tests/TestHelpers.h | 1 + 8 files changed, 538 insertions(+), 422 deletions(-) (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 7c9078b164a2..70d52164ff74 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -145,6 +145,7 @@ cc_test { "tests/TypeWrappers_test.cpp", "tests/ZipUtils_test.cpp", ], + static_libs: ["libgmock"], target: { android: { srcs: [ diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 2fc8e952707b..a558ff7ccfc1 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -36,6 +36,31 @@ namespace android { +struct FindEntryResult { + // A pointer to the resource table entry for this resource. + // If the size of the entry is > sizeof(ResTable_entry), it can be cast to + // a ResTable_map_entry and processed as a bag/map. + const ResTable_entry* entry; + + // The configuration for which the resulting entry was defined. This is already swapped to host + // endianness. + ResTable_config config; + + // The bitmask of configuration axis with which the resource value varies. + uint32_t type_flags; + + // The dynamic package ID map for the package from which this resource came from. + const DynamicRefTable* dynamic_ref_table; + + // The string pool reference to the type's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef type_string_ref; + + // The string pool reference to the entry's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef entry_string_ref; +}; + AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } @@ -44,6 +69,7 @@ bool AssetManager2::SetApkAssets(const std::vector& apk_assets bool invalidate_caches) { apk_assets_ = apk_assets; BuildDynamicRefTable(); + RebuildFilterList(); if (invalidate_caches) { InvalidateCaches(static_cast(-1)); } @@ -79,7 +105,7 @@ void AssetManager2::BuildDynamicRefTable() { PackageGroup* package_group = &package_groups_[idx]; // Add the package and to the set of packages with the same ID. - package_group->packages_.push_back(package.get()); + package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); package_group->cookies_.push_back(static_cast(i)); // Add the package name -> build time ID mappings. @@ -94,7 +120,7 @@ void AssetManager2::BuildDynamicRefTable() { // Now assign the runtime IDs so that we have a build-time to runtime ID map. const auto package_groups_end = package_groups_.end(); for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) { - const std::string& package_name = iter->packages_[0]->GetPackageName(); + const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName(); for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) { iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()), iter->dynamic_ref_table.mAssignedPackageId); @@ -108,17 +134,20 @@ void AssetManager2::DumpToLog() const { std::string list; for (size_t i = 0; i < package_ids_.size(); i++) { if (package_ids_[i] != 0xff) { - base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]); + base::StringAppendF(&list, "%02x -> %d, ", (int)i, package_ids_[i]); } } LOG(INFO) << "Package ID map: " << list; - for (const auto& package_group: package_groups_) { - list = ""; - for (const auto& package : package_group.packages_) { - base::StringAppendF(&list, "%s(%02x), ", package->GetPackageName().c_str(), package->GetPackageId()); - } - LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list; + for (const auto& package_group : package_groups_) { + list = ""; + for (const auto& package : package_group.packages_) { + base::StringAppendF(&list, "%s(%02x), ", package.loaded_package_->GetPackageName().c_str(), + package.loaded_package_->GetPackageId()); + } + LOG(INFO) << base::StringPrintf("PG (%02x): ", + package_group.dynamic_ref_table.mAssignedPackageId) + << list; } } @@ -157,52 +186,54 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { configuration_ = configuration; if (diff) { + RebuildFilterList(); InvalidateCaches(static_cast(diff)); } } std::set AssetManager2::GetResourceConfigurations(bool exclude_system, - bool exclude_mipmap) { + bool exclude_mipmap) const { ATRACE_CALL(); std::set configurations; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { - if (exclude_system && package->IsSystem()) { + for (const ConfiguredPackage& package : package_group.packages_) { + if (exclude_system && package.loaded_package_->IsSystem()) { continue; } - package->CollectConfigurations(exclude_mipmap, &configurations); + package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations); } } return configurations; } std::set AssetManager2::GetResourceLocales(bool exclude_system, - bool merge_equivalent_languages) { + bool merge_equivalent_languages) const { ATRACE_CALL(); std::set locales; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { - if (exclude_system && package->IsSystem()) { + for (const ConfiguredPackage& package : package_group.packages_) { + if (exclude_system && package.loaded_package_->IsSystem()) { continue; } - package->CollectLocales(merge_equivalent_languages, &locales); + package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales); } } return locales; } -std::unique_ptr AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) { +std::unique_ptr AssetManager2::Open(const std::string& filename, + Asset::AccessMode mode) const { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, mode); } std::unique_ptr AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) { + Asset::AccessMode mode) const { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, cookie, mode); } -std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) { +std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) const { ATRACE_CALL(); std::string full_path = "assets/" + dirname; @@ -236,7 +267,7 @@ std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) { // is inconsistent for split APKs. std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie) { + ApkAssetsCookie* out_cookie) const { ATRACE_CALL(); for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) { std::unique_ptr asset = apk_assets_[i]->Open(filename, mode); @@ -255,7 +286,8 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, - ApkAssetsCookie cookie, Asset::AccessMode mode) { + ApkAssetsCookie cookie, + Asset::AccessMode mode) const { ATRACE_CALL(); if (cookie < 0 || static_cast(cookie) >= apk_assets_.size()) { return {}; @@ -264,12 +296,13 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, - bool stop_at_first_match, FindEntryResult* out_entry) { + bool /*stop_at_first_match*/, + FindEntryResult* out_entry) const { // Might use this if density_override != 0. ResTable_config density_override_config; // Select our configuration or generate a density override configuration. - ResTable_config* desired_config = &configuration_; + const ResTable_config* desired_config = &configuration_; if (density_override != 0 && density_override != configuration_.density) { density_override_config = configuration_; density_override_config.density = density_override; @@ -283,53 +316,135 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri const uint32_t package_id = get_package_id(resid); const uint8_t type_idx = get_type_id(resid) - 1; - const uint16_t entry_id = get_entry_id(resid); + const uint16_t entry_idx = get_entry_id(resid); - const uint8_t idx = package_ids_[package_id]; - if (idx == 0xff) { + const uint8_t package_idx = package_ids_[package_id]; + if (package_idx == 0xff) { LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); return kInvalidCookie; } - FindEntryResult best_entry; - ApkAssetsCookie best_cookie = kInvalidCookie; - uint32_t cumulated_flags = 0u; - - const PackageGroup& package_group = package_groups_[idx]; + const PackageGroup& package_group = package_groups_[package_idx]; const size_t package_count = package_group.packages_.size(); - FindEntryResult current_entry; - for (size_t i = 0; i < package_count; i++) { - const LoadedPackage* loaded_package = package_group.packages_[i]; - if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry)) { + + ApkAssetsCookie best_cookie = kInvalidCookie; + const LoadedPackage* best_package = nullptr; + const ResTable_type* best_type = nullptr; + const ResTable_config* best_config = nullptr; + ResTable_config best_config_copy; + uint32_t best_offset = 0u; + uint32_t type_flags = 0u; + + // If desired_config is the same as the set configuration, then we can use our filtered list + // and we don't need to match the configurations, since they already matched. + const bool use_fast_path = desired_config == &configuration_; + + for (size_t pi = 0; pi < package_count; pi++) { + const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; + const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_; + ApkAssetsCookie cookie = package_group.cookies_[pi]; + + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx); + if (UNLIKELY(type_spec == nullptr)) { continue; } - cumulated_flags |= current_entry.type_flags; + uint16_t local_entry_idx = entry_idx; - const ResTable_config* current_config = current_entry.config; - const ResTable_config* best_config = best_entry.config; - if (best_cookie == kInvalidCookie || - current_config->isBetterThan(*best_config, desired_config) || - (loaded_package->IsOverlay() && current_config->compare(*best_config) == 0)) { - best_entry = current_entry; - best_cookie = package_group.cookies_[i]; - if (stop_at_first_match) { - break; + // If there is an IDMAP supplied with this package, translate the entry ID. + if (type_spec->idmap_entries != nullptr) { + if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) { + // There is no mapping, so the resource is not meant to be in this overlay package. + continue; + } + } + + type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx); + + // If the package is an overlay, then even configurations that are the same MUST be chosen. + const bool package_is_overlay = loaded_package->IsOverlay(); + + const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; + if (use_fast_path) { + const std::vector& candidate_configs = filtered_group.configurations; + const size_t type_count = candidate_configs.size(); + for (uint32_t i = 0; i < type_count; i++) { + const ResTable_config& this_config = candidate_configs[i]; + + // We can skip calling ResTable_config::match() because we know that all candidate + // configurations that do NOT match have been filtered-out. + if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || + (package_is_overlay && this_config.compare(*best_config) == 0)) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const ResTable_type* type_chunk = filtered_group.types[i]; + const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + best_cookie = cookie; + best_package = loaded_package; + best_type = type_chunk; + best_config = &this_config; + best_offset = offset; + } + } + } else { + // This is the slower path, which doesn't use the filtered list of configurations. + // Here we must read the ResTable_config from the mmapped APK, convert it to host endianness + // and fill in any new fields that did not exist when the APK was compiled. + // Furthermore when selecting configurations we can't just record the pointer to the + // ResTable_config, we must copy it. + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config this_config; + this_config.copyFromDtoH((*iter)->config); + + if (this_config.match(*desired_config)) { + if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || + (package_is_overlay && this_config.compare(*best_config) == 0)) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + best_cookie = cookie; + best_package = loaded_package; + best_type = *iter; + best_config_copy = this_config; + best_config = &best_config_copy; + best_offset = offset; + } + } } } } - if (best_cookie == kInvalidCookie) { + if (UNLIKELY(best_cookie == kInvalidCookie)) { return kInvalidCookie; } - *out_entry = best_entry; + const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset); + if (UNLIKELY(best_entry == nullptr)) { + return kInvalidCookie; + } + + out_entry->entry = best_entry; + out_entry->config = *best_config; + out_entry->type_flags = type_flags; + out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); + out_entry->entry_string_ref = + StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; - out_entry->type_flags = cumulated_flags; return best_cookie; } -bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { +bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const { ATRACE_CALL(); FindEntryResult entry; @@ -339,7 +454,8 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { return false; } - const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid); + const LoadedPackage* package = + apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); if (package == nullptr) { return false; } @@ -367,7 +483,7 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { return true; } -bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { +bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const { FindEntryResult entry; ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); @@ -381,7 +497,7 @@ bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) { + uint32_t* out_flags) const { ATRACE_CALL(); FindEntryResult entry; @@ -400,7 +516,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Create a reference since we can't represent this complex type as a Res_value. out_value->dataType = Res_value::TYPE_REFERENCE; out_value->data = resid; - *out_selected_config = *entry.config; + *out_selected_config = entry.config; *out_flags = entry.type_flags; return cookie; } @@ -412,7 +528,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Convert the package ID to the runtime assigned package ID. entry.dynamic_ref_table->lookupResourceValue(out_value); - *out_selected_config = *entry.config; + *out_selected_config = entry.config; *out_flags = entry.type_flags; return cookie; } @@ -420,7 +536,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) { + uint32_t* out_last_reference) const { ATRACE_CALL(); constexpr const int kMaxIterations = 20; @@ -488,7 +604,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { // Attributes, arrays, etc don't have a resource id as the name. They specify // other data, which would be wrong to change via a lookup. if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, + resid); return nullptr; } } @@ -520,7 +637,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { const ResolvedBag* parent_bag = GetBag(parent_resid); if (parent_bag == nullptr) { // Failed to get the parent that should exist. - LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid); + LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, + resid); return nullptr; } @@ -539,7 +657,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t child_key = dtohl(map_entry->name.ident); if (!is_internal_resid(child_key)) { if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, + resid); return nullptr; } } @@ -578,7 +697,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t new_key = dtohl(map_entry->name.ident); if (!is_internal_resid(new_key)) { if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, + resid); return nullptr; } } @@ -634,7 +754,7 @@ static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) { uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const std::string& fallback_type, - const std::string& fallback_package) { + const std::string& fallback_package) const { StringPiece package_name, type, entry; if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) { return 0u; @@ -666,7 +786,8 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const static std::u16string kAttrPrivate16 = u"^attr-private"; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { + for (const ConfiguredPackage& package_impl : package_group.packages_) { + const LoadedPackage* package = package_impl.loaded_package_; if (package_name != package->GetPackageName()) { // All packages in the same group are expected to have the same package name. break; @@ -688,6 +809,32 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, return 0u; } +void AssetManager2::RebuildFilterList() { + for (PackageGroup& group : package_groups_) { + for (ConfiguredPackage& impl : group.packages_) { + // Destroy it. + impl.filtered_configs_.~ByteBucketArray(); + + // Re-create it. + new (&impl.filtered_configs_) ByteBucketArray(); + + // Create the filters here. + impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec* spec, uint8_t type_index) { + FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_index); + const auto iter_end = spec->types + spec->type_count; + for (auto iter = spec->types; iter != iter_end; ++iter) { + ResTable_config this_config; + this_config.copyFromDtoH((*iter)->config); + if (this_config.match(configuration_)) { + group.configurations.push_back(this_config); + group.types.push_back(*iter); + } + } + }); + } + } +} + void AssetManager2::InvalidateCaches(uint32_t diff) { if (diff == 0xffffffffu) { // Everything must go. @@ -868,7 +1015,7 @@ ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_type_spec_flags, - uint32_t* out_last_ref) { + uint32_t* out_last_ref) const { if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) { uint32_t new_flags; cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags); diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index e08848f891f6..1d2c597c4c8c 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -44,44 +44,6 @@ namespace android { constexpr const static int kAppPackageId = 0x7f; -// Element of a TypeSpec array. See TypeSpec. -struct Type { - // The configuration for which this type defines entries. - // This is already converted to host endianness. - ResTable_config configuration; - - // Pointer to the mmapped data where entry definitions are kept. - const ResTable_type* type; -}; - -// TypeSpec is going to be immediately proceeded by -// an array of Type structs, all in the same block of memory. -struct TypeSpec { - // Pointer to the mmapped data where flags are kept. - // Flags denote whether the resource entry is public - // and under which configurations it varies. - const ResTable_typeSpec* type_spec; - - // Pointer to the mmapped data where the IDMAP mappings for this type - // exist. May be nullptr if no IDMAP exists. - const IdmapEntry_header* idmap_entries; - - // The number of types that follow this struct. - // There is a type for each configuration - // that entries are defined for. - size_t type_count; - - // Trick to easily access a variable number of Type structs - // proceeding this struct, and to ensure their alignment. - const Type types[0]; -}; - -// TypeSpecPtr points to the block of memory that holds -// a TypeSpec struct, followed by an array of Type structs. -// TypeSpecPtr is a managed pointer that knows how to delete -// itself. -using TypeSpecPtr = util::unique_cptr; - namespace { // Builder that helps accumulate Type structs and then create a single @@ -95,21 +57,22 @@ class TypeSpecPtrBuilder { } void AddType(const ResTable_type* type) { - ResTable_config config; - config.copyFromDtoH(type->config); - types_.push_back(Type{config, type}); + types_.push_back(type); } TypeSpecPtr Build() { // Check for overflow. - if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) { + using ElementType = const ResTable_type*; + if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(ElementType) < + types_.size()) { return {}; } - TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type))); + TypeSpec* type_spec = + (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType))); type_spec->type_spec = header_; type_spec->idmap_entries = idmap_header_; type_spec->type_count = types_.size(); - memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type)); + memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType)); return TypeSpecPtr(type_spec); } @@ -118,7 +81,7 @@ class TypeSpecPtrBuilder { const ResTable_typeSpec* header_; const IdmapEntry_header* idmap_header_; - std::vector types_; + std::vector types_; }; } // namespace @@ -162,18 +125,17 @@ static bool VerifyResTableType(const ResTable_type* header) { return true; } -static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset, - size_t entry_idx) { +static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) { // Check that the offset is aligned. if (entry_offset & 0x03) { - LOG(ERROR) << "Entry offset at index " << entry_idx << " is not 4-byte aligned."; + LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned."; return false; } // Check that the offset doesn't overflow. if (entry_offset > std::numeric_limits::max() - dtohl(type->entriesStart)) { // Overflow in offset. - LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large."; + LOG(ERROR) << "Entry at offset " << entry_offset << " is too large."; return false; } @@ -181,7 +143,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset entry_offset += dtohl(type->entriesStart); if (entry_offset > chunk_size - sizeof(ResTable_entry)) { - LOG(ERROR) << "Entry offset at index " << entry_idx + LOG(ERROR) << "Entry at offset " << entry_offset << " is too large. No room for ResTable_entry."; return false; } @@ -191,13 +153,13 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t entry_size = dtohs(entry->size); if (entry_size < sizeof(*entry)) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx + LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too small."; return false; } if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx + LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too large."; return false; } @@ -205,7 +167,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset if (entry_size < sizeof(ResTable_map_entry)) { // There needs to be room for one Res_value struct. if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) { - LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << entry_idx + LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset << " for type " << (int)type->id << "."; return false; } @@ -214,12 +176,12 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset reinterpret_cast(reinterpret_cast(entry) + entry_size); const size_t value_size = dtohs(value->size); if (value_size < sizeof(Res_value)) { - LOG(ERROR) << "Res_value at index " << entry_idx << " is too small."; + LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small."; return false; } if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) { - LOG(ERROR) << "Res_value size " << value_size << " at index " << entry_idx + LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset << " is too large."; return false; } @@ -228,117 +190,76 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t map_entry_count = dtohl(map->count); size_t map_entries_start = entry_offset + entry_size; if (map_entries_start & 0x03) { - LOG(ERROR) << "Map entries at index " << entry_idx << " start at unaligned offset."; + LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset."; return false; } // Each entry is sizeof(ResTable_map) big. if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) { - LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << entry_idx << "."; + LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << "."; return false; } } return true; } -bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_idx, - const ResTable_config& config, FindEntryResult* out_entry) const { - const ResTable_config* best_config = nullptr; - const ResTable_type* best_type = nullptr; - uint32_t best_offset = 0; - - for (uint32_t i = 0; i < type_spec_ptr->type_count; i++) { - const Type* type = &type_spec_ptr->types[i]; - const ResTable_type* type_chunk = type->type; - - if (type->configuration.match(config) && - (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const size_t entry_count = dtohl(type_chunk->entryCount); - const size_t offsets_offset = dtohs(type_chunk->header.headerSize); - - // Check if there is the desired entry in this type. - - if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { - // This is encoded as a sparse map, so perform a binary search. - const ResTable_sparseTypeEntry* sparse_indices = - reinterpret_cast( - reinterpret_cast(type_chunk) + offsets_offset); - const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; - const ResTable_sparseTypeEntry* result = - std::lower_bound(sparse_indices, sparse_indices_end, entry_idx, - [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { - return dtohs(entry.idx) < entry_idx; - }); - - if (result == sparse_indices_end || dtohs(result->idx) != entry_idx) { - // No entry found. - continue; - } - - // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as - // the real offset divided by 4. - best_offset = uint32_t{dtohs(result->offset)} * 4u; - } else { - if (entry_idx >= entry_count) { - // This entry cannot be here. - continue; - } +const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk, + uint16_t entry_index) { + uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index); + if (entry_offset == ResTable_type::NO_ENTRY) { + return nullptr; + } + return GetEntryFromOffset(type_chunk, entry_offset); +} - const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type_chunk) + offsets_offset); - const uint32_t offset = dtohl(entry_offsets[entry_idx]); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } +uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const size_t entry_count = dtohl(type_chunk->entryCount); + const size_t offsets_offset = dtohs(type_chunk->header.headerSize); - // There is an entry for this resource, record it. - best_offset = offset; - } + // Check if there is the desired entry in this type. - best_config = &type->configuration; - best_type = type_chunk; + if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { + // This is encoded as a sparse map, so perform a binary search. + const ResTable_sparseTypeEntry* sparse_indices = + reinterpret_cast( + reinterpret_cast(type_chunk) + offsets_offset); + const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; + const ResTable_sparseTypeEntry* result = + std::lower_bound(sparse_indices, sparse_indices_end, entry_index, + [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { + return dtohs(entry.idx) < entry_idx; + }); + + if (result == sparse_indices_end || dtohs(result->idx) != entry_index) { + // No entry found. + return ResTable_type::NO_ENTRY; } - } - if (best_type == nullptr) { - return false; + // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as + // the real offset divided by 4. + return uint32_t{dtohs(result->offset)} * 4u; } - if (UNLIKELY(!VerifyResTableEntry(best_type, best_offset, entry_idx))) { - return false; + // This type is encoded as a dense array. + if (entry_index >= entry_count) { + // This entry cannot be here. + return ResTable_type::NO_ENTRY; } - const ResTable_entry* best_entry = reinterpret_cast( - reinterpret_cast(best_type) + best_offset + dtohl(best_type->entriesStart)); - - const uint32_t* flags = reinterpret_cast(type_spec_ptr->type_spec + 1); - out_entry->type_flags = dtohl(flags[entry_idx]); - out_entry->entry = best_entry; - out_entry->config = best_config; - out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1); - out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index)); - return true; + const uint32_t* entry_offsets = reinterpret_cast( + reinterpret_cast(type_chunk) + offsets_offset); + return dtohl(entry_offsets[entry_index]); } -bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, - FindEntryResult* out_entry) const { - // If the type IDs are offset in this package, we need to take that into account when searching - // for a type. - const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; - if (UNLIKELY(ptr == nullptr)) { - return false; +const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk, + uint32_t offset) { + if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) { + return nullptr; } - - // If there is an IDMAP supplied with this package, translate the entry ID. - if (ptr->idmap_entries != nullptr) { - if (!LoadedIdmap::Lookup(ptr->idmap_entries, entry_idx, &entry_idx)) { - // There is no mapping, so the resource is not meant to be in this overlay package. - return false; - } - } - return FindEntry(ptr, entry_idx, config, out_entry); + return reinterpret_cast(reinterpret_cast(type_chunk) + + offset + dtohl(type_chunk->entriesStart)); } void LoadedPackage::CollectConfigurations(bool exclude_mipmap, @@ -346,7 +267,7 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, const static std::u16string kMipMap = u"mipmap"; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const util::unique_cptr& type_spec = type_specs_[i]; + const TypeSpecPtr& type_spec = type_specs_[i]; if (type_spec != nullptr) { if (exclude_mipmap) { const int type_idx = type_spec->type_spec->id - 1; @@ -367,8 +288,11 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, } } - for (size_t j = 0; j < type_spec->type_count; j++) { - out_configs->insert(type_spec->types[j].configuration); + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config config; + config.copyFromDtoH((*iter)->config); + out_configs->insert(config); } } } @@ -378,10 +302,12 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set* out char temp_locale[RESTABLE_MAX_LOCALE_LEN]; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const util::unique_cptr& type_spec = type_specs_[i]; + const TypeSpecPtr& type_spec = type_specs_[i]; if (type_spec != nullptr) { - for (size_t j = 0; j < type_spec->type_count; j++) { - const ResTable_config& configuration = type_spec->types[j].configuration; + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config configuration; + configuration.copyFromDtoH((*iter)->config); if (configuration.locale != 0) { configuration.getBcp47Locale(temp_locale, canonicalize); std::string locale(temp_locale); @@ -409,17 +335,17 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } - for (size_t ti = 0; ti < type_spec->type_count; ti++) { - const Type* type = &type_spec->types[ti]; - size_t entry_count = dtohl(type->type->entryCount); + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + const ResTable_type* type = *iter; + size_t entry_count = dtohl(type->entryCount); for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) { const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type->type) + dtohs(type->type->header.headerSize)); + reinterpret_cast(type) + dtohs(type->header.headerSize)); const uint32_t offset = dtohl(entry_offsets[entry_idx]); if (offset != ResTable_type::NO_ENTRY) { - const ResTable_entry* entry = - reinterpret_cast(reinterpret_cast(type->type) + - dtohl(type->type->entriesStart) + offset); + const ResTable_entry* entry = reinterpret_cast( + reinterpret_cast(type) + dtohl(type->entriesStart) + offset); if (dtohl(entry->key.index) == static_cast(key_idx)) { // The package ID will be overridden by the caller (due to runtime assignment of package // IDs for shared libraries). @@ -431,8 +357,7 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } -const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const { - const uint8_t package_id = get_package_id(resid); +const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { for (const auto& loaded_package : packages_) { if (loaded_package->GetPackageId() == package_id) { return loaded_package.get(); @@ -680,26 +605,6 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, return std::move(loaded_package); } -bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, - FindEntryResult* out_entry) const { - ATRACE_CALL(); - - const uint8_t package_id = get_package_id(resid); - const uint8_t type_id = get_type_id(resid); - const uint16_t entry_id = get_entry_id(resid); - - if (UNLIKELY(type_id == 0)) { - LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); - return false; - } - - for (const auto& loaded_package : packages_) { - if (loaded_package->GetPackageId() == package_id) { - return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry); - } - } - return false; -} bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library) { diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index b033137b4764..ef08897d997a 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -69,6 +69,8 @@ struct ResolvedBag { Entry entries[0]; }; +struct FindEntryResult; + // AssetManager2 is the main entry point for accessing assets and resources. // AssetManager2 provides caching of resources retrieved via the underlying ApkAssets. class AssetManager2 { @@ -127,7 +129,7 @@ class AssetManager2 { // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap' // will be excluded from the list. std::set GetResourceConfigurations(bool exclude_system = false, - bool exclude_mipmap = false); + bool exclude_mipmap = false) const; // Returns all the locales for which there are resources defined. This includes resource // locales in all the ApkAssets set for this AssetManager. @@ -136,24 +138,24 @@ class AssetManager2 { // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized // and de-duped in the resulting list. std::set GetResourceLocales(bool exclude_system = false, - bool merge_equivalent_languages = false); + bool merge_equivalent_languages = false) const; // Searches the set of APKs loaded by this AssetManager and opens the first one found located // in the assets/ directory. // `mode` controls how the file is opened. // // NOTE: The loaded APKs are searched in reverse order. - std::unique_ptr Open(const std::string& filename, Asset::AccessMode mode); + std::unique_ptr Open(const std::string& filename, Asset::AccessMode mode) const; // Opens a file within the assets/ directory of the APK specified by `cookie`. // `mode` controls how the file is opened. std::unique_ptr Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode); + Asset::AccessMode mode) const; // Opens the directory specified by `dirname`. The result is an AssetDir that is the combination // of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded. // The entries are sorted by their ASCII name. - std::unique_ptr OpenDir(const std::string& dirname); + std::unique_ptr OpenDir(const std::string& dirname) const; // Searches the set of APKs loaded by this AssetManager and opens the first one found. // `mode` controls how the file is opened. @@ -161,24 +163,24 @@ class AssetManager2 { // // NOTE: The loaded APKs are searched in reverse order. std::unique_ptr OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie = nullptr); + ApkAssetsCookie* out_cookie = nullptr) const; // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened. // This is typically used to open a specific AndroidManifest.xml, or a binary XML file // referenced by a resource lookup with GetResource(). std::unique_ptr OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode); + Asset::AccessMode mode) const; // Populates the `out_name` parameter with resource name information. // Utf8 strings are preferred, and only if they are unavailable are // the Utf16 variants populated. // Returns false if the resource was not found or the name was missing/corrupt. - bool GetResourceName(uint32_t resid, ResourceName* out_name); + bool GetResourceName(uint32_t resid, ResourceName* out_name) const; // Populates `out_flags` with the bitmask of configuration axis that this resource varies with. // See ResTable_config for the list of configuration axis. // Returns false if the resource was not found. - bool GetResourceFlags(uint32_t resid, uint32_t* out_flags); + bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const; // Finds the resource ID assigned to `resource_name`. // `resource_name` must be of the form '[package:][type/]entry'. @@ -186,7 +188,7 @@ class AssetManager2 { // If no type is specified in `resource_name`, then `fallback_type` is used as the type. // Returns 0x0 if no resource by that name was found. uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {}, - const std::string& fallback_package = {}); + const std::string& fallback_package = {}) const; // Retrieves the best matching resource with ID `resid`. The resource value is filled into // `out_value` and the configuration for the selected value is populated in `out_selected_config`. @@ -199,7 +201,7 @@ class AssetManager2 { // this function logs if the resource was a map/bag type before returning kInvalidCookie. ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags); + uint32_t* out_flags) const; // Resolves the resource reference in `in_out_value` if the data type is // Res_value::TYPE_REFERENCE. @@ -215,7 +217,7 @@ class AssetManager2 { // it was not found. ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference); + uint32_t* out_last_reference) const; // Retrieves the best matching bag/map resource with ID `resid`. // This method will resolve all parent references for this bag and merge keys with the child. @@ -233,9 +235,9 @@ class AssetManager2 { std::unique_ptr NewTheme(); template - void ForEachPackage(Func func) { + void ForEachPackage(Func func) const { for (const PackageGroup& package_group : package_groups_) { - func(package_group.packages_.front()->GetPackageName(), + func(package_group.packages_.front().loaded_package_->GetPackageName(), package_group.dynamic_ref_table.mAssignedPackageId); } } @@ -260,7 +262,7 @@ class AssetManager2 { // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds. ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, - FindEntryResult* out_entry); + FindEntryResult* out_entry) const; // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. @@ -270,13 +272,43 @@ class AssetManager2 { // bitmask `diff`. void InvalidateCaches(uint32_t diff); + // Triggers the re-construction of lists of types that match the set configuration. + // This should always be called when mutating the AssetManager's configuration or ApkAssets set. + void RebuildFilterList(); + // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector apk_assets_; + // A collection of configurations and their associated ResTable_type that match the current + // AssetManager configuration. + struct FilteredConfigGroup { + std::vector configurations; + std::vector types; + }; + + // Represents an single package. + struct ConfiguredPackage { + // A pointer to the immutable, loaded package info. + const LoadedPackage* loaded_package_; + + // A mutable AssetManager-specific list of configurations that match the AssetManager's + // current configuration. This is used as an optimization to avoid checking every single + // candidate configuration when looking up resources. + ByteBucketArray filtered_configs_; + }; + + // Represents a logical package, which can be made up of many individual packages. Each package + // in a PackageGroup shares the same package name and package ID. struct PackageGroup { - std::vector packages_; + // The set of packages that make-up this group. + std::vector packages_; + + // The cookies associated with each package in the group. They share the same order as + // packages_. std::vector cookies_; + + // A library reference table that contains build-package ID to runtime-package ID mappings. DynamicRefTable dynamic_ref_table; }; @@ -350,7 +382,7 @@ class Theme { ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config = nullptr, uint32_t* in_out_type_spec_flags = nullptr, - uint32_t* out_last_ref = nullptr); + uint32_t* out_last_ref = nullptr) const; private: DISALLOW_COPY_AND_ASSIGN(Theme); diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 1775f5070f4e..35ae5fcd9e7b 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -41,33 +41,40 @@ class DynamicPackageEntry { int package_id = 0; }; -struct FindEntryResult { - // A pointer to the resource table entry for this resource. - // If the size of the entry is > sizeof(ResTable_entry), it can be cast to - // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry; - - // The configuration for which the resulting entry was defined. This points to a structure that - // is already swapped to host endianness. - const ResTable_config* config; - - // The bitmask of configuration axis with which the resource value varies. - uint32_t type_flags; - - // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table; - - // The string pool reference to the type's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef type_string_ref; - - // The string pool reference to the entry's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef entry_string_ref; +// TypeSpec is going to be immediately proceeded by +// an array of Type structs, all in the same block of memory. +struct TypeSpec { + // Pointer to the mmapped data where flags are kept. + // Flags denote whether the resource entry is public + // and under which configurations it varies. + const ResTable_typeSpec* type_spec; + + // Pointer to the mmapped data where the IDMAP mappings for this type + // exist. May be nullptr if no IDMAP exists. + const IdmapEntry_header* idmap_entries; + + // The number of types that follow this struct. + // There is a type for each configuration that entries are defined for. + size_t type_count; + + // Trick to easily access a variable number of Type structs + // proceeding this struct, and to ensure their alignment. + const ResTable_type* types[0]; + + inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const { + if (entry_index >= dtohl(type_spec->entryCount)) { + return 0u; + } + + const uint32_t* flags = reinterpret_cast(type_spec + 1); + return flags[entry_index]; + } }; -struct TypeSpec; -class LoadedArsc; +// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of +// ResTable_type pointers. +// TypeSpecPtr is a managed pointer that knows how to delete itself. +using TypeSpecPtr = util::unique_cptr; class LoadedPackage { public: @@ -77,9 +84,6 @@ class LoadedPackage { ~LoadedPackage(); - bool FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, - FindEntryResult* out_entry) const; - // Finds the entry with the specified type name and entry name. The names are in UTF-16 because // the underlying ResStringPool API expects this. For now this is acceptable, but since // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. @@ -87,6 +91,12 @@ class LoadedPackage { // for patching the correct package ID to the resource ID. uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; + static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index); + + static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index); + + static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset); + // Returns the string pool where type names are stored. inline const ResStringPool* GetTypeStringPool() const { return &type_string_pool_; @@ -136,14 +146,32 @@ class LoadedPackage { // before being inserted into the set. This may cause some equivalent locales to de-dupe. void CollectLocales(bool canonicalize, std::set* out_locales) const; + // type_idx is TT - 1 from 0xPPTTEEEE. + inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const { + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + return type_specs_[type_index - type_id_offset_].get(); + } + + template + void ForEachTypeSpec(Func f) const { + for (size_t i = 0; i < type_specs_.size(); i++) { + const TypeSpecPtr& ptr = type_specs_[i]; + if (ptr != nullptr) { + uint8_t type_id = ptr->type_spec->id; + if (ptr->idmap_entries != nullptr) { + type_id = ptr->idmap_entries->target_type_id; + } + f(ptr.get(), type_id - 1); + } + } + } + private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); LoadedPackage(); - bool FindEntry(const util::unique_cptr& type_spec_ptr, uint16_t entry_idx, - const ResTable_config& config, FindEntryResult* out_entry) const; - ResStringPool type_string_pool_; ResStringPool key_string_pool_; std::string package_name_; @@ -153,7 +181,7 @@ class LoadedPackage { bool system_ = false; bool overlay_ = false; - ByteBucketArray> type_specs_; + ByteBucketArray type_specs_; std::vector dynamic_package_map_; }; @@ -181,25 +209,20 @@ class LoadedArsc { return &global_string_pool_; } - // Finds the resource with ID `resid` with the best value for configuration `config`. - // The parameter `out_entry` will be filled with the resulting resource entry. - // The resource entry can be a simple entry (ResTable_entry) or a complex bag - // (ResTable_entry_map). - bool FindEntry(uint32_t resid, const ResTable_config& config, FindEntryResult* out_entry) const; + // Gets a pointer to the package with the specified package ID, or nullptr if no such package + // exists. + const LoadedPackage* GetPackageById(uint8_t package_id) const; - // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist. - const LoadedPackage* GetPackageForId(uint32_t resid) const; + // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. + inline const std::vector>& GetPackages() const { + return packages_; + } // Returns true if this is a system provided resource. inline bool IsSystem() const { return system_; } - // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. - inline const std::vector>& GetPackages() const { - return packages_; - } - private: DISALLOW_COPY_AND_ASSIGN(LoadedArsc); diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index 6c43a67e602f..e2b9f0040989 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -26,58 +26,56 @@ using ::android::base::unique_fd; using ::com::android::basic::R; +using ::testing::Eq; +using ::testing::Ge; +using ::testing::NotNull; +using ::testing::SizeIs; +using ::testing::StrEq; namespace android { TEST(ApkAssetsTest, LoadApk) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - - const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); - ASSERT_NE(nullptr, loaded_package); - - std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); + ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkFromFd) { const std::string path = GetTestDataPath() + "/basic/basic.apk"; unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY)); - ASSERT_GE(fd.get(), 0); + ASSERT_THAT(fd.get(), Ge(0)); std::unique_ptr loaded_apk = ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - - const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); - ASSERT_NE(nullptr, loaded_package); - - std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); + ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); + const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic()); loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic()); } @@ -86,19 +84,22 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { ResTable target_table; const std::string target_path = GetTestDataPath() + "/basic/basic.apk"; ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, target_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); + ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/), + Eq(NO_ERROR)); ResTable overlay_table; const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk"; ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); + ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/), + Eq(NO_ERROR)); util::unique_cptr idmap_data; void* temp_data; size_t idmap_len; - ASSERT_EQ(NO_ERROR, target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), - overlay_path.c_str(), &temp_data, &idmap_len)); + ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), + overlay_path.c_str(), &temp_data, &idmap_len), + Eq(NO_ERROR)); idmap_data.reset(temp_data); TemporaryFile tf; @@ -108,37 +109,30 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { // Open something so that the destructor of TemporaryFile closes a valid fd. tf.fd = open("/dev/null", O_WRONLY); - std::unique_ptr loaded_overlay_apk = ApkAssets::LoadOverlay(tf.path); - ASSERT_NE(nullptr, loaded_overlay_apk); + ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull()); } TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); - { - std::unique_ptr assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, assets); - } + { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } - { - std::unique_ptr assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, assets); - } + { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } } TEST(ApkAssetsTest, OpenUncompressedAssetFd) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(asset, NotNull()); off64_t start, length; unique_fd fd(asset->openFileDescriptor(&start, &length)); - EXPECT_GE(fd.get(), 0); + ASSERT_THAT(fd.get(), Ge(0)); lseek64(fd.get(), start, SEEK_SET); @@ -146,7 +140,7 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) { buffer.resize(length); ASSERT_TRUE(base::ReadFully(fd.get(), &*buffer.begin(), length)); - EXPECT_EQ("This should be uncompressed.\n\n", buffer); + EXPECT_THAT(buffer, StrEq("This should be uncompressed.\n\n")); } } // namespace android diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 37ddafb14fd3..bedebd66cb2f 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -16,6 +16,8 @@ #include "androidfw/LoadedArsc.h" +#include "androidfw/ResourceUtils.h" + #include "TestHelpers.h" #include "data/basic/R.h" #include "data/libclient/R.h" @@ -27,6 +29,13 @@ namespace basic = com::android::basic; namespace libclient = com::android::libclient; namespace sparse = com::android::sparse; +using ::testing::Eq; +using ::testing::Ge; +using ::testing::IsNull; +using ::testing::NotNull; +using ::testing::SizeIs; +using ::testing::StrEq; + namespace android { TEST(LoadedArscTest, LoadSinglePackageArsc) { @@ -35,39 +44,24 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); - - const std::vector>& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - EXPECT_EQ(std::string("com.android.app"), packages[0]->GetPackageName()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); - - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.sdkVersion = 24; - - FindEntryResult entry; + ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_TRUE(loaded_arsc->FindEntry(app::R::string::string_one, config, &entry)); - ASSERT_NE(nullptr, entry.entry); -} + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(app::R::string::string_one)); + ASSERT_THAT(package, NotNull()); + EXPECT_THAT(package->GetPackageName(), StrEq("com.android.app")); + EXPECT_THAT(package->GetPackageId(), Eq(0x7f)); -TEST(LoadedArscTest, FindDefaultEntry) { - std::string contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); + const uint8_t type_index = get_type_id(app::R::string::string_one) - 1; + const uint16_t entry_index = get_entry_id(app::R::string::string_one); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); - desired_config.language[0] = 'd'; - desired_config.language[1] = 'e'; - - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry)); - ASSERT_NE(nullptr, entry.entry); + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); } TEST(LoadedArscTest, LoadSparseEntryApp) { @@ -76,15 +70,22 @@ TEST(LoadedArscTest, LoadSparseEntryApp) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); + + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9)); + ASSERT_THAT(package, NotNull()); - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.sdkVersion = 26; + const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1; + const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9); - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(sparse::R::integer::foo_9, config, &entry)); - ASSERT_NE(nullptr, entry.entry); + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); } TEST(LoadedArscTest, LoadSharedLibrary) { @@ -93,14 +94,13 @@ TEST(LoadedArscTest, LoadSharedLibrary) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_EQ(std::string("com.android.lib_one"), packages[0]->GetPackageName()); - EXPECT_EQ(0, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.lib_one")); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0)); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); @@ -114,25 +114,23 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) { "resources.arsc", &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_FALSE(packages[0]->IsDynamic()); - EXPECT_EQ(std::string("com.android.libclient"), packages[0]->GetPackageName()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.libclient")); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); // The library has two dependencies. - ASSERT_EQ(2u, dynamic_pkg_map.size()); + ASSERT_THAT(dynamic_pkg_map, SizeIs(2u)); + EXPECT_THAT(dynamic_pkg_map[0].package_name, StrEq("com.android.lib_one")); + EXPECT_THAT(dynamic_pkg_map[0].package_id, Eq(0x02)); - EXPECT_EQ(std::string("com.android.lib_one"), dynamic_pkg_map[0].package_name); - EXPECT_EQ(0x02, dynamic_pkg_map[0].package_id); - - EXPECT_EQ(std::string("com.android.lib_two"), dynamic_pkg_map[1].package_name); - EXPECT_EQ(0x03, dynamic_pkg_map[1].package_id); + EXPECT_THAT(dynamic_pkg_map[1].package_name, StrEq("com.android.lib_two")); + EXPECT_THAT(dynamic_pkg_map[1].package_id, Eq(0x03)); } TEST(LoadedArscTest, LoadAppAsSharedLibrary) { @@ -143,13 +141,12 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, true /*load_as_shared_library*/); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); } TEST(LoadedArscTest, LoadFeatureSplit) { @@ -157,21 +154,27 @@ TEST(LoadedArscTest, LoadFeatureSplit) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc", &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(basic::R::string::test3)); + ASSERT_THAT(package, NotNull()); - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry)); + uint8_t type_index = get_type_id(basic::R::string::test3) - 1; + uint8_t entry_index = get_entry_id(basic::R::string::test3); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); size_t len; - const char16_t* type_name16 = entry.type_string_ref.string16(&len); - ASSERT_NE(nullptr, type_name16); - ASSERT_NE(0u, len); + const char16_t* type_name16 = + package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len); + ASSERT_THAT(type_name16, NotNull()); + EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string")); - std::string type_name = util::Utf16ToUtf8(StringPiece16(type_name16, len)); - EXPECT_EQ(std::string("string"), type_name); + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull()); } class MockLoadedIdmap : public LoadedIdmap { @@ -199,23 +202,33 @@ class MockLoadedIdmap : public LoadedIdmap { }; TEST(LoadedArscTest, LoadOverlay) { - std::string contents, overlay_contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); + std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc", - &overlay_contents)); + &contents)); MockLoadedIdmap loaded_idmap; std::unique_ptr loaded_arsc = - LoadedArsc::Load(StringPiece(overlay_contents), &loaded_idmap); - ASSERT_NE(nullptr, loaded_arsc); - - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); - - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(0x08030001u, desired_config, &entry)); + LoadedArsc::Load(StringPiece(contents), &loaded_idmap); + ASSERT_THAT(loaded_arsc, NotNull()); + + const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u); + ASSERT_THAT(package, NotNull()); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); + + // The entry being overlaid doesn't exist at the original entry index. + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull()); + + // Since this is an overlay, the actual entry ID must be mapped. + ASSERT_THAT(type_spec->idmap_entries, NotNull()); + uint16_t target_entry_id = 0u; + ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id)); + ASSERT_THAT(target_entry_id, Eq(0x0u)); + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull()); } // structs with size fields (like Res_value, ResTable_entry) should be diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h index 43a995536d89..df0c642f4565 100644 --- a/libs/androidfw/tests/TestHelpers.h +++ b/libs/androidfw/tests/TestHelpers.h @@ -20,6 +20,7 @@ #include #include "androidfw/ResourceTypes.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "CommonHelpers.h" -- cgit v1.2.3-59-g8ed1b From 50706b6ebc224920bceffa66baa30734de5e27ff Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Tue, 23 Jan 2018 03:16:16 -0800 Subject: Revert "libandroidfw: Improve performance of AssetManager2" This reverts commit 392132748416719e3df427e6ac8dc11af194342c. --- libs/androidfw/Android.bp | 1 - libs/androidfw/AssetManager2.cpp | 273 ++++++----------------- libs/androidfw/LoadedArsc.cpp | 261 +++++++++++++++------- libs/androidfw/include/androidfw/AssetManager2.h | 66 ++---- libs/androidfw/include/androidfw/LoadedArsc.h | 111 ++++----- libs/androidfw/tests/ApkAssets_test.cpp | 78 ++++--- libs/androidfw/tests/LoadedArsc_test.cpp | 169 +++++++------- libs/androidfw/tests/TestHelpers.h | 1 - 8 files changed, 422 insertions(+), 538 deletions(-) (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 70d52164ff74..7c9078b164a2 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -145,7 +145,6 @@ cc_test { "tests/TypeWrappers_test.cpp", "tests/ZipUtils_test.cpp", ], - static_libs: ["libgmock"], target: { android: { srcs: [ diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index a558ff7ccfc1..2fc8e952707b 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -36,31 +36,6 @@ namespace android { -struct FindEntryResult { - // A pointer to the resource table entry for this resource. - // If the size of the entry is > sizeof(ResTable_entry), it can be cast to - // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry; - - // The configuration for which the resulting entry was defined. This is already swapped to host - // endianness. - ResTable_config config; - - // The bitmask of configuration axis with which the resource value varies. - uint32_t type_flags; - - // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table; - - // The string pool reference to the type's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef type_string_ref; - - // The string pool reference to the entry's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef entry_string_ref; -}; - AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } @@ -69,7 +44,6 @@ bool AssetManager2::SetApkAssets(const std::vector& apk_assets bool invalidate_caches) { apk_assets_ = apk_assets; BuildDynamicRefTable(); - RebuildFilterList(); if (invalidate_caches) { InvalidateCaches(static_cast(-1)); } @@ -105,7 +79,7 @@ void AssetManager2::BuildDynamicRefTable() { PackageGroup* package_group = &package_groups_[idx]; // Add the package and to the set of packages with the same ID. - package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); + package_group->packages_.push_back(package.get()); package_group->cookies_.push_back(static_cast(i)); // Add the package name -> build time ID mappings. @@ -120,7 +94,7 @@ void AssetManager2::BuildDynamicRefTable() { // Now assign the runtime IDs so that we have a build-time to runtime ID map. const auto package_groups_end = package_groups_.end(); for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) { - const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName(); + const std::string& package_name = iter->packages_[0]->GetPackageName(); for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) { iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()), iter->dynamic_ref_table.mAssignedPackageId); @@ -134,20 +108,17 @@ void AssetManager2::DumpToLog() const { std::string list; for (size_t i = 0; i < package_ids_.size(); i++) { if (package_ids_[i] != 0xff) { - base::StringAppendF(&list, "%02x -> %d, ", (int)i, package_ids_[i]); + base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]); } } LOG(INFO) << "Package ID map: " << list; - for (const auto& package_group : package_groups_) { - list = ""; - for (const auto& package : package_group.packages_) { - base::StringAppendF(&list, "%s(%02x), ", package.loaded_package_->GetPackageName().c_str(), - package.loaded_package_->GetPackageId()); - } - LOG(INFO) << base::StringPrintf("PG (%02x): ", - package_group.dynamic_ref_table.mAssignedPackageId) - << list; + for (const auto& package_group: package_groups_) { + list = ""; + for (const auto& package : package_group.packages_) { + base::StringAppendF(&list, "%s(%02x), ", package->GetPackageName().c_str(), package->GetPackageId()); + } + LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list; } } @@ -186,54 +157,52 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { configuration_ = configuration; if (diff) { - RebuildFilterList(); InvalidateCaches(static_cast(diff)); } } std::set AssetManager2::GetResourceConfigurations(bool exclude_system, - bool exclude_mipmap) const { + bool exclude_mipmap) { ATRACE_CALL(); std::set configurations; for (const PackageGroup& package_group : package_groups_) { - for (const ConfiguredPackage& package : package_group.packages_) { - if (exclude_system && package.loaded_package_->IsSystem()) { + for (const LoadedPackage* package : package_group.packages_) { + if (exclude_system && package->IsSystem()) { continue; } - package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations); + package->CollectConfigurations(exclude_mipmap, &configurations); } } return configurations; } std::set AssetManager2::GetResourceLocales(bool exclude_system, - bool merge_equivalent_languages) const { + bool merge_equivalent_languages) { ATRACE_CALL(); std::set locales; for (const PackageGroup& package_group : package_groups_) { - for (const ConfiguredPackage& package : package_group.packages_) { - if (exclude_system && package.loaded_package_->IsSystem()) { + for (const LoadedPackage* package : package_group.packages_) { + if (exclude_system && package->IsSystem()) { continue; } - package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales); + package->CollectLocales(merge_equivalent_languages, &locales); } } return locales; } -std::unique_ptr AssetManager2::Open(const std::string& filename, - Asset::AccessMode mode) const { +std::unique_ptr AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, mode); } std::unique_ptr AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) const { + Asset::AccessMode mode) { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, cookie, mode); } -std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) const { +std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) { ATRACE_CALL(); std::string full_path = "assets/" + dirname; @@ -267,7 +236,7 @@ std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) con // is inconsistent for split APKs. std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie) const { + ApkAssetsCookie* out_cookie) { ATRACE_CALL(); for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) { std::unique_ptr asset = apk_assets_[i]->Open(filename, mode); @@ -286,8 +255,7 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, - ApkAssetsCookie cookie, - Asset::AccessMode mode) const { + ApkAssetsCookie cookie, Asset::AccessMode mode) { ATRACE_CALL(); if (cookie < 0 || static_cast(cookie) >= apk_assets_.size()) { return {}; @@ -296,13 +264,12 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, - bool /*stop_at_first_match*/, - FindEntryResult* out_entry) const { + bool stop_at_first_match, FindEntryResult* out_entry) { // Might use this if density_override != 0. ResTable_config density_override_config; // Select our configuration or generate a density override configuration. - const ResTable_config* desired_config = &configuration_; + ResTable_config* desired_config = &configuration_; if (density_override != 0 && density_override != configuration_.density) { density_override_config = configuration_; density_override_config.density = density_override; @@ -316,135 +283,53 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri const uint32_t package_id = get_package_id(resid); const uint8_t type_idx = get_type_id(resid) - 1; - const uint16_t entry_idx = get_entry_id(resid); + const uint16_t entry_id = get_entry_id(resid); - const uint8_t package_idx = package_ids_[package_id]; - if (package_idx == 0xff) { + const uint8_t idx = package_ids_[package_id]; + if (idx == 0xff) { LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); return kInvalidCookie; } - const PackageGroup& package_group = package_groups_[package_idx]; - const size_t package_count = package_group.packages_.size(); - + FindEntryResult best_entry; ApkAssetsCookie best_cookie = kInvalidCookie; - const LoadedPackage* best_package = nullptr; - const ResTable_type* best_type = nullptr; - const ResTable_config* best_config = nullptr; - ResTable_config best_config_copy; - uint32_t best_offset = 0u; - uint32_t type_flags = 0u; - - // If desired_config is the same as the set configuration, then we can use our filtered list - // and we don't need to match the configurations, since they already matched. - const bool use_fast_path = desired_config == &configuration_; - - for (size_t pi = 0; pi < package_count; pi++) { - const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; - const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_; - ApkAssetsCookie cookie = package_group.cookies_[pi]; - - // If the type IDs are offset in this package, we need to take that into account when searching - // for a type. - const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx); - if (UNLIKELY(type_spec == nullptr)) { - continue; - } - - uint16_t local_entry_idx = entry_idx; + uint32_t cumulated_flags = 0u; - // If there is an IDMAP supplied with this package, translate the entry ID. - if (type_spec->idmap_entries != nullptr) { - if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) { - // There is no mapping, so the resource is not meant to be in this overlay package. - continue; - } + const PackageGroup& package_group = package_groups_[idx]; + const size_t package_count = package_group.packages_.size(); + FindEntryResult current_entry; + for (size_t i = 0; i < package_count; i++) { + const LoadedPackage* loaded_package = package_group.packages_[i]; + if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry)) { + continue; } - type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx); - - // If the package is an overlay, then even configurations that are the same MUST be chosen. - const bool package_is_overlay = loaded_package->IsOverlay(); - - const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; - if (use_fast_path) { - const std::vector& candidate_configs = filtered_group.configurations; - const size_t type_count = candidate_configs.size(); - for (uint32_t i = 0; i < type_count; i++) { - const ResTable_config& this_config = candidate_configs[i]; - - // We can skip calling ResTable_config::match() because we know that all candidate - // configurations that do NOT match have been filtered-out. - if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || - (package_is_overlay && this_config.compare(*best_config) == 0)) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const ResTable_type* type_chunk = filtered_group.types[i]; - const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } - - best_cookie = cookie; - best_package = loaded_package; - best_type = type_chunk; - best_config = &this_config; - best_offset = offset; - } - } - } else { - // This is the slower path, which doesn't use the filtered list of configurations. - // Here we must read the ResTable_config from the mmapped APK, convert it to host endianness - // and fill in any new fields that did not exist when the APK was compiled. - // Furthermore when selecting configurations we can't just record the pointer to the - // ResTable_config, we must copy it. - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config this_config; - this_config.copyFromDtoH((*iter)->config); - - if (this_config.match(*desired_config)) { - if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || - (package_is_overlay && this_config.compare(*best_config) == 0)) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } + cumulated_flags |= current_entry.type_flags; - best_cookie = cookie; - best_package = loaded_package; - best_type = *iter; - best_config_copy = this_config; - best_config = &best_config_copy; - best_offset = offset; - } - } + const ResTable_config* current_config = current_entry.config; + const ResTable_config* best_config = best_entry.config; + if (best_cookie == kInvalidCookie || + current_config->isBetterThan(*best_config, desired_config) || + (loaded_package->IsOverlay() && current_config->compare(*best_config) == 0)) { + best_entry = current_entry; + best_cookie = package_group.cookies_[i]; + if (stop_at_first_match) { + break; } } } - if (UNLIKELY(best_cookie == kInvalidCookie)) { + if (best_cookie == kInvalidCookie) { return kInvalidCookie; } - const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset); - if (UNLIKELY(best_entry == nullptr)) { - return kInvalidCookie; - } - - out_entry->entry = best_entry; - out_entry->config = *best_config; - out_entry->type_flags = type_flags; - out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); - out_entry->entry_string_ref = - StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); + *out_entry = best_entry; out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; + out_entry->type_flags = cumulated_flags; return best_cookie; } -bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const { +bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { ATRACE_CALL(); FindEntryResult entry; @@ -454,8 +339,7 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons return false; } - const LoadedPackage* package = - apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); + const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid); if (package == nullptr) { return false; } @@ -483,7 +367,7 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons return true; } -bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const { +bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { FindEntryResult entry; ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); @@ -497,7 +381,7 @@ bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) const { + uint32_t* out_flags) { ATRACE_CALL(); FindEntryResult entry; @@ -516,7 +400,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Create a reference since we can't represent this complex type as a Res_value. out_value->dataType = Res_value::TYPE_REFERENCE; out_value->data = resid; - *out_selected_config = entry.config; + *out_selected_config = *entry.config; *out_flags = entry.type_flags; return cookie; } @@ -528,7 +412,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Convert the package ID to the runtime assigned package ID. entry.dynamic_ref_table->lookupResourceValue(out_value); - *out_selected_config = entry.config; + *out_selected_config = *entry.config; *out_flags = entry.type_flags; return cookie; } @@ -536,7 +420,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) const { + uint32_t* out_last_reference) { ATRACE_CALL(); constexpr const int kMaxIterations = 20; @@ -604,8 +488,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { // Attributes, arrays, etc don't have a resource id as the name. They specify // other data, which would be wrong to change via a lookup. if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, - resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); return nullptr; } } @@ -637,8 +520,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { const ResolvedBag* parent_bag = GetBag(parent_resid); if (parent_bag == nullptr) { // Failed to get the parent that should exist. - LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, - resid); + LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid); return nullptr; } @@ -657,8 +539,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t child_key = dtohl(map_entry->name.ident); if (!is_internal_resid(child_key)) { if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, - resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid); return nullptr; } } @@ -697,8 +578,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t new_key = dtohl(map_entry->name.ident); if (!is_internal_resid(new_key)) { if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, - resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); return nullptr; } } @@ -754,7 +634,7 @@ static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) { uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const std::string& fallback_type, - const std::string& fallback_package) const { + const std::string& fallback_package) { StringPiece package_name, type, entry; if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) { return 0u; @@ -786,8 +666,7 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const static std::u16string kAttrPrivate16 = u"^attr-private"; for (const PackageGroup& package_group : package_groups_) { - for (const ConfiguredPackage& package_impl : package_group.packages_) { - const LoadedPackage* package = package_impl.loaded_package_; + for (const LoadedPackage* package : package_group.packages_) { if (package_name != package->GetPackageName()) { // All packages in the same group are expected to have the same package name. break; @@ -809,32 +688,6 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, return 0u; } -void AssetManager2::RebuildFilterList() { - for (PackageGroup& group : package_groups_) { - for (ConfiguredPackage& impl : group.packages_) { - // Destroy it. - impl.filtered_configs_.~ByteBucketArray(); - - // Re-create it. - new (&impl.filtered_configs_) ByteBucketArray(); - - // Create the filters here. - impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec* spec, uint8_t type_index) { - FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_index); - const auto iter_end = spec->types + spec->type_count; - for (auto iter = spec->types; iter != iter_end; ++iter) { - ResTable_config this_config; - this_config.copyFromDtoH((*iter)->config); - if (this_config.match(configuration_)) { - group.configurations.push_back(this_config); - group.types.push_back(*iter); - } - } - }); - } - } -} - void AssetManager2::InvalidateCaches(uint32_t diff) { if (diff == 0xffffffffu) { // Everything must go. @@ -1015,7 +868,7 @@ ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_type_spec_flags, - uint32_t* out_last_ref) const { + uint32_t* out_last_ref) { if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) { uint32_t new_flags; cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags); diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 1d2c597c4c8c..e08848f891f6 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -44,6 +44,44 @@ namespace android { constexpr const static int kAppPackageId = 0x7f; +// Element of a TypeSpec array. See TypeSpec. +struct Type { + // The configuration for which this type defines entries. + // This is already converted to host endianness. + ResTable_config configuration; + + // Pointer to the mmapped data where entry definitions are kept. + const ResTable_type* type; +}; + +// TypeSpec is going to be immediately proceeded by +// an array of Type structs, all in the same block of memory. +struct TypeSpec { + // Pointer to the mmapped data where flags are kept. + // Flags denote whether the resource entry is public + // and under which configurations it varies. + const ResTable_typeSpec* type_spec; + + // Pointer to the mmapped data where the IDMAP mappings for this type + // exist. May be nullptr if no IDMAP exists. + const IdmapEntry_header* idmap_entries; + + // The number of types that follow this struct. + // There is a type for each configuration + // that entries are defined for. + size_t type_count; + + // Trick to easily access a variable number of Type structs + // proceeding this struct, and to ensure their alignment. + const Type types[0]; +}; + +// TypeSpecPtr points to the block of memory that holds +// a TypeSpec struct, followed by an array of Type structs. +// TypeSpecPtr is a managed pointer that knows how to delete +// itself. +using TypeSpecPtr = util::unique_cptr; + namespace { // Builder that helps accumulate Type structs and then create a single @@ -57,22 +95,21 @@ class TypeSpecPtrBuilder { } void AddType(const ResTable_type* type) { - types_.push_back(type); + ResTable_config config; + config.copyFromDtoH(type->config); + types_.push_back(Type{config, type}); } TypeSpecPtr Build() { // Check for overflow. - using ElementType = const ResTable_type*; - if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(ElementType) < - types_.size()) { + if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) { return {}; } - TypeSpec* type_spec = - (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType))); + TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type))); type_spec->type_spec = header_; type_spec->idmap_entries = idmap_header_; type_spec->type_count = types_.size(); - memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType)); + memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type)); return TypeSpecPtr(type_spec); } @@ -81,7 +118,7 @@ class TypeSpecPtrBuilder { const ResTable_typeSpec* header_; const IdmapEntry_header* idmap_header_; - std::vector types_; + std::vector types_; }; } // namespace @@ -125,17 +162,18 @@ static bool VerifyResTableType(const ResTable_type* header) { return true; } -static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) { +static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset, + size_t entry_idx) { // Check that the offset is aligned. if (entry_offset & 0x03) { - LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned."; + LOG(ERROR) << "Entry offset at index " << entry_idx << " is not 4-byte aligned."; return false; } // Check that the offset doesn't overflow. if (entry_offset > std::numeric_limits::max() - dtohl(type->entriesStart)) { // Overflow in offset. - LOG(ERROR) << "Entry at offset " << entry_offset << " is too large."; + LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large."; return false; } @@ -143,7 +181,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset entry_offset += dtohl(type->entriesStart); if (entry_offset > chunk_size - sizeof(ResTable_entry)) { - LOG(ERROR) << "Entry at offset " << entry_offset + LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large. No room for ResTable_entry."; return false; } @@ -153,13 +191,13 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t entry_size = dtohs(entry->size); if (entry_size < sizeof(*entry)) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset + LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx << " is too small."; return false; } if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset + LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx << " is too large."; return false; } @@ -167,7 +205,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset if (entry_size < sizeof(ResTable_map_entry)) { // There needs to be room for one Res_value struct. if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) { - LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset + LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << entry_idx << " for type " << (int)type->id << "."; return false; } @@ -176,12 +214,12 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset reinterpret_cast(reinterpret_cast(entry) + entry_size); const size_t value_size = dtohs(value->size); if (value_size < sizeof(Res_value)) { - LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small."; + LOG(ERROR) << "Res_value at index " << entry_idx << " is too small."; return false; } if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) { - LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset + LOG(ERROR) << "Res_value size " << value_size << " at index " << entry_idx << " is too large."; return false; } @@ -190,76 +228,117 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t map_entry_count = dtohl(map->count); size_t map_entries_start = entry_offset + entry_size; if (map_entries_start & 0x03) { - LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset."; + LOG(ERROR) << "Map entries at index " << entry_idx << " start at unaligned offset."; return false; } // Each entry is sizeof(ResTable_map) big. if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) { - LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << "."; + LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << entry_idx << "."; return false; } } return true; } -const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk, - uint16_t entry_index) { - uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index); - if (entry_offset == ResTable_type::NO_ENTRY) { - return nullptr; - } - return GetEntryFromOffset(type_chunk, entry_offset); -} - -uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const size_t entry_count = dtohl(type_chunk->entryCount); - const size_t offsets_offset = dtohs(type_chunk->header.headerSize); +bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_idx, + const ResTable_config& config, FindEntryResult* out_entry) const { + const ResTable_config* best_config = nullptr; + const ResTable_type* best_type = nullptr; + uint32_t best_offset = 0; + + for (uint32_t i = 0; i < type_spec_ptr->type_count; i++) { + const Type* type = &type_spec_ptr->types[i]; + const ResTable_type* type_chunk = type->type; + + if (type->configuration.match(config) && + (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const size_t entry_count = dtohl(type_chunk->entryCount); + const size_t offsets_offset = dtohs(type_chunk->header.headerSize); + + // Check if there is the desired entry in this type. + + if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { + // This is encoded as a sparse map, so perform a binary search. + const ResTable_sparseTypeEntry* sparse_indices = + reinterpret_cast( + reinterpret_cast(type_chunk) + offsets_offset); + const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; + const ResTable_sparseTypeEntry* result = + std::lower_bound(sparse_indices, sparse_indices_end, entry_idx, + [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { + return dtohs(entry.idx) < entry_idx; + }); + + if (result == sparse_indices_end || dtohs(result->idx) != entry_idx) { + // No entry found. + continue; + } - // Check if there is the desired entry in this type. + // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as + // the real offset divided by 4. + best_offset = uint32_t{dtohs(result->offset)} * 4u; + } else { + if (entry_idx >= entry_count) { + // This entry cannot be here. + continue; + } - if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { - // This is encoded as a sparse map, so perform a binary search. - const ResTable_sparseTypeEntry* sparse_indices = - reinterpret_cast( + const uint32_t* entry_offsets = reinterpret_cast( reinterpret_cast(type_chunk) + offsets_offset); - const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; - const ResTable_sparseTypeEntry* result = - std::lower_bound(sparse_indices, sparse_indices_end, entry_index, - [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { - return dtohs(entry.idx) < entry_idx; - }); - - if (result == sparse_indices_end || dtohs(result->idx) != entry_index) { - // No entry found. - return ResTable_type::NO_ENTRY; + const uint32_t offset = dtohl(entry_offsets[entry_idx]); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + // There is an entry for this resource, record it. + best_offset = offset; + } + + best_config = &type->configuration; + best_type = type_chunk; } + } - // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as - // the real offset divided by 4. - return uint32_t{dtohs(result->offset)} * 4u; + if (best_type == nullptr) { + return false; } - // This type is encoded as a dense array. - if (entry_index >= entry_count) { - // This entry cannot be here. - return ResTable_type::NO_ENTRY; + if (UNLIKELY(!VerifyResTableEntry(best_type, best_offset, entry_idx))) { + return false; } - const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type_chunk) + offsets_offset); - return dtohl(entry_offsets[entry_index]); + const ResTable_entry* best_entry = reinterpret_cast( + reinterpret_cast(best_type) + best_offset + dtohl(best_type->entriesStart)); + + const uint32_t* flags = reinterpret_cast(type_spec_ptr->type_spec + 1); + out_entry->type_flags = dtohl(flags[entry_idx]); + out_entry->entry = best_entry; + out_entry->config = best_config; + out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1); + out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index)); + return true; } -const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk, - uint32_t offset) { - if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) { - return nullptr; +bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, + FindEntryResult* out_entry) const { + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; + if (UNLIKELY(ptr == nullptr)) { + return false; } - return reinterpret_cast(reinterpret_cast(type_chunk) + - offset + dtohl(type_chunk->entriesStart)); + + // If there is an IDMAP supplied with this package, translate the entry ID. + if (ptr->idmap_entries != nullptr) { + if (!LoadedIdmap::Lookup(ptr->idmap_entries, entry_idx, &entry_idx)) { + // There is no mapping, so the resource is not meant to be in this overlay package. + return false; + } + } + return FindEntry(ptr, entry_idx, config, out_entry); } void LoadedPackage::CollectConfigurations(bool exclude_mipmap, @@ -267,7 +346,7 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, const static std::u16string kMipMap = u"mipmap"; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const TypeSpecPtr& type_spec = type_specs_[i]; + const util::unique_cptr& type_spec = type_specs_[i]; if (type_spec != nullptr) { if (exclude_mipmap) { const int type_idx = type_spec->type_spec->id - 1; @@ -288,11 +367,8 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, } } - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config config; - config.copyFromDtoH((*iter)->config); - out_configs->insert(config); + for (size_t j = 0; j < type_spec->type_count; j++) { + out_configs->insert(type_spec->types[j].configuration); } } } @@ -302,12 +378,10 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set* out char temp_locale[RESTABLE_MAX_LOCALE_LEN]; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const TypeSpecPtr& type_spec = type_specs_[i]; + const util::unique_cptr& type_spec = type_specs_[i]; if (type_spec != nullptr) { - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config configuration; - configuration.copyFromDtoH((*iter)->config); + for (size_t j = 0; j < type_spec->type_count; j++) { + const ResTable_config& configuration = type_spec->types[j].configuration; if (configuration.locale != 0) { configuration.getBcp47Locale(temp_locale, canonicalize); std::string locale(temp_locale); @@ -335,17 +409,17 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - const ResTable_type* type = *iter; - size_t entry_count = dtohl(type->entryCount); + for (size_t ti = 0; ti < type_spec->type_count; ti++) { + const Type* type = &type_spec->types[ti]; + size_t entry_count = dtohl(type->type->entryCount); for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) { const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type) + dtohs(type->header.headerSize)); + reinterpret_cast(type->type) + dtohs(type->type->header.headerSize)); const uint32_t offset = dtohl(entry_offsets[entry_idx]); if (offset != ResTable_type::NO_ENTRY) { - const ResTable_entry* entry = reinterpret_cast( - reinterpret_cast(type) + dtohl(type->entriesStart) + offset); + const ResTable_entry* entry = + reinterpret_cast(reinterpret_cast(type->type) + + dtohl(type->type->entriesStart) + offset); if (dtohl(entry->key.index) == static_cast(key_idx)) { // The package ID will be overridden by the caller (due to runtime assignment of package // IDs for shared libraries). @@ -357,7 +431,8 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } -const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { +const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const { + const uint8_t package_id = get_package_id(resid); for (const auto& loaded_package : packages_) { if (loaded_package->GetPackageId() == package_id) { return loaded_package.get(); @@ -605,6 +680,26 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, return std::move(loaded_package); } +bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, + FindEntryResult* out_entry) const { + ATRACE_CALL(); + + const uint8_t package_id = get_package_id(resid); + const uint8_t type_id = get_type_id(resid); + const uint16_t entry_id = get_entry_id(resid); + + if (UNLIKELY(type_id == 0)) { + LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); + return false; + } + + for (const auto& loaded_package : packages_) { + if (loaded_package->GetPackageId() == package_id) { + return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry); + } + } + return false; +} bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library) { diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index ef08897d997a..b033137b4764 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -69,8 +69,6 @@ struct ResolvedBag { Entry entries[0]; }; -struct FindEntryResult; - // AssetManager2 is the main entry point for accessing assets and resources. // AssetManager2 provides caching of resources retrieved via the underlying ApkAssets. class AssetManager2 { @@ -129,7 +127,7 @@ class AssetManager2 { // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap' // will be excluded from the list. std::set GetResourceConfigurations(bool exclude_system = false, - bool exclude_mipmap = false) const; + bool exclude_mipmap = false); // Returns all the locales for which there are resources defined. This includes resource // locales in all the ApkAssets set for this AssetManager. @@ -138,24 +136,24 @@ class AssetManager2 { // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized // and de-duped in the resulting list. std::set GetResourceLocales(bool exclude_system = false, - bool merge_equivalent_languages = false) const; + bool merge_equivalent_languages = false); // Searches the set of APKs loaded by this AssetManager and opens the first one found located // in the assets/ directory. // `mode` controls how the file is opened. // // NOTE: The loaded APKs are searched in reverse order. - std::unique_ptr Open(const std::string& filename, Asset::AccessMode mode) const; + std::unique_ptr Open(const std::string& filename, Asset::AccessMode mode); // Opens a file within the assets/ directory of the APK specified by `cookie`. // `mode` controls how the file is opened. std::unique_ptr Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) const; + Asset::AccessMode mode); // Opens the directory specified by `dirname`. The result is an AssetDir that is the combination // of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded. // The entries are sorted by their ASCII name. - std::unique_ptr OpenDir(const std::string& dirname) const; + std::unique_ptr OpenDir(const std::string& dirname); // Searches the set of APKs loaded by this AssetManager and opens the first one found. // `mode` controls how the file is opened. @@ -163,24 +161,24 @@ class AssetManager2 { // // NOTE: The loaded APKs are searched in reverse order. std::unique_ptr OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie = nullptr) const; + ApkAssetsCookie* out_cookie = nullptr); // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened. // This is typically used to open a specific AndroidManifest.xml, or a binary XML file // referenced by a resource lookup with GetResource(). std::unique_ptr OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) const; + Asset::AccessMode mode); // Populates the `out_name` parameter with resource name information. // Utf8 strings are preferred, and only if they are unavailable are // the Utf16 variants populated. // Returns false if the resource was not found or the name was missing/corrupt. - bool GetResourceName(uint32_t resid, ResourceName* out_name) const; + bool GetResourceName(uint32_t resid, ResourceName* out_name); // Populates `out_flags` with the bitmask of configuration axis that this resource varies with. // See ResTable_config for the list of configuration axis. // Returns false if the resource was not found. - bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const; + bool GetResourceFlags(uint32_t resid, uint32_t* out_flags); // Finds the resource ID assigned to `resource_name`. // `resource_name` must be of the form '[package:][type/]entry'. @@ -188,7 +186,7 @@ class AssetManager2 { // If no type is specified in `resource_name`, then `fallback_type` is used as the type. // Returns 0x0 if no resource by that name was found. uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {}, - const std::string& fallback_package = {}) const; + const std::string& fallback_package = {}); // Retrieves the best matching resource with ID `resid`. The resource value is filled into // `out_value` and the configuration for the selected value is populated in `out_selected_config`. @@ -201,7 +199,7 @@ class AssetManager2 { // this function logs if the resource was a map/bag type before returning kInvalidCookie. ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) const; + uint32_t* out_flags); // Resolves the resource reference in `in_out_value` if the data type is // Res_value::TYPE_REFERENCE. @@ -217,7 +215,7 @@ class AssetManager2 { // it was not found. ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) const; + uint32_t* out_last_reference); // Retrieves the best matching bag/map resource with ID `resid`. // This method will resolve all parent references for this bag and merge keys with the child. @@ -235,9 +233,9 @@ class AssetManager2 { std::unique_ptr NewTheme(); template - void ForEachPackage(Func func) const { + void ForEachPackage(Func func) { for (const PackageGroup& package_group : package_groups_) { - func(package_group.packages_.front().loaded_package_->GetPackageName(), + func(package_group.packages_.front()->GetPackageName(), package_group.dynamic_ref_table.mAssignedPackageId); } } @@ -262,7 +260,7 @@ class AssetManager2 { // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds. ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, - FindEntryResult* out_entry) const; + FindEntryResult* out_entry); // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. @@ -272,43 +270,13 @@ class AssetManager2 { // bitmask `diff`. void InvalidateCaches(uint32_t diff); - // Triggers the re-construction of lists of types that match the set configuration. - // This should always be called when mutating the AssetManager's configuration or ApkAssets set. - void RebuildFilterList(); - // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector apk_assets_; - // A collection of configurations and their associated ResTable_type that match the current - // AssetManager configuration. - struct FilteredConfigGroup { - std::vector configurations; - std::vector types; - }; - - // Represents an single package. - struct ConfiguredPackage { - // A pointer to the immutable, loaded package info. - const LoadedPackage* loaded_package_; - - // A mutable AssetManager-specific list of configurations that match the AssetManager's - // current configuration. This is used as an optimization to avoid checking every single - // candidate configuration when looking up resources. - ByteBucketArray filtered_configs_; - }; - - // Represents a logical package, which can be made up of many individual packages. Each package - // in a PackageGroup shares the same package name and package ID. struct PackageGroup { - // The set of packages that make-up this group. - std::vector packages_; - - // The cookies associated with each package in the group. They share the same order as - // packages_. + std::vector packages_; std::vector cookies_; - - // A library reference table that contains build-package ID to runtime-package ID mappings. DynamicRefTable dynamic_ref_table; }; @@ -382,7 +350,7 @@ class Theme { ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config = nullptr, uint32_t* in_out_type_spec_flags = nullptr, - uint32_t* out_last_ref = nullptr) const; + uint32_t* out_last_ref = nullptr); private: DISALLOW_COPY_AND_ASSIGN(Theme); diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 35ae5fcd9e7b..1775f5070f4e 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -41,40 +41,33 @@ class DynamicPackageEntry { int package_id = 0; }; -// TypeSpec is going to be immediately proceeded by -// an array of Type structs, all in the same block of memory. -struct TypeSpec { - // Pointer to the mmapped data where flags are kept. - // Flags denote whether the resource entry is public - // and under which configurations it varies. - const ResTable_typeSpec* type_spec; - - // Pointer to the mmapped data where the IDMAP mappings for this type - // exist. May be nullptr if no IDMAP exists. - const IdmapEntry_header* idmap_entries; - - // The number of types that follow this struct. - // There is a type for each configuration that entries are defined for. - size_t type_count; - - // Trick to easily access a variable number of Type structs - // proceeding this struct, and to ensure their alignment. - const ResTable_type* types[0]; - - inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const { - if (entry_index >= dtohl(type_spec->entryCount)) { - return 0u; - } - - const uint32_t* flags = reinterpret_cast(type_spec + 1); - return flags[entry_index]; - } +struct FindEntryResult { + // A pointer to the resource table entry for this resource. + // If the size of the entry is > sizeof(ResTable_entry), it can be cast to + // a ResTable_map_entry and processed as a bag/map. + const ResTable_entry* entry; + + // The configuration for which the resulting entry was defined. This points to a structure that + // is already swapped to host endianness. + const ResTable_config* config; + + // The bitmask of configuration axis with which the resource value varies. + uint32_t type_flags; + + // The dynamic package ID map for the package from which this resource came from. + const DynamicRefTable* dynamic_ref_table; + + // The string pool reference to the type's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef type_string_ref; + + // The string pool reference to the entry's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef entry_string_ref; }; -// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of -// ResTable_type pointers. -// TypeSpecPtr is a managed pointer that knows how to delete itself. -using TypeSpecPtr = util::unique_cptr; +struct TypeSpec; +class LoadedArsc; class LoadedPackage { public: @@ -84,6 +77,9 @@ class LoadedPackage { ~LoadedPackage(); + bool FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, + FindEntryResult* out_entry) const; + // Finds the entry with the specified type name and entry name. The names are in UTF-16 because // the underlying ResStringPool API expects this. For now this is acceptable, but since // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. @@ -91,12 +87,6 @@ class LoadedPackage { // for patching the correct package ID to the resource ID. uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; - static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index); - - static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index); - - static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset); - // Returns the string pool where type names are stored. inline const ResStringPool* GetTypeStringPool() const { return &type_string_pool_; @@ -146,32 +136,14 @@ class LoadedPackage { // before being inserted into the set. This may cause some equivalent locales to de-dupe. void CollectLocales(bool canonicalize, std::set* out_locales) const; - // type_idx is TT - 1 from 0xPPTTEEEE. - inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const { - // If the type IDs are offset in this package, we need to take that into account when searching - // for a type. - return type_specs_[type_index - type_id_offset_].get(); - } - - template - void ForEachTypeSpec(Func f) const { - for (size_t i = 0; i < type_specs_.size(); i++) { - const TypeSpecPtr& ptr = type_specs_[i]; - if (ptr != nullptr) { - uint8_t type_id = ptr->type_spec->id; - if (ptr->idmap_entries != nullptr) { - type_id = ptr->idmap_entries->target_type_id; - } - f(ptr.get(), type_id - 1); - } - } - } - private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); LoadedPackage(); + bool FindEntry(const util::unique_cptr& type_spec_ptr, uint16_t entry_idx, + const ResTable_config& config, FindEntryResult* out_entry) const; + ResStringPool type_string_pool_; ResStringPool key_string_pool_; std::string package_name_; @@ -181,7 +153,7 @@ class LoadedPackage { bool system_ = false; bool overlay_ = false; - ByteBucketArray type_specs_; + ByteBucketArray> type_specs_; std::vector dynamic_package_map_; }; @@ -209,20 +181,25 @@ class LoadedArsc { return &global_string_pool_; } - // Gets a pointer to the package with the specified package ID, or nullptr if no such package - // exists. - const LoadedPackage* GetPackageById(uint8_t package_id) const; + // Finds the resource with ID `resid` with the best value for configuration `config`. + // The parameter `out_entry` will be filled with the resulting resource entry. + // The resource entry can be a simple entry (ResTable_entry) or a complex bag + // (ResTable_entry_map). + bool FindEntry(uint32_t resid, const ResTable_config& config, FindEntryResult* out_entry) const; - // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. - inline const std::vector>& GetPackages() const { - return packages_; - } + // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist. + const LoadedPackage* GetPackageForId(uint32_t resid) const; // Returns true if this is a system provided resource. inline bool IsSystem() const { return system_; } + // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. + inline const std::vector>& GetPackages() const { + return packages_; + } + private: DISALLOW_COPY_AND_ASSIGN(LoadedArsc); diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index e2b9f0040989..6c43a67e602f 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -26,56 +26,58 @@ using ::android::base::unique_fd; using ::com::android::basic::R; -using ::testing::Eq; -using ::testing::Ge; -using ::testing::NotNull; -using ::testing::SizeIs; -using ::testing::StrEq; namespace android { TEST(ApkAssetsTest, LoadApk) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); - ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); + ASSERT_NE(nullptr, loaded_arsc); + + const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); + ASSERT_NE(nullptr, loaded_package); + + std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); + ASSERT_NE(nullptr, asset); } TEST(ApkAssetsTest, LoadApkFromFd) { const std::string path = GetTestDataPath() + "/basic/basic.apk"; unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY)); - ASSERT_THAT(fd.get(), Ge(0)); + ASSERT_GE(fd.get(), 0); std::unique_ptr loaded_apk = ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); - ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); + ASSERT_NE(nullptr, loaded_arsc); + + const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); + ASSERT_NE(nullptr, loaded_package); + + std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); + ASSERT_NE(nullptr, asset); } TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_THAT(loaded_apk, NotNull()); - + ASSERT_NE(nullptr, loaded_apk); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); + ASSERT_NE(nullptr, loaded_arsc); + ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic()); loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); + ASSERT_NE(nullptr, loaded_arsc); + ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic()); } @@ -84,22 +86,19 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { ResTable target_table; const std::string target_path = GetTestDataPath() + "/basic/basic.apk"; ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents)); - ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/), - Eq(NO_ERROR)); + ASSERT_EQ(NO_ERROR, target_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); ResTable overlay_table; const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk"; ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents)); - ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/), - Eq(NO_ERROR)); + ASSERT_EQ(NO_ERROR, overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); util::unique_cptr idmap_data; void* temp_data; size_t idmap_len; - ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), - overlay_path.c_str(), &temp_data, &idmap_len), - Eq(NO_ERROR)); + ASSERT_EQ(NO_ERROR, target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), + overlay_path.c_str(), &temp_data, &idmap_len)); idmap_data.reset(temp_data); TemporaryFile tf; @@ -109,30 +108,37 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { // Open something so that the destructor of TemporaryFile closes a valid fd. tf.fd = open("/dev/null", O_WRONLY); - ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull()); + std::unique_ptr loaded_overlay_apk = ApkAssets::LoadOverlay(tf.path); + ASSERT_NE(nullptr, loaded_overlay_apk); } TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); - { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } + { + std::unique_ptr assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); + ASSERT_NE(nullptr, assets); + } - { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } + { + std::unique_ptr assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); + ASSERT_NE(nullptr, assets); + } } TEST(ApkAssetsTest, OpenUncompressedAssetFd) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN); - ASSERT_THAT(asset, NotNull()); + ASSERT_NE(nullptr, asset); off64_t start, length; unique_fd fd(asset->openFileDescriptor(&start, &length)); - ASSERT_THAT(fd.get(), Ge(0)); + EXPECT_GE(fd.get(), 0); lseek64(fd.get(), start, SEEK_SET); @@ -140,7 +146,7 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) { buffer.resize(length); ASSERT_TRUE(base::ReadFully(fd.get(), &*buffer.begin(), length)); - EXPECT_THAT(buffer, StrEq("This should be uncompressed.\n\n")); + EXPECT_EQ("This should be uncompressed.\n\n", buffer); } } // namespace android diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index bedebd66cb2f..37ddafb14fd3 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -16,8 +16,6 @@ #include "androidfw/LoadedArsc.h" -#include "androidfw/ResourceUtils.h" - #include "TestHelpers.h" #include "data/basic/R.h" #include "data/libclient/R.h" @@ -29,13 +27,6 @@ namespace basic = com::android::basic; namespace libclient = com::android::libclient; namespace sparse = com::android::sparse; -using ::testing::Eq; -using ::testing::Ge; -using ::testing::IsNull; -using ::testing::NotNull; -using ::testing::SizeIs; -using ::testing::StrEq; - namespace android { TEST(LoadedArscTest, LoadSinglePackageArsc) { @@ -44,24 +35,39 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); + + const std::vector>& packages = loaded_arsc->GetPackages(); + ASSERT_EQ(1u, packages.size()); + EXPECT_EQ(std::string("com.android.app"), packages[0]->GetPackageName()); + EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.sdkVersion = 24; + + FindEntryResult entry; - const LoadedPackage* package = - loaded_arsc->GetPackageById(get_package_id(app::R::string::string_one)); - ASSERT_THAT(package, NotNull()); - EXPECT_THAT(package->GetPackageName(), StrEq("com.android.app")); - EXPECT_THAT(package->GetPackageId(), Eq(0x7f)); + ASSERT_TRUE(loaded_arsc->FindEntry(app::R::string::string_one, config, &entry)); + ASSERT_NE(nullptr, entry.entry); +} - const uint8_t type_index = get_type_id(app::R::string::string_one) - 1; - const uint16_t entry_index = get_entry_id(app::R::string::string_one); +TEST(LoadedArscTest, FindDefaultEntry) { + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + ASSERT_NE(nullptr, loaded_arsc); - const ResTable_type* type = type_spec->types[0]; - ASSERT_THAT(type, NotNull()); - ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + desired_config.language[0] = 'd'; + desired_config.language[1] = 'e'; + + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry)); + ASSERT_NE(nullptr, entry.entry); } TEST(LoadedArscTest, LoadSparseEntryApp) { @@ -70,22 +76,15 @@ TEST(LoadedArscTest, LoadSparseEntryApp) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); - - const LoadedPackage* package = - loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9)); - ASSERT_THAT(package, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); - const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1; - const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9); + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.sdkVersion = 26; - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - - const ResTable_type* type = type_spec->types[0]; - ASSERT_THAT(type, NotNull()); - ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(sparse::R::integer::foo_9, config, &entry)); + ASSERT_NE(nullptr, entry.entry); } TEST(LoadedArscTest, LoadSharedLibrary) { @@ -94,13 +93,14 @@ TEST(LoadedArscTest, LoadSharedLibrary) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_THAT(packages, SizeIs(1u)); + ASSERT_EQ(1u, packages.size()); + EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.lib_one")); - EXPECT_THAT(packages[0]->GetPackageId(), Eq(0)); + EXPECT_EQ(std::string("com.android.lib_one"), packages[0]->GetPackageName()); + EXPECT_EQ(0, packages[0]->GetPackageId()); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); @@ -114,23 +114,25 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) { "resources.arsc", &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_THAT(packages, SizeIs(1u)); + ASSERT_EQ(1u, packages.size()); + EXPECT_FALSE(packages[0]->IsDynamic()); - EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.libclient")); - EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); + EXPECT_EQ(std::string("com.android.libclient"), packages[0]->GetPackageName()); + EXPECT_EQ(0x7f, packages[0]->GetPackageId()); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); // The library has two dependencies. - ASSERT_THAT(dynamic_pkg_map, SizeIs(2u)); - EXPECT_THAT(dynamic_pkg_map[0].package_name, StrEq("com.android.lib_one")); - EXPECT_THAT(dynamic_pkg_map[0].package_id, Eq(0x02)); + ASSERT_EQ(2u, dynamic_pkg_map.size()); - EXPECT_THAT(dynamic_pkg_map[1].package_name, StrEq("com.android.lib_two")); - EXPECT_THAT(dynamic_pkg_map[1].package_id, Eq(0x03)); + EXPECT_EQ(std::string("com.android.lib_one"), dynamic_pkg_map[0].package_name); + EXPECT_EQ(0x02, dynamic_pkg_map[0].package_id); + + EXPECT_EQ(std::string("com.android.lib_two"), dynamic_pkg_map[1].package_name); + EXPECT_EQ(0x03, dynamic_pkg_map[1].package_id); } TEST(LoadedArscTest, LoadAppAsSharedLibrary) { @@ -141,12 +143,13 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, true /*load_as_shared_library*/); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_THAT(packages, SizeIs(1u)); + ASSERT_EQ(1u, packages.size()); + EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); + EXPECT_EQ(0x7f, packages[0]->GetPackageId()); } TEST(LoadedArscTest, LoadFeatureSplit) { @@ -154,27 +157,21 @@ TEST(LoadedArscTest, LoadFeatureSplit) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc", &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); - const LoadedPackage* package = - loaded_arsc->GetPackageById(get_package_id(basic::R::string::test3)); - ASSERT_THAT(package, NotNull()); + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); - uint8_t type_index = get_type_id(basic::R::string::test3) - 1; - uint8_t entry_index = get_entry_id(basic::R::string::test3); - - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry)); size_t len; - const char16_t* type_name16 = - package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len); - ASSERT_THAT(type_name16, NotNull()); - EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string")); + const char16_t* type_name16 = entry.type_string_ref.string16(&len); + ASSERT_NE(nullptr, type_name16); + ASSERT_NE(0u, len); - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull()); + std::string type_name = util::Utf16ToUtf8(StringPiece16(type_name16, len)); + EXPECT_EQ(std::string("string"), type_name); } class MockLoadedIdmap : public LoadedIdmap { @@ -202,33 +199,23 @@ class MockLoadedIdmap : public LoadedIdmap { }; TEST(LoadedArscTest, LoadOverlay) { - std::string contents; + std::string contents, overlay_contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc", - &contents)); + &overlay_contents)); MockLoadedIdmap loaded_idmap; std::unique_ptr loaded_arsc = - LoadedArsc::Load(StringPiece(contents), &loaded_idmap); - ASSERT_THAT(loaded_arsc, NotNull()); - - const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u); - ASSERT_THAT(package, NotNull()); - - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); - - // The entry being overlaid doesn't exist at the original entry index. - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull()); - - // Since this is an overlay, the actual entry ID must be mapped. - ASSERT_THAT(type_spec->idmap_entries, NotNull()); - uint16_t target_entry_id = 0u; - ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id)); - ASSERT_THAT(target_entry_id, Eq(0x0u)); - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull()); + LoadedArsc::Load(StringPiece(overlay_contents), &loaded_idmap); + ASSERT_NE(nullptr, loaded_arsc); + + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(0x08030001u, desired_config, &entry)); } // structs with size fields (like Res_value, ResTable_entry) should be diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h index df0c642f4565..43a995536d89 100644 --- a/libs/androidfw/tests/TestHelpers.h +++ b/libs/androidfw/tests/TestHelpers.h @@ -20,7 +20,6 @@ #include #include "androidfw/ResourceTypes.h" -#include "gmock/gmock.h" #include "gtest/gtest.h" #include "CommonHelpers.h" -- cgit v1.2.3-59-g8ed1b From 7fb38311361390e24d7e43ce1eb220faccd251ff Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Tue, 23 Jan 2018 03:17:26 -0800 Subject: Revert "Replace AssetManager with AssetManager2 implementation" This reverts commit b20a0ce59f59cb5ec857748e056cc341dbd13b92. --- core/java/android/content/pm/PackageParser.java | 19 +- core/java/android/content/res/ApkAssets.java | 221 -- core/java/android/content/res/AssetManager.java | 1302 ++++----- core/java/android/content/res/Resources.java | 9 +- core/java/android/content/res/ResourcesImpl.java | 22 +- core/java/android/content/res/TypedArray.java | 185 +- core/java/android/content/res/XmlBlock.java | 8 +- core/jni/Android.bp | 3 +- core/jni/AndroidRuntime.cpp | 2 - core/jni/android/graphics/FontFamily.cpp | 29 +- core/jni/android_app_NativeActivity.cpp | 2 +- core/jni/android_content_res_ApkAssets.cpp | 150 - core/jni/android_util_AssetManager.cpp | 2884 +++++++++++--------- .../android_runtime/android_util_AssetManager.h | 15 +- libs/androidfw/AssetManager2.cpp | 6 +- libs/androidfw/AttributeResolution.cpp | 263 +- libs/androidfw/LoadedArsc.cpp | 2 + libs/androidfw/include/androidfw/AttributeFinder.h | 6 - .../include/androidfw/AttributeResolution.h | 11 +- libs/androidfw/include/androidfw/LoadedArsc.h | 13 +- libs/androidfw/include/androidfw/MutexGuard.h | 101 - libs/androidfw/tests/AttributeResolution_test.cpp | 39 +- libs/androidfw/tests/BenchmarkHelpers.cpp | 4 +- native/android/asset_manager.cpp | 28 +- rs/jni/android_renderscript_RenderScript.cpp | 30 +- 25 files changed, 2493 insertions(+), 2861 deletions(-) delete mode 100644 core/java/android/content/res/ApkAssets.java delete mode 100644 core/jni/android_content_res_ApkAssets.cpp delete mode 100644 libs/androidfw/include/androidfw/MutexGuard.h (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 850927400aa1..4a71467f4b36 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -54,7 +54,6 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper; import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.SplitAssetDependencyLoader; import android.content.pm.split.SplitAssetLoader; -import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -1576,19 +1575,21 @@ public class PackageParser { int flags) throws PackageParserException { final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); - ApkAssets apkAssets = null; + AssetManager assets = null; XmlResourceParser parser = null; try { - try { - apkAssets = fd != null - ? ApkAssets.loadFromFd(fd, debugPathName, false, false) - : ApkAssets.loadFromPath(apkPath); - } catch (IOException e) { + assets = newConfiguredAssetManager(); + int cookie = fd != null + ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath); + if (cookie == 0) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse " + apkPath); } - parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME); + final DisplayMetrics metrics = new DisplayMetrics(); + metrics.setToDefaults(); + + parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); final SigningDetails signingDetails; if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) { @@ -1614,7 +1615,7 @@ public class PackageParser { "Failed to parse " + apkPath, e); } finally { IoUtils.closeQuietly(parser); - IoUtils.closeQuietly(apkAssets); + IoUtils.closeQuietly(assets); } } diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java deleted file mode 100644 index b087c48d8d4c..000000000000 --- a/core/java/android/content/res/ApkAssets.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.content.res; - -import android.annotation.NonNull; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.Preconditions; - -import java.io.FileDescriptor; -import java.io.IOException; - -/** - * The loaded, immutable, in-memory representation of an APK. - * - * The main implementation is native C++ and there is very little API surface exposed here. The APK - * is mainly accessed via {@link AssetManager}. - * - * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers, - * making the creation of AssetManagers very cheap. - * @hide - */ -public final class ApkAssets implements AutoCloseable { - @GuardedBy("this") private long mNativePtr; - @GuardedBy("this") private StringBlock mStringBlock; - - /** - * Creates a new ApkAssets instance from the given path on disk. - * - * @param path The path to an APK on disk. - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException { - return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/); - } - - /** - * Creates a new ApkAssets instance from the given path on disk. - * - * @param path The path to an APK on disk. - * @param system When true, the APK is loaded as a system APK (framework). - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system) - throws IOException { - return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/); - } - - /** - * Creates a new ApkAssets instance from the given path on disk. - * - * @param path The path to an APK on disk. - * @param system When true, the APK is loaded as a system APK (framework). - * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are - * loaded as a shared library. - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system, - boolean forceSharedLibrary) throws IOException { - return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/); - } - - /** - * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications. - * - * Performs a dup of the underlying fd, so you must take care of still closing - * the FileDescriptor yourself (and can do that whenever you want). - * - * @param fd The FileDescriptor of an open, readable APK. - * @param friendlyName The friendly name used to identify this ApkAssets when logging. - * @param system When true, the APK is loaded as a system APK (framework). - * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are - * loaded as a shared library. - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd, - @NonNull String friendlyName, boolean system, boolean forceSharedLibrary) - throws IOException { - return new ApkAssets(fd, friendlyName, system, forceSharedLibrary); - } - - /** - * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path - * is encoded within the IDMAP. - * - * @param idmapPath Path to the IDMAP of an overlay APK. - * @param system When true, the APK is loaded as a system APK (framework). - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system) - throws IOException { - return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/); - } - - private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) - throws IOException { - Preconditions.checkNotNull(path, "path"); - mNativePtr = nativeLoad(path, system, forceSharedLib, overlay); - mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); - } - - private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system, - boolean forceSharedLib) throws IOException { - Preconditions.checkNotNull(fd, "fd"); - Preconditions.checkNotNull(friendlyName, "friendlyName"); - mNativePtr = nativeLoadFromFd(fd, friendlyName, system, forceSharedLib); - mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); - } - - @NonNull String getAssetPath() { - synchronized (this) { - ensureValidLocked(); - return nativeGetAssetPath(mNativePtr); - } - } - - CharSequence getStringFromPool(int idx) { - synchronized (this) { - ensureValidLocked(); - return mStringBlock.get(idx); - } - } - - /** - * Retrieve a parser for a compiled XML file. This is associated with a single APK and - * NOT a full AssetManager. This means that shared-library references will not be - * dynamically assigned runtime package IDs. - * - * @param fileName The path to the file within the APK. - * @return An XmlResourceParser. - * @throws IOException if the file was not found or an error occurred retrieving it. - */ - public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException { - Preconditions.checkNotNull(fileName, "fileName"); - synchronized (this) { - ensureValidLocked(); - long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName); - try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) { - XmlResourceParser parser = block.newParser(); - // If nativeOpenXml doesn't throw, it will always return a valid native pointer, - // which makes newParser always return non-null. But let's be paranoid. - if (parser == null) { - throw new AssertionError("block.newParser() returned a null parser"); - } - return parser; - } - } - } - - /** - * Returns false if the underlying APK was changed since this ApkAssets was loaded. - */ - public boolean isUpToDate() { - synchronized (this) { - ensureValidLocked(); - return nativeIsUpToDate(mNativePtr); - } - } - - /** - * Closes the ApkAssets and destroys the underlying native implementation. Further use of the - * ApkAssets object will cause exceptions to be thrown. - * - * Calling close on an already closed ApkAssets does nothing. - */ - @Override - public void close() { - synchronized (this) { - if (mNativePtr == 0) { - return; - } - - mStringBlock = null; - nativeDestroy(mNativePtr); - mNativePtr = 0; - } - } - - @Override - protected void finalize() throws Throwable { - if (mNativePtr != 0) { - nativeDestroy(mNativePtr); - } - } - - private void ensureValidLocked() { - if (mNativePtr == 0) { - throw new RuntimeException("ApkAssets is closed"); - } - } - - private static native long nativeLoad( - @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) - throws IOException; - private static native long nativeLoadFromFd(@NonNull FileDescriptor fd, - @NonNull String friendlyName, boolean system, boolean forceSharedLib) - throws IOException; - private static native void nativeDestroy(long ptr); - private static native @NonNull String nativeGetAssetPath(long ptr); - private static native long nativeGetStringBlock(long ptr); - private static native boolean nativeIsUpToDate(long ptr); - private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException; -} diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 4f614a73a521..78665609bdd4 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -18,11 +18,9 @@ package android.content.res; import android.annotation.AnyRes; import android.annotation.ArrayRes; -import android.annotation.AttrRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; -import android.annotation.StyleRes; import android.content.pm.ActivityInfo; import android.content.res.Configuration.NativeConfig; import android.os.ParcelFileDescriptor; @@ -30,18 +28,10 @@ import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.Preconditions; - -import java.io.BufferedReader; -import java.io.FileInputStream; +import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.channels.FileLock; -import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; /** @@ -52,17 +42,7 @@ import java.util.HashMap; * bytes. */ public final class AssetManager implements AutoCloseable { - private static final String TAG = "AssetManager"; - private static final boolean DEBUG_REFS = false; - - private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk"; - - private static final Object sSync = new Object(); - - // Not private for LayoutLib's BridgeAssetManager. - @GuardedBy("sSync") static AssetManager sSystem = null; - - @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0]; + /* modes used when opening an asset */ /** * Mode for {@link #open(String, int)}: no specific information about how @@ -85,282 +65,90 @@ public final class AssetManager implements AutoCloseable { */ public static final int ACCESS_BUFFER = 3; - @GuardedBy("this") private final TypedValue mValue = new TypedValue(); - @GuardedBy("this") private final long[] mOffsets = new long[2]; - - // Pointer to native implementation, stuffed inside a long. - @GuardedBy("this") private long mObject; - - // The loaded asset paths. - @GuardedBy("this") private ApkAssets[] mApkAssets; + private static final String TAG = "AssetManager"; + private static final boolean localLOGV = false || false; + + private static final boolean DEBUG_REFS = false; + + private static final Object sSync = new Object(); + /*package*/ static AssetManager sSystem = null; - // Debug/reference counting implementation. - @GuardedBy("this") private boolean mOpen = true; - @GuardedBy("this") private int mNumRefs = 1; - @GuardedBy("this") private HashMap mRefStacks; + private final TypedValue mValue = new TypedValue(); + private final long[] mOffsets = new long[2]; + + // For communication with native code. + private long mObject; + private StringBlock mStringBlocks[] = null; + + private int mNumRefs = 1; + private boolean mOpen = true; + private HashMap mRefStacks; + /** * Create a new AssetManager containing only the basic system assets. * Applications will not generally use this method, instead retrieving the * appropriate asset manager with {@link Resources#getAssets}. Not for * use by applications. - * @hide + * {@hide} */ public AssetManager() { - final ApkAssets[] assets; - synchronized (sSync) { - createSystemAssetsInZygoteLocked(); - assets = sSystemApkAssets; - } - - mObject = nativeCreate(); - if (DEBUG_REFS) { - mNumRefs = 0; - incRefsLocked(hashCode()); + synchronized (this) { + if (DEBUG_REFS) { + mNumRefs = 0; + incRefsLocked(this.hashCode()); + } + init(false); + if (localLOGV) Log.v(TAG, "New asset manager: " + this); + ensureSystemAssets(); } - - // Always set the framework resources. - setApkAssets(assets, false /*invalidateCaches*/); } - /** - * Private constructor that doesn't call ensureSystemAssets. - * Used for the creation of system assets. - */ - @SuppressWarnings("unused") - private AssetManager(boolean sentinel) { - mObject = nativeCreate(); - if (DEBUG_REFS) { - mNumRefs = 0; - incRefsLocked(hashCode()); + private static void ensureSystemAssets() { + synchronized (sSync) { + if (sSystem == null) { + AssetManager system = new AssetManager(true); + system.makeStringBlocks(null); + sSystem = system; + } } } - - /** - * This must be called from Zygote so that system assets are shared by all applications. - * @hide - */ - private static void createSystemAssetsInZygoteLocked() { - if (sSystem != null) { - return; - } - - // Make sure that all IDMAPs are up to date. - nativeVerifySystemIdmaps(); - - try { - ArrayList apkAssets = new ArrayList<>(); - apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/)); - - // Load all static RROs. - try (FileInputStream fis = new FileInputStream( - "/data/resource-cache/overlays.list"); - BufferedReader br = new BufferedReader(new InputStreamReader(fis))) { - // Acquire a lock so that any idmap scanning doesn't impact the current set. - try (FileLock flock = fis.getChannel().lock(0, Long.MAX_VALUE, - true /*shared*/)) { - for (String line; (line = br.readLine()) != null; ) { - String idmapPath = line.split(" ")[1]; - apkAssets.add( - ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/)); - } - } + + private AssetManager(boolean isSystem) { + if (DEBUG_REFS) { + synchronized (this) { + mNumRefs = 0; + incRefsLocked(this.hashCode()); } - - sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]); - sSystem = new AssetManager(true /*sentinel*/); - sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/); - } catch (IOException e) { - throw new IllegalStateException("Failed to create system AssetManager", e); } + init(true); + if (localLOGV) Log.v(TAG, "New asset manager: " + this); } /** * Return a global shared asset manager that provides access to only * system assets (no application assets). - * @hide + * {@hide} */ public static AssetManager getSystem() { - synchronized (sSync) { - createSystemAssetsInZygoteLocked(); - return sSystem; - } + ensureSystemAssets(); + return sSystem; } /** * Close this asset manager. */ - @Override public void close() { - synchronized (this) { - if (!mOpen) { - return; - } - - mOpen = false; - decRefsLocked(hashCode()); - } - } - - /** - * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)} - * family of methods. - * - * @param apkAssets The new set of paths. - * @param invalidateCaches Whether to invalidate any caches. This should almost always be true. - * Set this to false if you are appending new resources - * (not new configurations). - * @hide - */ - public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) { - Preconditions.checkNotNull(apkAssets, "apkAssets"); - synchronized (this) { - ensureValidLocked(); - mApkAssets = apkAssets; - nativeSetApkAssets(mObject, apkAssets, invalidateCaches); - if (invalidateCaches) { - // Invalidate all caches. - invalidateCachesLocked(-1); + synchronized(this) { + //System.out.println("Release: num=" + mNumRefs + // + ", released=" + mReleased); + if (mOpen) { + mOpen = false; + decRefsLocked(this.hashCode()); } } } - /** - * Invalidates the caches in this AssetManager according to the bitmask `diff`. - * - * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}. - * @see ActivityInfo.Config - */ - private void invalidateCachesLocked(int diff) { - // TODO(adamlesinski): Currently there are no caches to invalidate in Java code. - } - - /** - * @hide - */ - public @NonNull ApkAssets[] getApkAssets() { - synchronized (this) { - ensureValidLocked(); - return mApkAssets; - } - } - - /** - * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} - * @hide - */ - @Deprecated - public int addAssetPath(String path) { - return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/); - } - - /** - * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} - * @hide - */ - @Deprecated - public int addAssetPathAsSharedLibrary(String path) { - return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/); - } - - /** - * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} - * @hide - */ - @Deprecated - public int addOverlayPath(String path) { - return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/); - } - - private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) { - Preconditions.checkNotNull(path, "path"); - synchronized (this) { - ensureOpenLocked(); - final int count = mApkAssets.length; - for (int i = 0; i < count; i++) { - if (mApkAssets[i].getAssetPath().equals(path)) { - return i + 1; - } - } - - final ApkAssets assets; - try { - if (overlay) { - // TODO(b/70343104): This hardcoded path will be removed once - // addAssetPathInternal is deleted. - final String idmapPath = "/data/resource-cache/" - + path.substring(1).replace('/', '@') - + "@idmap"; - assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/); - } else { - assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib); - } - } catch (IOException e) { - return 0; - } - - final ApkAssets[] newApkAssets = Arrays.copyOf(mApkAssets, count + 1); - newApkAssets[count] = assets; - setApkAssets(newApkAssets, true); - return count + 1; - } - } - - /** - * Ensures that the native implementation has not been destroyed. - * The AssetManager may have been closed, but references to it still exist - * and therefore the native implementation is not destroyed. - */ - private void ensureValidLocked() { - if (mObject == 0) { - throw new RuntimeException("AssetManager has been destroyed"); - } - } - - /** - * Ensures that the AssetManager has not been explicitly closed. If this method passes, - * then this implies that ensureValidLocked() also passes. - */ - private void ensureOpenLocked() { - if (!mOpen) { - throw new RuntimeException("AssetManager has been closed"); - } - } - - /** - * Populates {@code outValue} with the data associated a particular - * resource identifier for the current configuration. - * - * @param resId the resource identifier to load - * @param densityDpi the density bucket for which to load the resource - * @param outValue the typed value in which to put the data - * @param resolveRefs {@code true} to resolve references, {@code false} - * to leave them unresolved - * @return {@code true} if the data was loaded into {@code outValue}, - * {@code false} otherwise - */ - boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, - boolean resolveRefs) { - Preconditions.checkNotNull(outValue, "outValue"); - synchronized (this) { - ensureValidLocked(); - final int cookie = nativeGetResourceValue( - mObject, resId, (short) densityDpi, outValue, resolveRefs); - if (cookie <= 0) { - return false; - } - - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); - - if (outValue.type == TypedValue.TYPE_STRING) { - outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); - } - return true; - } - } - /** * Retrieves the string value associated with a particular resource * identifier for the current configuration. @@ -368,7 +156,8 @@ public final class AssetManager implements AutoCloseable { * @param resId the resource identifier to load * @return the string value, or {@code null} */ - @Nullable CharSequence getResourceText(@StringRes int resId) { + @Nullable + final CharSequence getResourceText(@StringRes int resId) { synchronized (this) { final TypedValue outValue = mValue; if (getResourceValue(resId, 0, outValue, true)) { @@ -383,15 +172,15 @@ public final class AssetManager implements AutoCloseable { * identifier for the current configuration. * * @param resId the resource identifier to load - * @param bagEntryId the index into the bag to load + * @param bagEntryId * @return the string value, or {@code null} */ - @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { + @Nullable + final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { synchronized (this) { - ensureValidLocked(); final TypedValue outValue = mValue; - final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue); - if (cookie <= 0) { + final int block = loadResourceBagValue(resId, bagEntryId, outValue, true); + if (block < 0) { return null; } @@ -400,60 +189,52 @@ public final class AssetManager implements AutoCloseable { outValue.changingConfigurations); if (outValue.type == TypedValue.TYPE_STRING) { - return mApkAssets[cookie - 1].getStringFromPool(outValue.data); + return mStringBlocks[block].get(outValue.data); } return outValue.coerceToString(); } } - int getResourceArraySize(@ArrayRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceArraySize(mObject, resId); - } - } - /** - * Populates `outData` with array elements of `resId`. `outData` is normally - * used with - * {@link TypedArray}. - * - * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES} - * long, - * with the indices of the data representing the type, value, asset cookie, - * resource ID, - * configuration change mask, and density of the element. - * - * @param resId The resource ID of an array resource. - * @param outData The array to populate with data. - * @return The length of the array. + * Retrieves the string array associated with a particular resource + * identifier for the current configuration. * - * @see TypedArray#STYLE_TYPE - * @see TypedArray#STYLE_DATA - * @see TypedArray#STYLE_ASSET_COOKIE - * @see TypedArray#STYLE_RESOURCE_ID - * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS - * @see TypedArray#STYLE_DENSITY + * @param resId the resource identifier of the string array + * @return the string array, or {@code null} */ - int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) { - Preconditions.checkNotNull(outData, "outData"); - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceArray(mObject, resId, outData); - } + @Nullable + final String[] getResourceStringArray(@ArrayRes int resId) { + return getArrayStringResource(resId); } /** - * Retrieves the string array associated with a particular resource - * identifier for the current configuration. + * Populates {@code outValue} with the data associated a particular + * resource identifier for the current configuration. * - * @param resId the resource identifier of the string array - * @return the string array, or {@code null} + * @param resId the resource identifier to load + * @param densityDpi the density bucket for which to load the resource + * @param outValue the typed value in which to put the data + * @param resolveRefs {@code true} to resolve references, {@code false} + * to leave them unresolved + * @return {@code true} if the data was loaded into {@code outValue}, + * {@code false} otherwise */ - @Nullable String[] getResourceStringArray(@ArrayRes int resId) { + final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, + boolean resolveRefs) { synchronized (this) { - ensureValidLocked(); - return nativeGetResourceStringArray(mObject, resId); + final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs); + if (block < 0) { + return false; + } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mStringBlocks[block].get(outValue.data); + } + return true; } } @@ -463,48 +244,26 @@ public final class AssetManager implements AutoCloseable { * * @param resId the resource id of the string array */ - @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { + final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { synchronized (this) { - ensureValidLocked(); - final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId); + final int[] rawInfoArray = getArrayStringInfo(resId); if (rawInfoArray == null) { return null; } - final int rawInfoArrayLen = rawInfoArray.length; final int infoArrayLen = rawInfoArrayLen / 2; + int block; + int index; final CharSequence[] retArray = new CharSequence[infoArrayLen]; for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) { - int cookie = rawInfoArray[i]; - int index = rawInfoArray[i + 1]; - retArray[j] = (index >= 0 && cookie > 0) - ? mApkAssets[cookie - 1].getStringFromPool(index) : null; + block = rawInfoArray[i]; + index = rawInfoArray[i + 1]; + retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null; } return retArray; } } - @Nullable int[] getResourceIntArray(@ArrayRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceIntArray(mObject, resId); - } - } - - /** - * Get the attributes for a style resource. These are the <item> - * elements in - * a <style> resource. - * @param resId The resource ID of the style - * @return An array of attribute IDs. - */ - @AttrRes int[] getStyleAttributes(@StyleRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetStyleAttributes(mObject, resId); - } - } - /** * Populates {@code outValue} with the data associated with a particular * resource identifier for the current configuration. Resolves theme @@ -518,88 +277,73 @@ public final class AssetManager implements AutoCloseable { * @return {@code true} if the data was loaded into {@code outValue}, * {@code false} otherwise */ - boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, + final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, boolean resolveRefs) { - Preconditions.checkNotNull(outValue, "outValue"); - synchronized (this) { - ensureValidLocked(); - final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue, - resolveRefs); - if (cookie <= 0) { - return false; - } - - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); - - if (outValue.type == TypedValue.TYPE_STRING) { - outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); - } - return true; - } - } - - void dumpTheme(long theme, int priority, String tag, String prefix) { - synchronized (this) { - ensureValidLocked(); - nativeThemeDump(mObject, theme, priority, tag, prefix); + final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs); + if (block < 0) { + return false; } - } - @Nullable String getResourceName(@AnyRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceName(mObject, resId); - } - } + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); - @Nullable String getResourcePackageName(@AnyRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourcePackageName(mObject, resId); + if (outValue.type == TypedValue.TYPE_STRING) { + final StringBlock[] blocks = ensureStringBlocks(); + outValue.string = blocks[block].get(outValue.data); } + return true; } - @Nullable String getResourceTypeName(@AnyRes int resId) { + /** + * Ensures the string blocks are loaded. + * + * @return the string blocks + */ + @NonNull + final StringBlock[] ensureStringBlocks() { synchronized (this) { - ensureValidLocked(); - return nativeGetResourceTypeName(mObject, resId); + if (mStringBlocks == null) { + makeStringBlocks(sSystem.mStringBlocks); + } + return mStringBlocks; } } - @Nullable String getResourceEntryName(@AnyRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceEntryName(mObject, resId); + /*package*/ final void makeStringBlocks(StringBlock[] seed) { + final int seedNum = (seed != null) ? seed.length : 0; + final int num = getStringBlockCount(); + mStringBlocks = new StringBlock[num]; + if (localLOGV) Log.v(TAG, "Making string blocks for " + this + + ": " + num); + for (int i=0; i Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len; + long len = getAssetRemainingLength(mAsset); + return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len; } - - @Override public final void close() throws IOException { - if (mAssetNativePtr != 0) { - nativeAssetDestroy(mAssetNativePtr); - mAssetNativePtr = 0; - - synchronized (AssetManager.this) { + synchronized (AssetManager.this) { + if (mAsset != 0) { + destroyAsset(mAsset); + mAsset = 0; decRefsLocked(hashCode()); } } } - - @Override public final void mark(int readlimit) { - mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0); + mMarkPos = seekAsset(mAsset, 0, 0); } - - @Override public final void reset() throws IOException { - nativeAssetSeek(mAssetNativePtr, mMarkPos, -1); + seekAsset(mAsset, mMarkPos, -1); } - - @Override - public final int read(@NonNull byte[] b) throws IOException { - Preconditions.checkNotNull(b, "b"); - return nativeAssetRead(mAssetNativePtr, b, 0, b.length); + public final int read(byte[] b) throws IOException { + return readAsset(mAsset, b, 0, b.length); } - - @Override - public final int read(@NonNull byte[] b, int off, int len) throws IOException { - Preconditions.checkNotNull(b, "b"); - return nativeAssetRead(mAssetNativePtr, b, off, len); + public final int read(byte[] b, int off, int len) throws IOException { + return readAsset(mAsset, b, off, len); } - - @Override public final long skip(long n) throws IOException { - long pos = nativeAssetSeek(mAssetNativePtr, 0, 0); - if ((pos + n) > mLength) { - n = mLength - pos; + long pos = seekAsset(mAsset, 0, 0); + if ((pos+n) > mLength) { + n = mLength-pos; } if (n > 0) { - nativeAssetSeek(mAssetNativePtr, n, 0); + seekAsset(mAsset, n, 0); } return n; } - @Override - protected void finalize() throws Throwable { + protected void finalize() throws Throwable + { close(); } + + private long mAsset; + private long mLength; + private long mMarkPos; + } + + /** + * Add an additional set of assets to the asset manager. This can be + * either a directory or ZIP file. Not for use by applications. Returns + * the cookie of the added asset, or 0 on failure. + * {@hide} + */ + public final int addAssetPath(String path) { + return addAssetPathInternal(path, false); + } + + /** + * Add an application assets to the asset manager and loading it as shared library. + * This can be either a directory or ZIP file. Not for use by applications. Returns + * the cookie of the added asset, or 0 on failure. + * {@hide} + */ + public final int addAssetPathAsSharedLibrary(String path) { + return addAssetPathInternal(path, true); + } + + private final int addAssetPathInternal(String path, boolean appAsLib) { + synchronized (this) { + int res = addAssetPathNative(path, appAsLib); + makeStringBlocks(mStringBlocks); + return res; + } + } + + private native final int addAssetPathNative(String path, boolean appAsLib); + + /** + * Add an additional set of assets to the asset manager from an already open + * FileDescriptor. Not for use by applications. + * This does not give full AssetManager functionality for these assets, + * since the origin of the file is not known for purposes of sharing, + * overlay resolution, and other features. However it does allow you + * to do simple access to the contents of the given fd as an apk file. + * Performs a dup of the underlying fd, so you must take care of still closing + * the FileDescriptor yourself (and can do that whenever you want). + * Returns the cookie of the added asset, or 0 on failure. + * {@hide} + */ + public int addAssetFd(FileDescriptor fd, String debugPathName) { + return addAssetFdInternal(fd, debugPathName, false); + } + + private int addAssetFdInternal(FileDescriptor fd, String debugPathName, + boolean appAsLib) { + synchronized (this) { + int res = addAssetFdNative(fd, debugPathName, appAsLib); + makeStringBlocks(mStringBlocks); + return res; + } + } + + private native int addAssetFdNative(FileDescriptor fd, String debugPathName, + boolean appAsLib); + + /** + * Add a set of assets to overlay an already added set of assets. + * + * This is only intended for application resources. System wide resources + * are handled before any Java code is executed. + * + * {@hide} + */ + + public final int addOverlayPath(String idmapPath) { + synchronized (this) { + int res = addOverlayPathNative(idmapPath); + makeStringBlocks(mStringBlocks); + return res; + } + } + + /** + * See addOverlayPath. + * + * {@hide} + */ + public native final int addOverlayPathNative(String idmapPath); + + /** + * Add multiple sets of assets to the asset manager at once. See + * {@link #addAssetPath(String)} for more information. Returns array of + * cookies for each added asset with 0 indicating failure, or null if + * the input array of paths is null. + * {@hide} + */ + public final int[] addAssetPaths(String[] paths) { + if (paths == null) { + return null; + } + + int[] cookies = new int[paths.length]; + for (int i = 0; i < paths.length; i++) { + cookies[i] = addAssetPath(paths[i]); + } + + return cookies; } /** * Determine whether the state in this asset manager is up-to-date with * the files on the filesystem. If false is returned, you need to * instantiate a new AssetManager class to see the new data. - * @hide + * {@hide} */ - public boolean isUpToDate() { - for (ApkAssets apkAssets : getApkAssets()) { - if (!apkAssets.isUpToDate()) { - return false; - } - } - return true; - } + public native final boolean isUpToDate(); /** * Get the locales that this asset manager contains data for. @@ -1058,12 +784,7 @@ public final class AssetManager implements AutoCloseable { * are of the form {@code ll_CC} where {@code ll} is a two letter language code, * and {@code CC} is a two letter country code. */ - public String[] getLocales() { - synchronized (this) { - ensureValidLocked(); - return nativeGetLocales(mObject, false /*excludeSystem*/); - } - } + public native final String[] getLocales(); /** * Same as getLocales(), except that locales that are only provided by the system (i.e. those @@ -1073,57 +794,131 @@ public final class AssetManager implements AutoCloseable { * assets support Cherokee and French, getLocales() would return * [Cherokee, English, French, German], while getNonSystemLocales() would return * [Cherokee, French]. - * @hide + * {@hide} */ - public String[] getNonSystemLocales() { - synchronized (this) { - ensureValidLocked(); - return nativeGetLocales(mObject, true /*excludeSystem*/); - } - } + public native final String[] getNonSystemLocales(); - /** - * @hide - */ - Configuration[] getSizeConfigurations() { - synchronized (this) { - ensureValidLocked(); - return nativeGetSizeConfigurations(mObject); - } - } + /** {@hide} */ + public native final Configuration[] getSizeConfigurations(); /** - * Change the configuration used when retrieving resources. Not for use by + * Change the configuation used when retrieving resources. Not for use by * applications. - * @hide + * {@hide} */ - public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation, - int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, - int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, - int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) { - synchronized (this) { - ensureValidLocked(); - nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density, - keyboard, keyboardHidden, navigation, screenWidth, screenHeight, - smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode, - colorMode, majorVersion); - } - } + public native final void setConfiguration(int mcc, int mnc, String locale, + int orientation, int touchscreen, int density, int keyboard, + int keyboardHidden, int navigation, int screenWidth, int screenHeight, + int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, + int screenLayout, int uiMode, int colorMode, int majorVersion); /** - * @hide + * Retrieve the resource identifier for the given resource name. */ - public SparseArray getAssignedPackageIdentifiers() { - synchronized (this) { - ensureValidLocked(); - return nativeGetAssignedPackageIdentifiers(mObject); - } - } + /*package*/ native final int getResourceIdentifier(String name, + String defType, + String defPackage); - private void incRefsLocked(long id) { + /*package*/ native final String getResourceName(int resid); + /*package*/ native final String getResourcePackageName(int resid); + /*package*/ native final String getResourceTypeName(int resid); + /*package*/ native final String getResourceEntryName(int resid); + + private native final long openAsset(String fileName, int accessMode); + private final native ParcelFileDescriptor openAssetFd(String fileName, + long[] outOffsets) throws IOException; + private native final long openNonAssetNative(int cookie, String fileName, + int accessMode); + private native ParcelFileDescriptor openNonAssetFdNative(int cookie, + String fileName, long[] outOffsets) throws IOException; + private native final void destroyAsset(long asset); + private native final int readAssetChar(long asset); + private native final int readAsset(long asset, byte[] b, int off, int len); + private native final long seekAsset(long asset, long offset, int whence); + private native final long getAssetLength(long asset); + private native final long getAssetRemainingLength(long asset); + + /** Returns true if the resource was found, filling in mRetStringBlock and + * mRetData. */ + private native final int loadResourceValue(int ident, short density, TypedValue outValue, + boolean resolve); + /** Returns true if the resource was found, filling in mRetStringBlock and + * mRetData. */ + private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue, + boolean resolve); + /*package*/ static final int STYLE_NUM_ENTRIES = 6; + /*package*/ static final int STYLE_TYPE = 0; + /*package*/ static final int STYLE_DATA = 1; + /*package*/ static final int STYLE_ASSET_COOKIE = 2; + /*package*/ static final int STYLE_RESOURCE_ID = 3; + + /* Offset within typed data array for native changingConfigurations. */ + static final int STYLE_CHANGING_CONFIGURATIONS = 4; + + /*package*/ static final int STYLE_DENSITY = 5; + /*package*/ native static final void applyStyle(long theme, + int defStyleAttr, int defStyleRes, long xmlParser, + int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress); + /*package*/ native static final boolean resolveAttrs(long theme, + int defStyleAttr, int defStyleRes, int[] inValues, + int[] inAttrs, int[] outValues, int[] outIndices); + /*package*/ native final boolean retrieveAttributes( + long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); + /*package*/ native final int getArraySize(int resource); + /*package*/ native final int retrieveArray(int resource, int[] outValues); + private native final int getStringBlockCount(); + private native final long getNativeStringBlock(int block); + + /** + * {@hide} + */ + public native final String getCookieName(int cookie); + + /** + * {@hide} + */ + public native final SparseArray getAssignedPackageIdentifiers(); + + /** + * {@hide} + */ + public native static final int getGlobalAssetCount(); + + /** + * {@hide} + */ + public native static final String getAssetAllocations(); + + /** + * {@hide} + */ + public native static final int getGlobalAssetManagerCount(); + + private native final long newTheme(); + private native final void deleteTheme(long theme); + /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force); + /*package*/ native static final void copyTheme(long dest, long source); + /*package*/ native static final void clearTheme(long theme); + /*package*/ native static final int loadThemeAttributeValue(long theme, int ident, + TypedValue outValue, + boolean resolve); + /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix); + /*package*/ native static final @NativeConfig int getThemeChangingConfigurations(long theme); + + private native final long openXmlAssetNative(int cookie, String fileName); + + private native final String[] getArrayStringResource(int arrayRes); + private native final int[] getArrayStringInfo(int arrayRes); + /*package*/ native final int[] getArrayIntResource(int arrayRes); + /*package*/ native final int[] getStyleAttributes(int themeRes); + + private native final void init(boolean isSystem); + private native final void destroy(); + + private final void incRefsLocked(long id) { if (DEBUG_REFS) { if (mRefStacks == null) { - mRefStacks = new HashMap<>(); + mRefStacks = new HashMap(); } RuntimeException ex = new RuntimeException(); ex.fillInStackTrace(); @@ -1131,117 +926,16 @@ public final class AssetManager implements AutoCloseable { } mNumRefs++; } - - private void decRefsLocked(long id) { + + private final void decRefsLocked(long id) { if (DEBUG_REFS && mRefStacks != null) { mRefStacks.remove(id); } mNumRefs--; - if (mNumRefs == 0 && mObject != 0) { - nativeDestroy(mObject); - mObject = 0; + //System.out.println("Dec streams: mNumRefs=" + mNumRefs + // + " mReleased=" + mReleased); + if (mNumRefs == 0) { + destroy(); } } - - // AssetManager setup native methods. - private static native long nativeCreate(); - private static native void nativeDestroy(long ptr); - private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, - boolean invalidateCaches); - private static native void nativeSetConfiguration(long ptr, int mcc, int mnc, - @Nullable String locale, int orientation, int touchscreen, int density, int keyboard, - int keyboardHidden, int navigation, int screenWidth, int screenHeight, - int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, - int uiMode, int colorMode, int majorVersion); - private static native @NonNull SparseArray nativeGetAssignedPackageIdentifiers( - long ptr); - - // File native methods. - private static native @Nullable String[] nativeList(long ptr, @NonNull String path) - throws IOException; - private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode); - private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr, - @NonNull String fileName, long[] outOffsets) throws IOException; - private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName, - int accessMode); - private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie, - @NonNull String fileName, @NonNull long[] outOffsets) throws IOException; - private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName); - - // Primitive resource native methods. - private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density, - @NonNull TypedValue outValue, boolean resolveReferences); - private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId, - @NonNull TypedValue outValue); - - private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr, - @StyleRes int resId); - private static native @Nullable String[] nativeGetResourceStringArray(long ptr, - @ArrayRes int resId); - private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr, - @ArrayRes int resId); - private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId); - private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId); - private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId, - @NonNull int[] outValues); - - // Resource name/ID native methods. - private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name, - @Nullable String defType, @Nullable String defPackage); - private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid); - private static native @Nullable String nativeGetResourcePackageName(long ptr, - @AnyRes int resid); - private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid); - private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid); - private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem); - private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr); - - // Style attribute retrieval native methods. - private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, - @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs, - long outValuesAddress, long outIndicesAddress); - private static native boolean nativeResolveAttrs(long ptr, long themePtr, - @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, - @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); - private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr, - @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); - - // Theme related native methods - private static native long nativeThemeCreate(long ptr); - private static native void nativeThemeDestroy(long themePtr); - private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, - boolean force); - static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr); - static native void nativeThemeClear(long themePtr); - private static native int nativeThemeGetAttributeValue(long ptr, long themePtr, - @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve); - private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag, - String prefix); - static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr); - - // AssetInputStream related native methods. - private static native void nativeAssetDestroy(long assetPtr); - private static native int nativeAssetReadChar(long assetPtr); - private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len); - private static native long nativeAssetSeek(long assetPtr, long offset, int whence); - private static native long nativeAssetGetLength(long assetPtr); - private static native long nativeAssetGetRemainingLength(long assetPtr); - - private static native void nativeVerifySystemIdmaps(); - - // Global debug native methods. - /** - * @hide - */ - public static native int getGlobalAssetCount(); - - /** - * @hide - */ - public static native String getAssetAllocations(); - - /** - * @hide - */ - public static native int getGlobalAssetManagerCount(); } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 8f58891ed556..e173653cd961 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -590,7 +590,7 @@ public class Resources { */ @NonNull public int[] getIntArray(@ArrayRes int id) throws NotFoundException { - int[] res = mResourcesImpl.getAssets().getResourceIntArray(id); + int[] res = mResourcesImpl.getAssets().getArrayIntResource(id); if (res != null) { return res; } @@ -613,13 +613,13 @@ public class Resources { @NonNull public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException { final ResourcesImpl impl = mResourcesImpl; - int len = impl.getAssets().getResourceArraySize(id); + int len = impl.getAssets().getArraySize(id); if (len < 0) { throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id)); } TypedArray array = TypedArray.obtain(this, len); - array.mLength = impl.getAssets().getResourceArray(id, array.mData); + array.mLength = impl.getAssets().retrieveArray(id, array.mData); array.mIndices[0] = 0; return array; @@ -1789,7 +1789,8 @@ public class Resources { // out the attributes from the XML file (applying type information // contained in the resources and such). XmlBlock.Parser parser = (XmlBlock.Parser)set; - mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices); + mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, attrs, + array.mData, array.mIndices); array.mXml = parser; diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 2a4b278bdca8..97cb78bc4243 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -168,6 +168,7 @@ public class ResourcesImpl { mDisplayAdjustments = displayAdjustments; mConfiguration.setToDefaults(); updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo()); + mAssets.ensureStringBlocks(); } public DisplayAdjustments getDisplayAdjustments() { @@ -1273,7 +1274,8 @@ public class ResourcesImpl { void applyStyle(int resId, boolean force) { synchronized (mKey) { - mAssets.applyStyleToTheme(mTheme, resId, force); + AssetManager.applyThemeStyle(mTheme, resId, force); + mThemeResId = resId; mKey.append(resId, force); } @@ -1282,7 +1284,7 @@ public class ResourcesImpl { void setTo(ThemeImpl other) { synchronized (mKey) { synchronized (other.mKey) { - AssetManager.nativeThemeCopy(mTheme, other.mTheme); + AssetManager.copyTheme(mTheme, other.mTheme); mThemeResId = other.mThemeResId; mKey.setTo(other.getKey()); @@ -1305,10 +1307,12 @@ public class ResourcesImpl { // out the attributes from the XML file (applying type information // contained in the resources and such). final XmlBlock.Parser parser = (XmlBlock.Parser) set; - mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs, - array.mDataAddress, array.mIndicesAddress); + AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes, + parser != null ? parser.mParseState : 0, + attrs, attrs.length, array.mDataAddress, array.mIndicesAddress); array.mTheme = wrapper; array.mXml = parser; + return array; } } @@ -1325,7 +1329,7 @@ public class ResourcesImpl { } final TypedArray array = TypedArray.obtain(wrapper.getResources(), len); - mAssets.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); + AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); array.mTheme = wrapper; array.mXml = null; return array; @@ -1345,14 +1349,14 @@ public class ResourcesImpl { @Config int getChangingConfigurations() { synchronized (mKey) { final @NativeConfig int nativeChangingConfig = - AssetManager.nativeThemeGetChangingConfigurations(mTheme); + AssetManager.getThemeChangingConfigurations(mTheme); return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig); } } public void dump(int priority, String tag, String prefix) { synchronized (mKey) { - mAssets.dumpTheme(mTheme, priority, tag, prefix); + AssetManager.dumpTheme(mTheme, priority, tag, prefix); } } @@ -1381,13 +1385,13 @@ public class ResourcesImpl { */ void rebase() { synchronized (mKey) { - AssetManager.nativeThemeClear(mTheme); + AssetManager.clearTheme(mTheme); // Reapply the same styles in the same order. for (int i = 0; i < mKey.mCount; i++) { final int resId = mKey.mResId[i]; final boolean force = mKey.mForce[i]; - mAssets.applyStyleToTheme(mTheme, resId, force); + AssetManager.applyThemeStyle(mTheme, resId, force); } } } diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index cbb3c6df0558..f33c75168a5f 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -61,15 +61,6 @@ public class TypedArray { return attrs; } - // STYLE_ prefixed constants are offsets within the typed data array. - static final int STYLE_NUM_ENTRIES = 6; - static final int STYLE_TYPE = 0; - static final int STYLE_DATA = 1; - static final int STYLE_ASSET_COOKIE = 2; - static final int STYLE_RESOURCE_ID = 3; - static final int STYLE_CHANGING_CONFIGURATIONS = 4; - static final int STYLE_DENSITY = 5; - private final Resources mResources; private DisplayMetrics mMetrics; private AssetManager mAssets; @@ -87,7 +78,7 @@ public class TypedArray { private void resize(int len) { mLength = len; - final int dataLen = len * STYLE_NUM_ENTRIES; + final int dataLen = len * AssetManager.STYLE_NUM_ENTRIES; final int indicesLen = len + 1; final VMRuntime runtime = VMRuntime.getRuntime(); if (mDataAddress == 0 || mData.length < dataLen) { @@ -175,9 +166,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -212,9 +203,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -251,13 +242,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_STRING) { - final int cookie = data[index + STYLE_ASSET_COOKIE]; + final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; if (cookie < 0) { - return mXml.getPooledString(data[index + STYLE_DATA]).toString(); + return mXml.getPooledString( + data[index+AssetManager.STYLE_DATA]).toString(); } } return null; @@ -282,11 +274,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava( - data[index + STYLE_CHANGING_CONFIGURATIONS]); + data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); if ((changingConfigs & ~allowedChangingConfigs) != 0) { return null; } @@ -328,14 +320,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA] != 0; + return data[index+AssetManager.STYLE_DATA] != 0; } final TypedValue v = mValue; @@ -367,14 +359,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } final TypedValue v = mValue; @@ -404,16 +396,16 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FLOAT) { - return Float.intBitsToFloat(data[index + STYLE_DATA]); + return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]); } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } final TypedValue v = mValue; @@ -454,15 +446,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_STRING) { final TypedValue value = mValue; if (getValueAt(index, value)) { @@ -506,7 +498,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -541,7 +533,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -572,15 +564,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -620,14 +612,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimension( + data[index + AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -668,14 +661,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelOffset( + data[index + AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -717,14 +711,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize( + data[index+AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -760,15 +755,16 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize( + data[index+AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -799,14 +795,15 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize( + data[index + AssetManager.STYLE_DATA], mMetrics); } return defValue; @@ -836,14 +833,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FRACTION) { - return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase); + return TypedValue.complexToFraction( + data[index+AssetManager.STYLE_DATA], base, pbase); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -876,10 +874,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) { - final int resid = data[index + STYLE_RESOURCE_ID]; + if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) { + final int resid = data[index+AssetManager.STYLE_RESOURCE_ID]; if (resid != 0) { return resid; } @@ -904,10 +902,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { - return data[index + STYLE_DATA]; + if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { + return data[index + AssetManager.STYLE_DATA]; } return defValue; } @@ -941,7 +939,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -977,7 +975,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -1008,7 +1006,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { return mResources.getTextArray(value.resourceId); } return null; @@ -1029,7 +1027,7 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - return getValueAt(index * STYLE_NUM_ENTRIES, outValue); + return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue); } /** @@ -1045,8 +1043,8 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; - return mData[index + STYLE_TYPE]; + index *= AssetManager.STYLE_NUM_ENTRIES; + return mData[index + AssetManager.STYLE_TYPE]; } /** @@ -1065,9 +1063,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; return type != TypedValue.TYPE_NULL; } @@ -1086,11 +1084,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; return type != TypedValue.TYPE_NULL - || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; + || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; } /** @@ -1111,7 +1109,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { return value; } return null; @@ -1183,16 +1181,16 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * STYLE_NUM_ENTRIES; - if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { + final int index = i * AssetManager.STYLE_NUM_ENTRIES; + if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { // Not an attribute, ignore. continue; } // Null the entry so that we can safely call getZzz(). - data[index + STYLE_TYPE] = TypedValue.TYPE_NULL; + data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL; - final int attr = data[index + STYLE_DATA]; + final int attr = data[index + AssetManager.STYLE_DATA]; if (attr == 0) { // Useless data, ignore. continue; @@ -1233,44 +1231,45 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * STYLE_NUM_ENTRIES; - final int type = data[index + STYLE_TYPE]; + final int index = i * AssetManager.STYLE_NUM_ENTRIES; + final int type = data[index + AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { continue; } changingConfig |= ActivityInfo.activityInfoConfigNativeToJava( - data[index + STYLE_CHANGING_CONFIGURATIONS]); + data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); } return changingConfig; } private boolean getValueAt(int index, TypedValue outValue) { final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return false; } outValue.type = type; - outValue.data = data[index + STYLE_DATA]; - outValue.assetCookie = data[index + STYLE_ASSET_COOKIE]; - outValue.resourceId = data[index + STYLE_RESOURCE_ID]; + outValue.data = data[index+AssetManager.STYLE_DATA]; + outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; + outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID]; outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - data[index + STYLE_CHANGING_CONFIGURATIONS]); - outValue.density = data[index + STYLE_DENSITY]; + data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); + outValue.density = data[index+AssetManager.STYLE_DENSITY]; outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null; return true; } private CharSequence loadStringValueAt(int index) { final int[] data = mData; - final int cookie = data[index + STYLE_ASSET_COOKIE]; + final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; if (cookie < 0) { if (mXml != null) { - return mXml.getPooledString(data[index + STYLE_DATA]); + return mXml.getPooledString( + data[index+AssetManager.STYLE_DATA]); } return null; } - return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]); + return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]); } /** @hide */ diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java index d4ccffb83ca5..e6b957414ea8 100644 --- a/core/java/android/content/res/XmlBlock.java +++ b/core/java/android/content/res/XmlBlock.java @@ -16,7 +16,6 @@ package android.content.res; -import android.annotation.Nullable; import android.util.TypedValue; import com.android.internal.util.XmlUtils; @@ -34,7 +33,7 @@ import java.io.Reader; * * {@hide} */ -final class XmlBlock implements AutoCloseable { +final class XmlBlock { private static final boolean DEBUG=false; public XmlBlock(byte[] data) { @@ -49,7 +48,6 @@ final class XmlBlock implements AutoCloseable { mStrings = new StringBlock(nativeGetStringBlock(mNative), false); } - @Override public void close() { synchronized (this) { if (mOpen) { @@ -480,13 +478,13 @@ final class XmlBlock implements AutoCloseable { * are doing! The given native object must exist for the entire lifetime * of this newly creating XmlBlock. */ - XmlBlock(@Nullable AssetManager assets, long xmlBlock) { + XmlBlock(AssetManager assets, long xmlBlock) { mAssets = assets; mNative = xmlBlock; mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false); } - private @Nullable final AssetManager mAssets; + private final AssetManager mAssets; private final long mNative; /*package*/ final StringBlock mStrings; private boolean mOpen = true; diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 19cc2d0f5358..543acc7c2158 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -110,8 +110,8 @@ cc_library_shared { "android_util_AssetManager.cpp", "android_util_Binder.cpp", "android_util_EventLog.cpp", - "android_util_Log.cpp", "android_util_MemoryIntArray.cpp", + "android_util_Log.cpp", "android_util_PathParser.cpp", "android_util_Process.cpp", "android_util_StringBlock.cpp", @@ -189,7 +189,6 @@ cc_library_shared { "android_backup_FileBackupHelperBase.cpp", "android_backup_BackupHelperDispatcher.cpp", "android_app_backup_FullBackup.cpp", - "android_content_res_ApkAssets.cpp", "android_content_res_ObbScanner.cpp", "android_content_res_Configuration.cpp", "android_animation_PropertyValuesHolder.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index e3b5c8f3136a..aa9a82415f97 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -122,7 +122,6 @@ extern int register_android_util_MemoryIntArray(JNIEnv* env); extern int register_android_util_PathParser(JNIEnv* env); extern int register_android_content_StringBlock(JNIEnv* env); extern int register_android_content_XmlBlock(JNIEnv* env); -extern int register_android_content_res_ApkAssets(JNIEnv* env); extern int register_android_graphics_Canvas(JNIEnv* env); extern int register_android_graphics_CanvasProperty(JNIEnv* env); extern int register_android_graphics_ColorFilter(JNIEnv* env); @@ -1341,7 +1340,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_AssetManager), REG_JNI(register_android_content_StringBlock), REG_JNI(register_android_content_XmlBlock), - REG_JNI(register_android_content_res_ApkAssets), REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_Hyphenator), REG_JNI(register_android_text_MeasuredParagraph), diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index c50026ea570e..dd3e6f02e9fe 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include "Utils.h" #include "FontUtils.h" @@ -224,8 +224,7 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b NPE_CHECK_RETURN_ZERO(env, jpath); NativeFamilyBuilder* builder = reinterpret_cast(builderPtr); - - Guarded* mgr = AssetManagerForJavaObject(env, jassetMgr); + AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr); if (NULL == mgr) { builder->axes.clear(); return false; @@ -237,33 +236,27 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b return false; } - std::unique_ptr asset; - { - ScopedLock locked_mgr(*mgr); - if (isAsset) { - asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); - } else if (cookie > 0) { - // Valid java cookies are 1-based, but AssetManager cookies are 0-based. - asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast(cookie - 1), - Asset::ACCESS_BUFFER); - } else { - asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER); - } + Asset* asset; + if (isAsset) { + asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); + } else { + asset = cookie ? mgr->openNonAsset(static_cast(cookie), str.c_str(), + Asset::ACCESS_BUFFER) : mgr->openNonAsset(str.c_str(), Asset::ACCESS_BUFFER); } - if (nullptr == asset) { + if (NULL == asset) { builder->axes.clear(); return false; } const void* buf = asset->getBuffer(false); if (NULL == buf) { + delete asset; builder->axes.clear(); return false; } - sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, - asset.release())); + sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset)); return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); } diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 49a24a30f77e..09e37e1a3de6 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -361,7 +361,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName code->sdkVersion = sdkVersion; code->javaAssetManager = env->NewGlobalRef(jAssetMgr); - code->assetManager = NdkAssetManagerForJavaObject(env, jAssetMgr); + code->assetManager = assetManagerForJavaObject(env, jAssetMgr); if (obbDir != NULL) { dirStr = env->GetStringUTFChars(obbDir, NULL); diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp deleted file mode 100644 index c0f151b71c93..000000000000 --- a/core/jni/android_content_res_ApkAssets.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "android-base/macros.h" -#include "android-base/stringprintf.h" -#include "android-base/unique_fd.h" -#include "androidfw/ApkAssets.h" -#include "utils/misc.h" - -#include "core_jni_helpers.h" -#include "jni.h" -#include "nativehelper/ScopedUtfChars.h" - -using ::android::base::unique_fd; - -namespace android { - -static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboolean system, - jboolean force_shared_lib, jboolean overlay) { - ScopedUtfChars path(env, java_path); - if (path.c_str() == nullptr) { - return 0; - } - - std::unique_ptr apk_assets; - if (overlay) { - apk_assets = ApkAssets::LoadOverlay(path.c_str(), system); - } else if (force_shared_lib) { - apk_assets = ApkAssets::LoadAsSharedLibrary(path.c_str(), system); - } else { - apk_assets = ApkAssets::Load(path.c_str(), system); - } - - if (apk_assets == nullptr) { - std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str()); - jniThrowException(env, "java/io/IOException", error_msg.c_str()); - return 0; - } - return reinterpret_cast(apk_assets.release()); -} - -static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor, - jstring friendly_name, jboolean system, jboolean force_shared_lib) { - ScopedUtfChars friendly_name_utf8(env, friendly_name); - if (friendly_name_utf8.c_str() == nullptr) { - return 0; - } - - int fd = jniGetFDFromFileDescriptor(env, file_descriptor); - if (fd < 0) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); - return 0; - } - - unique_fd dup_fd(::dup(fd)); - if (dup_fd < 0) { - jniThrowIOException(env, errno); - return 0; - } - - std::unique_ptr apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), - friendly_name_utf8.c_str(), - system, force_shared_lib); - if (apk_assets == nullptr) { - std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d", - friendly_name_utf8.c_str(), dup_fd.get()); - jniThrowException(env, "java/io/IOException", error_msg.c_str()); - return 0; - } - return reinterpret_cast(apk_assets.release()); -} - -static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - delete reinterpret_cast(ptr); -} - -static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) { - const ApkAssets* apk_assets = reinterpret_cast(ptr); - return env->NewStringUTF(apk_assets->GetPath().c_str()); -} - -static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - const ApkAssets* apk_assets = reinterpret_cast(ptr); - return reinterpret_cast(apk_assets->GetLoadedArsc()->GetStringPool()); -} - -static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - const ApkAssets* apk_assets = reinterpret_cast(ptr); - (void)apk_assets; - return JNI_TRUE; -} - -static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) { - ScopedUtfChars path_utf8(env, file_name); - if (path_utf8.c_str() == nullptr) { - return 0; - } - - const ApkAssets* apk_assets = reinterpret_cast(ptr); - std::unique_ptr asset = apk_assets->Open(path_utf8.c_str(), - Asset::AccessMode::ACCESS_RANDOM); - if (asset == nullptr) { - jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str()); - return 0; - } - - // DynamicRefTable is only needed when looking up resource references. Opening an XML file - // directly from an ApkAssets has no notion of proper resource references. - std::unique_ptr xml_tree = util::make_unique(nullptr /*dynamicRefTable*/); - status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); - asset.reset(); - - if (err != NO_ERROR) { - jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); - return 0; - } - return reinterpret_cast(xml_tree.release()); -} - -// JNI registration. -static const JNINativeMethod gApkAssetsMethods[] = { - {"nativeLoad", "(Ljava/lang/String;ZZZ)J", (void*)NativeLoad}, - {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZ)J", - (void*)NativeLoadFromFd}, - {"nativeDestroy", "(J)V", (void*)NativeDestroy}, - {"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath}, - {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock}, - {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate}, - {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml}, -}; - -int register_android_content_res_ApkAssets(JNIEnv* env) { - return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods, - arraysize(gApkAssetsMethods)); -} - -} // namespace android diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 557b3ab26e64..c6828c4f60de 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -1,1438 +1,1846 @@ -/* - * Copyright 2006, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* //device/libs/android_runtime/android_util_AssetManager.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ #define LOG_TAG "asset" +#include + #include #include #include -#include -#include #include #include +#include +#include #include // for AID_SYSTEM -#include "android-base/logging.h" -#include "android-base/properties.h" -#include "android-base/stringprintf.h" -#include "android_runtime/android_util_AssetManager.h" -#include "android_runtime/AndroidRuntime.h" -#include "android_util_Binder.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" -#include "androidfw/AssetManager2.h" #include "androidfw/AttributeResolution.h" -#include "androidfw/MutexGuard.h" #include "androidfw/ResourceTypes.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_util_Binder.h" #include "core_jni_helpers.h" #include "jni.h" -#include "nativehelper/JNIHelp.h" -#include "nativehelper/ScopedPrimitiveArray.h" -#include "nativehelper/ScopedStringChars.h" -#include "nativehelper/ScopedUtfChars.h" +#include +#include +#include #include "utils/Log.h" -#include "utils/String8.h" #include "utils/misc.h" +#include "utils/String8.h" extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap); extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap); -using ::android::base::StringPrintf; namespace android { +static const bool kThrowOnBadId = false; + // ---------------------------------------------------------------------------- -static struct typedvalue_offsets_t { - jfieldID mType; - jfieldID mData; - jfieldID mString; - jfieldID mAssetCookie; - jfieldID mResourceId; - jfieldID mChangingConfigurations; - jfieldID mDensity; +static struct typedvalue_offsets_t +{ + jfieldID mType; + jfieldID mData; + jfieldID mString; + jfieldID mAssetCookie; + jfieldID mResourceId; + jfieldID mChangingConfigurations; + jfieldID mDensity; } gTypedValueOffsets; -static struct assetfiledescriptor_offsets_t { - jfieldID mFd; - jfieldID mStartOffset; - jfieldID mLength; +static struct assetfiledescriptor_offsets_t +{ + jfieldID mFd; + jfieldID mStartOffset; + jfieldID mLength; } gAssetFileDescriptorOffsets; -static struct assetmanager_offsets_t { - jfieldID mObject; +static struct assetmanager_offsets_t +{ + jfieldID mObject; } gAssetManagerOffsets; -static struct { - jfieldID native_ptr; -} gApkAssetsFields; - -static struct sparsearray_offsets_t { - jclass classObject; - jmethodID constructor; - jmethodID put; +static struct sparsearray_offsets_t +{ + jclass classObject; + jmethodID constructor; + jmethodID put; } gSparseArrayOffsets; -static struct configuration_offsets_t { - jclass classObject; - jmethodID constructor; - jfieldID mSmallestScreenWidthDpOffset; - jfieldID mScreenWidthDpOffset; - jfieldID mScreenHeightDpOffset; +static struct configuration_offsets_t +{ + jclass classObject; + jmethodID constructor; + jfieldID mSmallestScreenWidthDpOffset; + jfieldID mScreenWidthDpOffset; + jfieldID mScreenHeightDpOffset; } gConfigurationOffsets; -jclass g_stringClass = nullptr; +jclass g_stringClass = NULL; // ---------------------------------------------------------------------------- -// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. -constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { - return cookie != kInvalidCookie ? static_cast(cookie + 1) : -1; +static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, + const Res_value& value, uint32_t ref, ssize_t block, + uint32_t typeSpecFlags, ResTable_config* config = NULL); + +jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, + const Res_value& value, uint32_t ref, ssize_t block, + uint32_t typeSpecFlags, ResTable_config* config) +{ + env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType); + env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie, + static_cast(table->getTableCookie(block))); + env->SetIntField(outValue, gTypedValueOffsets.mData, value.data); + env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL); + env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref); + env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations, + typeSpecFlags); + if (config != NULL) { + env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density); + } + return block; } -constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) { - return cookie > 0 ? static_cast(cookie - 1) : kInvalidCookie; +// This is called by zygote (running as user root) as part of preloadResources. +static void verifySystemIdmaps() +{ + pid_t pid; + char system_id[10]; + + snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM); + + switch (pid = fork()) { + case -1: + ALOGE("failed to fork for idmap: %s", strerror(errno)); + break; + case 0: // child + { + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata; + + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + + capheader.version = _LINUX_CAPABILITY_VERSION; + capheader.pid = 0; + + if (capget(&capheader, &capdata) != 0) { + ALOGE("capget: %s\n", strerror(errno)); + exit(1); + } + + capdata.effective = capdata.permitted; + if (capset(&capheader, &capdata) != 0) { + ALOGE("capset: %s\n", strerror(errno)); + exit(1); + } + + if (setgid(AID_SYSTEM) != 0) { + ALOGE("setgid: %s\n", strerror(errno)); + exit(1); + } + + if (setuid(AID_SYSTEM) != 0) { + ALOGE("setuid: %s\n", strerror(errno)); + exit(1); + } + + // Generic idmap parameters + const char* argv[8]; + int argc = 0; + struct stat st; + + memset(argv, NULL, sizeof(argv)); + argv[argc++] = AssetManager::IDMAP_BIN; + argv[argc++] = "--scan"; + argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; + argv[argc++] = AssetManager::TARGET_APK_PATH; + argv[argc++] = AssetManager::IDMAP_DIR; + + // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, + // use OVERLAY_DIR/ in addition to OVERLAY_DIR. + char subdir[PROP_VALUE_MAX]; + int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir); + if (len > 0) { + String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir; + if (stat(overlayPath.string(), &st) == 0) { + argv[argc++] = overlayPath.string(); + } + } + if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::OVERLAY_DIR; + } + + // Finally, invoke idmap (if any overlay directory exists) + if (argc > 5) { + execv(AssetManager::IDMAP_BIN, (char* const*)argv); + ALOGE("failed to execv for idmap: %s", strerror(errno)); + exit(1); // should never get here + } else { + exit(0); + } + } + break; + default: // parent + waitpid(pid, NULL, 0); + break; + } } -// This is called by zygote (running as user root) as part of preloadResources. -static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { - switch (pid_t pid = fork()) { - case -1: - PLOG(ERROR) << "failed to fork for idmap"; - break; - - // child - case 0: { - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata; - - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - - capheader.version = _LINUX_CAPABILITY_VERSION; - capheader.pid = 0; - - if (capget(&capheader, &capdata) != 0) { - PLOG(ERROR) << "capget"; - exit(1); - } - - capdata.effective = capdata.permitted; - if (capset(&capheader, &capdata) != 0) { - PLOG(ERROR) << "capset"; - exit(1); - } - - if (setgid(AID_SYSTEM) != 0) { - PLOG(ERROR) << "setgid"; - exit(1); - } - - if (setuid(AID_SYSTEM) != 0) { - PLOG(ERROR) << "setuid"; - exit(1); - } - - // Generic idmap parameters - const char* argv[8]; - int argc = 0; - struct stat st; - - memset(argv, 0, sizeof(argv)); - argv[argc++] = AssetManager::IDMAP_BIN; - argv[argc++] = "--scan"; - argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; - argv[argc++] = AssetManager::TARGET_APK_PATH; - argv[argc++] = AssetManager::IDMAP_DIR; - - // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, - // use OVERLAY_DIR/ in addition to OVERLAY_DIR. - std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY, - ""); - if (!overlay_theme_path.empty()) { - overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path; - if (stat(overlay_theme_path.c_str(), &st) == 0) { - argv[argc++] = overlay_theme_path.c_str(); - } - } - - if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::OVERLAY_DIR; - } - - // Finally, invoke idmap (if any overlay directory exists) - if (argc > 5) { - execv(AssetManager::IDMAP_BIN, (char* const*)argv); - PLOG(ERROR) << "failed to execv for idmap"; - exit(1); // should never get here - } else { - exit(0); - } - } break; - - // parent - default: - waitpid(pid, nullptr, 0); - break; - } +// ---------------------------------------------------------------------------- + +// this guy is exported to other jni routines +AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj) +{ + jlong amHandle = env->GetLongField(obj, gAssetManagerOffsets.mObject); + AssetManager* am = reinterpret_cast(amHandle); + if (am != NULL) { + return am; + } + jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); + return NULL; } -static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref, - uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) { - env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType); - env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie, - ApkAssetsCookieToJavaCookie(cookie)); - env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data); - env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr); - env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref); - env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags); - if (config != nullptr) { - env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density); - } - return static_cast(ApkAssetsCookieToJavaCookie(cookie)); +static jlong android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz, + jstring fileName, jint mode) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + ALOGV("openAsset in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Empty file name"); + return -1; + } + + if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM + && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return -1; + } + + Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return -1; + } + + //printf("Created Asset Stream: %p\n", a); + + return reinterpret_cast(a); } -// ---------------------------------------------------------------------------- +static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets) +{ + off64_t startOffset, length; + int fd = a->openFileDescriptor(&startOffset, &length); + delete a; -// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance. -struct GuardedAssetManager : public ::AAssetManager { - Guarded guarded_assetmanager; -}; + if (fd < 0) { + jniThrowException(env, "java/io/FileNotFoundException", + "This file can not be opened as a file descriptor; it is probably compressed"); + return NULL; + } -::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { - jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject); - ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle); - if (am == nullptr) { - jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); - return nullptr; - } - return am; + jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0); + if (offsets == NULL) { + close(fd); + return NULL; + } + + offsets[0] = startOffset; + offsets[1] = length; + + env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0); + + jobject fileDesc = jniCreateFileDescriptor(env, fd); + if (fileDesc == NULL) { + close(fd); + return NULL; + } + + return newParcelFileDescriptor(env, fileDesc); } -Guarded* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) { - if (assetmanager == nullptr) { - return nullptr; - } - return &reinterpret_cast(assetmanager)->guarded_assetmanager; +static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz, + jstring fileName, jlongArray outOffsets) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ALOGV("openAssetFd in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return NULL; + } + + Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return NULL; + } + + //printf("Created Asset Stream: %p\n", a); + + return returnParcelFileDescriptor(env, a, outOffsets); } -Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { - return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager)); +static jlong android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz, + jint cookie, + jstring fileName, + jint mode) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return -1; + } + + if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM + && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return -1; + } + + Asset* a = cookie + ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), + (Asset::AccessMode)mode) + : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return -1; + } + + //printf("Created Asset Stream: %p\n", a); + + return reinterpret_cast(a); } -static Guarded& AssetManagerFromLong(jlong ptr) { - return *AssetManagerForNdkAssetManager(reinterpret_cast(ptr)); +static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz, + jint cookie, + jstring fileName, + jlongArray outOffsets) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ALOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return NULL; + } + + Asset* a = cookie + ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), Asset::ACCESS_RANDOM) + : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return NULL; + } + + //printf("Created Asset Stream: %p\n", a); + + return returnParcelFileDescriptor(env, a, outOffsets); } -static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr asset, - jlongArray out_offsets) { - off64_t start_offset, length; - int fd = asset->openFileDescriptor(&start_offset, &length); - asset.reset(); - - if (fd < 0) { - jniThrowException(env, "java/io/FileNotFoundException", - "This file can not be opened as a file descriptor; it is probably " - "compressed"); - return nullptr; - } - - jlong* offsets = reinterpret_cast(env->GetPrimitiveArrayCritical(out_offsets, 0)); - if (offsets == nullptr) { - close(fd); - return nullptr; - } - - offsets[0] = start_offset; - offsets[1] = length; - - env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0); - - jobject file_desc = jniCreateFileDescriptor(env, fd); - if (file_desc == nullptr) { - close(fd); - return nullptr; - } - return newParcelFileDescriptor(env, file_desc); +static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz, + jstring fileName) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return NULL; + } + + AssetDir* dir = am->openDir(fileName8.c_str()); + + if (dir == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return NULL; + } + + size_t N = dir->getFileCount(); + + jobjectArray array = env->NewObjectArray(dir->getFileCount(), + g_stringClass, NULL); + if (array == NULL) { + delete dir; + return NULL; + } + + for (size_t i=0; igetFileName(i); + jstring str = env->NewStringUTF(name.string()); + if (str == NULL) { + delete dir; + return NULL; + } + env->SetObjectArrayElement(array, i, str); + env->DeleteLocalRef(str); + } + + delete dir; + + return array; } -static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) { - return Asset::getGlobalCount(); +static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz, + jlong assetHandle) +{ + Asset* a = reinterpret_cast(assetHandle); + + //printf("Destroying Asset Stream: %p\n", a); + + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return; + } + + delete a; } -static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) { - String8 alloc = Asset::getAssetAllocations(); - if (alloc.length() <= 0) { - return nullptr; - } - return env->NewStringUTF(alloc.string()); +static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz, + jlong assetHandle) +{ + Asset* a = reinterpret_cast(assetHandle); + + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; + } + + uint8_t b; + ssize_t res = a->read(&b, 1); + return res == 1 ? b : -1; } -static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) { - // TODO(adamlesinski): Switch to AssetManager2. - return AssetManager::getGlobalCount(); +static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz, + jlong assetHandle, jbyteArray bArray, + jint off, jint len) +{ + Asset* a = reinterpret_cast(assetHandle); + + if (a == NULL || bArray == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; + } + + if (len == 0) { + return 0; + } + + jsize bLen = env->GetArrayLength(bArray); + if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); + return -1; + } + + jbyte* b = env->GetByteArrayElements(bArray, NULL); + ssize_t res = a->read(b+off, len); + env->ReleaseByteArrayElements(bArray, b, 0); + + if (res > 0) return static_cast(res); + + if (res < 0) { + jniThrowException(env, "java/io/IOException", ""); + } + return -1; } -static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) { - // AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and - // AssetManager2 in a contiguous block (GuardedAssetManager). - return reinterpret_cast(new GuardedAssetManager()); +static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz, + jlong assetHandle, + jlong offset, jint whence) +{ + Asset* a = reinterpret_cast(assetHandle); + + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; + } + + return a->seek( + offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)); } -static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - delete reinterpret_cast(ptr); +static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz, + jlong assetHandle) +{ + Asset* a = reinterpret_cast(assetHandle); + + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; + } + + return a->getLength(); } -static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr, - jobjectArray apk_assets_array, jboolean invalidate_caches) { - const jsize apk_assets_len = env->GetArrayLength(apk_assets_array); - std::vector apk_assets; - apk_assets.reserve(apk_assets_len); - for (jsize i = 0; i < apk_assets_len; i++) { - jobject obj = env->GetObjectArrayElement(apk_assets_array, i); - if (obj == nullptr) { - std::string msg = StringPrintf("ApkAssets at index %d is null", i); - jniThrowNullPointerException(env, msg.c_str()); - return; +static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz, + jlong assetHandle) +{ + Asset* a = reinterpret_cast(assetHandle); + + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; } - jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr); - if (env->ExceptionCheck()) { - return; + return a->getRemainingLength(); +} + +static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz, + jstring path, jboolean appAsLib) +{ + ScopedUtfChars path8(env, path); + if (path8.c_str() == NULL) { + return 0; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; } - apk_assets.push_back(reinterpret_cast(apk_assets_native_ptr)); - } - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - assetmanager->SetApkAssets(apk_assets, invalidate_caches); + int32_t cookie; + bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib); + + return (res) ? static_cast(cookie) : 0; } -static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc, - jstring locale, jint orientation, jint touchscreen, jint density, - jint keyboard, jint keyboard_hidden, jint navigation, - jint screen_width, jint screen_height, - jint smallest_screen_width_dp, jint screen_width_dp, - jint screen_height_dp, jint screen_layout, jint ui_mode, - jint color_mode, jint major_version) { - ResTable_config configuration; - memset(&configuration, 0, sizeof(configuration)); - configuration.mcc = static_cast(mcc); - configuration.mnc = static_cast(mnc); - configuration.orientation = static_cast(orientation); - configuration.touchscreen = static_cast(touchscreen); - configuration.density = static_cast(density); - configuration.keyboard = static_cast(keyboard); - configuration.inputFlags = static_cast(keyboard_hidden); - configuration.navigation = static_cast(navigation); - configuration.screenWidth = static_cast(screen_width); - configuration.screenHeight = static_cast(screen_height); - configuration.smallestScreenWidthDp = static_cast(smallest_screen_width_dp); - configuration.screenWidthDp = static_cast(screen_width_dp); - configuration.screenHeightDp = static_cast(screen_height_dp); - configuration.screenLayout = static_cast(screen_layout); - configuration.uiMode = static_cast(ui_mode); - configuration.colorMode = static_cast(color_mode); - configuration.sdkVersion = static_cast(major_version); - - if (locale != nullptr) { - ScopedUtfChars locale_utf8(env, locale); - CHECK(locale_utf8.c_str() != nullptr); - configuration.setBcp47Locale(locale_utf8.c_str()); - } - - // Constants duplicated from Java class android.content.res.Configuration. - static const jint kScreenLayoutRoundMask = 0x300; - static const jint kScreenLayoutRoundShift = 8; - - // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer - // in C++. We must extract the round qualifier out of the Java screenLayout and put it - // into screenLayout2. - configuration.screenLayout2 = - static_cast((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - assetmanager->SetConfiguration(configuration); +static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz, + jstring idmapPath) +{ + ScopedUtfChars idmapPath8(env, idmapPath); + if (idmapPath8.c_str() == NULL) { + return 0; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + int32_t cookie; + bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie); + + return (res) ? (jint)cookie : 0; } -static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); +static jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz, + jobject fileDescriptor, jstring debugPathName, + jboolean appAsLib) +{ + ScopedUtfChars debugPathName8(env, debugPathName); - jobject sparse_array = - env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor); + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (fd < 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); + return 0; + } - if (sparse_array == nullptr) { - // An exception is pending. - return nullptr; - } + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } - assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) { - jstring jpackage_name = env->NewStringUTF(package_name.c_str()); - if (jpackage_name == nullptr) { - // An exception is pending. - return; + int dupfd = ::dup(fd); + if (dupfd < 0) { + jniThrowIOException(env, errno); + return 0; } - env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast(package_id), - jpackage_name); - }); - return sparse_array; + int32_t cookie; + bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib); + + return (res) ? static_cast(cookie) : 0; } -static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) { - ScopedUtfChars path_utf8(env, path); - if (path_utf8.c_str() == nullptr) { - // This will throw NPE. - return nullptr; - } - - std::vector all_file_paths; - { - StringPiece normalized_path = path_utf8.c_str(); - if (normalized_path.data()[0] == '/') { - normalized_path = normalized_path.substr(1); - } - std::string root_path = StringPrintf("assets/%s", normalized_path.data()); - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - for (const ApkAssets* assets : assetmanager->GetApkAssets()) { - assets->ForEachFile(root_path, [&](const StringPiece& file_path, FileType type) { - if (type == FileType::kFileTypeRegular) { - all_file_paths.push_back(file_path.to_string()); - } - }); +static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_TRUE; + } + return am->isUpToDate() ? JNI_TRUE : JNI_FALSE; +} + +static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales) +{ + Vector locales; + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; } - } - jobjectArray array = env->NewObjectArray(all_file_paths.size(), g_stringClass, nullptr); - if (array == nullptr) { - return nullptr; - } + am->getLocales(&locales, includeSystemLocales); - jsize index = 0; - for (const std::string& file_path : all_file_paths) { - jstring java_string = env->NewStringUTF(file_path.c_str()); + const int N = locales.size(); - // Check for errors creating the strings (if malformed or no memory). - if (env->ExceptionCheck()) { - return nullptr; + jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL); + if (result == NULL) { + return NULL; } - env->SetObjectArrayElement(array, index++, java_string); + for (int i=0; iNewStringUTF(locales[i].string()); + if (str == NULL) { + return NULL; + } + env->SetObjectArrayElement(result, i, str); + env->DeleteLocalRef(str); + } - // If we have a large amount of string in our array, we might overflow the - // local reference table of the VM. - env->DeleteLocalRef(java_string); - } - return array; + return result; } -static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, - jint access_mode) { - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return 0; - } - - if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && - access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return 0; - } - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr asset = - assetmanager->Open(asset_path_utf8.c_str(), static_cast(access_mode)); - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return 0; - } - return reinterpret_cast(asset.release()); +static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz) +{ + return getLocales(env, clazz, true /* include system locales */); } -static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, - jlongArray out_offsets) { - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return nullptr; - } - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return nullptr; - } - return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); +static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz) +{ + return getLocales(env, clazz, false /* don't include system locales */); } -static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, - jstring asset_path, jint access_mode) { - ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return 0; - } - - if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && - access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return 0; - } - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr asset; - if (cookie != kInvalidCookie) { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, - static_cast(access_mode)); - } else { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), - static_cast(access_mode)); - } - - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return 0; - } - return reinterpret_cast(asset.release()); -} +static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) { + jobject result = env->NewObject(gConfigurationOffsets.classObject, + gConfigurationOffsets.constructor); + if (result == NULL) { + return NULL; + } -static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, - jstring asset_path, jlongArray out_offsets) { - ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return nullptr; - } - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr asset; - if (cookie != kInvalidCookie) { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); - } else { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); - } - - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return nullptr; - } - return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); -} + env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, + config.smallestScreenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); -static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie, - jstring asset_path) { - ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return 0; - } - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr asset; - if (cookie != kInvalidCookie) { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); - } else { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie); - } - - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return 0; - } - - // May be nullptr. - const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie); - - std::unique_ptr xml_tree = util::make_unique(dynamic_ref_table); - status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); - asset.reset(); - - if (err != NO_ERROR) { - jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); - return 0; - } - return reinterpret_cast(xml_tree.release()); + return result; } -static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, - jshort density, jobject typed_value, - jboolean resolve_references) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - ApkAssetsCookie cookie = - assetmanager->GetResource(static_cast(resid), false /*may_be_bag*/, - static_cast(density), &value, &selected_config, &flags); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - - uint32_t ref = static_cast(resid); - if (resolve_references) { - cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - } - return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value); +static jobjectArray getSizeConfigurationsInternal(JNIEnv* env, + const Vector& configs) { + const int N = configs.size(); + jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL); + if (result == NULL) { + return NULL; + } + + for (int i=0; iDeleteLocalRef(result); + return NULL; + } + + env->SetObjectArrayElement(result, i, config); + env->DeleteLocalRef(config); + } + + return result; } -static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, - jint bag_entry_id, jobject typed_value) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - - uint32_t type_spec_flags = bag->type_spec_flags; - ApkAssetsCookie cookie = kInvalidCookie; - const Res_value* bag_value = nullptr; - for (const ResolvedBag::Entry& entry : bag) { - if (entry.key == static_cast(bag_entry_id)) { - cookie = entry.cookie; - bag_value = &entry.value; - - // Keep searching (the old implementation did that). - } - } - - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - - Res_value value = *bag_value; - uint32_t ref = static_cast(resid); - ResTable_config selected_config; - cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value); +static jobjectArray android_content_AssetManager_getSizeConfigurations(JNIEnv* env, jobject clazz) { + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + const ResTable& res(am->getResources()); + Vector configs; + res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */); + + return getSizeConfigurationsInternal(env, configs); } -static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return nullptr; - } - - jintArray array = env->NewIntArray(bag->entry_count); - if (env->ExceptionCheck()) { - return nullptr; - } - - for (uint32_t i = 0; i < bag->entry_count; i++) { - jint attr_resid = bag->entries[i].key; - env->SetIntArrayRegion(array, i, 1, &attr_resid); - } - return array; +static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz, + jint mcc, jint mnc, + jstring locale, jint orientation, + jint touchscreen, jint density, + jint keyboard, jint keyboardHidden, + jint navigation, + jint screenWidth, jint screenHeight, + jint smallestScreenWidthDp, + jint screenWidthDp, jint screenHeightDp, + jint screenLayout, jint uiMode, + jint colorMode, jint sdkVersion) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return; + } + + ResTable_config config; + memset(&config, 0, sizeof(config)); + + const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL; + + // Constants duplicated from Java class android.content.res.Configuration. + static const jint kScreenLayoutRoundMask = 0x300; + static const jint kScreenLayoutRoundShift = 8; + + config.mcc = (uint16_t)mcc; + config.mnc = (uint16_t)mnc; + config.orientation = (uint8_t)orientation; + config.touchscreen = (uint8_t)touchscreen; + config.density = (uint16_t)density; + config.keyboard = (uint8_t)keyboard; + config.inputFlags = (uint8_t)keyboardHidden; + config.navigation = (uint8_t)navigation; + config.screenWidth = (uint16_t)screenWidth; + config.screenHeight = (uint16_t)screenHeight; + config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp; + config.screenWidthDp = (uint16_t)screenWidthDp; + config.screenHeightDp = (uint16_t)screenHeightDp; + config.screenLayout = (uint8_t)screenLayout; + config.uiMode = (uint8_t)uiMode; + config.colorMode = (uint8_t)colorMode; + config.sdkVersion = (uint16_t)sdkVersion; + config.minorVersion = 0; + + // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer + // in C++. We must extract the round qualifier out of the Java screenLayout and put it + // into screenLayout2. + config.screenLayout2 = + (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); + + am->setConfiguration(config, locale8); + + if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8); } -static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, - jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return nullptr; - } - - jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr); - if (array == nullptr) { - return nullptr; - } - - for (uint32_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - - // Resolve any references to their final value. - Res_value value = entry.value; - ResTable_config selected_config; - uint32_t flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - return nullptr; - } - - if (value.dataType == Res_value::TYPE_STRING) { - const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie]; - const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool(); - - jstring java_string = nullptr; - size_t str_len; - const char* str_utf8 = pool->string8At(value.data, &str_len); - if (str_utf8 != nullptr) { - java_string = env->NewStringUTF(str_utf8); - } else { - const char16_t* str_utf16 = pool->stringAt(value.data, &str_len); - java_string = env->NewString(reinterpret_cast(str_utf16), str_len); - } - - // Check for errors creating the strings (if malformed or no memory). - if (env->ExceptionCheck()) { - return nullptr; - } - - env->SetObjectArrayElement(array, i, java_string); - - // If we have a large amount of string in our array, we might overflow the - // local reference table of the VM. - env->DeleteLocalRef(java_string); - } - } - return array; +static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz, + jstring name, + jstring defType, + jstring defPackage) +{ + ScopedStringChars name16(env, name); + if (name16.get() == NULL) { + return 0; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + const char16_t* defType16 = reinterpret_cast(defType) + ? reinterpret_cast(env->GetStringChars(defType, NULL)) + : NULL; + jsize defTypeLen = defType + ? env->GetStringLength(defType) : 0; + const char16_t* defPackage16 = reinterpret_cast(defPackage) + ? reinterpret_cast(env->GetStringChars(defPackage, + NULL)) + : NULL; + jsize defPackageLen = defPackage + ? env->GetStringLength(defPackage) : 0; + + jint ident = am->getResources().identifierForName( + reinterpret_cast(name16.get()), name16.size(), + defType16, defTypeLen, defPackage16, defPackageLen); + + if (defPackage16) { + env->ReleaseStringChars(defPackage, + reinterpret_cast(defPackage16)); + } + if (defType16) { + env->ReleaseStringChars(defType, + reinterpret_cast(defType16)); + } + + return ident; } -static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr, - jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return nullptr; - } - - jintArray array = env->NewIntArray(bag->entry_count * 2); - if (array == nullptr) { - return nullptr; - } - - jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); - if (buffer == nullptr) { - return nullptr; - } - - for (size_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - Res_value value = entry.value; - ResTable_config selected_config; - uint32_t flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); - return nullptr; - } - - jint string_index = -1; - if (value.dataType == Res_value::TYPE_STRING) { - string_index = static_cast(value.data); - } - - buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie); - buffer[(i * 2) + 1] = string_index; - } - env->ReleasePrimitiveArrayCritical(array, buffer, 0); - return array; +static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, true, &name)) { + return NULL; + } + + String16 str; + if (name.package != NULL) { + str.setTo(name.package, name.packageLen); + } + if (name.type8 != NULL || name.type != NULL) { + if (str.size() > 0) { + char16_t div = ':'; + str.append(&div, 1); + } + if (name.type8 != NULL) { + str.append(String16(name.type8, name.typeLen)); + } else { + str.append(name.type, name.typeLen); + } + } + if (name.name8 != NULL || name.name != NULL) { + if (str.size() > 0) { + char16_t div = '/'; + str.append(&div, 1); + } + if (name.name8 != NULL) { + str.append(String16(name.name8, name.nameLen)); + } else { + str.append(name.name, name.nameLen); + } + } + + return env->NewString((const jchar*)str.string(), str.size()); } -static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return nullptr; - } - - jintArray array = env->NewIntArray(bag->entry_count); - if (array == nullptr) { - return nullptr; - } - - jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); - if (buffer == nullptr) { - return nullptr; - } - - for (size_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - Res_value value = entry.value; - ResTable_config selected_config; - uint32_t flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); - return nullptr; - } - - if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) { - buffer[i] = static_cast(value.data); - } - } - env->ReleasePrimitiveArrayCritical(array, buffer, 0); - return array; +static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, true, &name)) { + return NULL; + } + + if (name.package != NULL) { + return env->NewString((const jchar*)name.package, name.packageLen); + } + + return NULL; } -static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return -1; - } - return static_cast(bag->entry_count); +static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, true, &name)) { + return NULL; + } + + if (name.type8 != NULL) { + return env->NewStringUTF(name.type8); + } + + if (name.type != NULL) { + return env->NewString((const jchar*)name.type, name.typeLen); + } + + return NULL; } -static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, - jintArray out_data) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return -1; - } +static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } - const jsize out_data_length = env->GetArrayLength(out_data); - if (env->ExceptionCheck()) { - return -1; - } + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, true, &name)) { + return NULL; + } - if (static_cast(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough"); - return -1; - } + if (name.name8 != NULL) { + return env->NewStringUTF(name.name8); + } - jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(out_data, nullptr)); - if (buffer == nullptr) { - return -1; - } - - jint* cursor = buffer; - for (size_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - Res_value value = entry.value; - ResTable_config selected_config; - selected_config.density = 0; - uint32_t flags = bag->type_spec_flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT); - return -1; - } - - // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; - } - - cursor[STYLE_TYPE] = static_cast(value.dataType); - cursor[STYLE_DATA] = static_cast(value.data); - cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); - cursor[STYLE_RESOURCE_ID] = static_cast(ref); - cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast(flags); - cursor[STYLE_DENSITY] = static_cast(selected_config.density); - cursor += STYLE_NUM_ENTRIES; - } - env->ReleasePrimitiveArrayCritical(out_data, buffer, 0); - return static_cast(bag->entry_count); + if (name.name != NULL) { + return env->NewString((const jchar*)name.name, name.nameLen); + } + + return NULL; } -static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name, - jstring def_type, jstring def_package) { - ScopedUtfChars name_utf8(env, name); - if (name_utf8.c_str() == nullptr) { - // This will throw NPE. - return 0; - } - - std::string type; - if (def_type != nullptr) { - ScopedUtfChars type_utf8(env, def_type); - CHECK(type_utf8.c_str() != nullptr); - type = type_utf8.c_str(); - } - - std::string package; - if (def_package != nullptr) { - ScopedUtfChars package_utf8(env, def_package); - CHECK(package_utf8.c_str() != nullptr); - package = package_utf8.c_str(); - } - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - return static_cast(assetmanager->GetResourceId(name_utf8.c_str(), type, package)); +static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz, + jint ident, + jshort density, + jobject outValue, + jboolean resolve) +{ + if (outValue == NULL) { + jniThrowNullPointerException(env, "outValue"); + return 0; + } + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + const ResTable& res(am->getResources()); + + Res_value value; + ResTable_config config; + uint32_t typeSpecFlags; + ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return 0; + } + } + uint32_t ref = ident; + if (resolve) { + block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return 0; + } + } + } + if (block >= 0) { + return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config); + } + + return static_cast(block); } -static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast(resid), &name)) { - return nullptr; - } +static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz, + jint ident, jint bagEntryId, + jobject outValue, jboolean resolve) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + const ResTable& res(am->getResources()); - std::string result; - if (name.package != nullptr) { - result.append(name.package, name.package_len); - } + // Now lock down the resource object and start pulling stuff from it. + res.lock(); - if (name.type != nullptr || name.type16 != nullptr) { - if (!result.empty()) { - result += ":"; + ssize_t block = -1; + Res_value value; + + const ResTable::bag_entry* entry = NULL; + uint32_t typeSpecFlags; + ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags); + + for (ssize_t i=0; imap.name.ident) { + block = entry->stringBlock; + value = entry->map.value; + } + entry++; } - if (name.type != nullptr) { - result.append(name.type, name.type_len); - } else { - result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len)); + res.unlock(); + + if (block < 0) { + return static_cast(block); } - } - if (name.entry != nullptr || name.entry16 != nullptr) { - if (!result.empty()) { - result += "/"; + uint32_t ref = ident; + if (resolve) { + block = res.resolveReference(&value, block, &ref, &typeSpecFlags); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return 0; + } + } } + if (block >= 0) { + return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags); + } + + return static_cast(block); +} - if (name.entry != nullptr) { - result.append(name.entry, name.entry_len); - } else { - result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); +static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; } - } - return env->NewStringUTF(result.c_str()); + return am->getResources().getTableCount(); } -static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast(resid), &name)) { - return nullptr; - } - - if (name.package != nullptr) { - return env->NewStringUTF(name.package); - } - return nullptr; +static jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz, + jint block) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + return reinterpret_cast(am->getResources().getTableStringBlock(block)); } -static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast(resid), &name)) { - return nullptr; - } - - if (name.type != nullptr) { - return env->NewStringUTF(name.type); - } else if (name.type16 != nullptr) { - return env->NewString(reinterpret_cast(name.type16), name.type_len); - } - return nullptr; +static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz, + jint cookie) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + String8 name(am->getAssetPath(static_cast(cookie))); + if (name.length() == 0) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name"); + return NULL; + } + jstring str = env->NewStringUTF(name.string()); + return str; } -static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast(resid), &name)) { - return nullptr; - } - - if (name.entry != nullptr) { - return env->NewStringUTF(name.entry); - } else if (name.entry16 != nullptr) { - return env->NewString(reinterpret_cast(name.entry16), name.entry_len); - } - return nullptr; +static jobject android_content_AssetManager_getAssignedPackageIdentifiers(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + const ResTable& res = am->getResources(); + + jobject sparseArray = env->NewObject(gSparseArrayOffsets.classObject, + gSparseArrayOffsets.constructor); + const size_t N = res.getBasePackageCount(); + for (size_t i = 0; i < N; i++) { + const String16 name = res.getBasePackageName(i); + env->CallVoidMethod( + sparseArray, gSparseArrayOffsets.put, + static_cast(res.getBasePackageId(i)), + env->NewString(reinterpret_cast(name.string()), + name.size())); + } + return sparseArray; } -static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr, - jboolean exclude_system) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::set locales = - assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/); - - jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr); - if (array == nullptr) { - return nullptr; - } - - size_t idx = 0; - for (const std::string& locale : locales) { - jstring java_string = env->NewStringUTF(locale.c_str()); - if (java_string == nullptr) { - return nullptr; - } - env->SetObjectArrayElement(array, idx++, java_string); - env->DeleteLocalRef(java_string); - } - return array; +static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + return reinterpret_cast(new ResTable::Theme(am->getResources())); } -static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) { - jobject result = - env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor); - if (result == nullptr) { - return nullptr; - } - - env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, - config.smallestScreenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); - return result; +static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz, + jlong themeHandle) +{ + ResTable::Theme* theme = reinterpret_cast(themeHandle); + delete theme; } -static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::set configurations = - assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/); +static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz, + jlong themeHandle, + jint styleRes, + jboolean force) +{ + ResTable::Theme* theme = reinterpret_cast(themeHandle); + theme->applyStyle(styleRes, force ? true : false); +} - jobjectArray array = - env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr); - if (array == nullptr) { - return nullptr; - } +static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz, + jlong destHandle, jlong srcHandle) +{ + ResTable::Theme* dest = reinterpret_cast(destHandle); + ResTable::Theme* src = reinterpret_cast(srcHandle); + dest->setTo(*src); +} + +static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle) +{ + ResTable::Theme* theme = reinterpret_cast(themeHandle); + theme->clear(); +} - size_t idx = 0; - for (const ResTable_config& configuration : configurations) { - jobject java_configuration = ConstructConfigurationObject(env, configuration); - if (java_configuration == nullptr) { - return nullptr; +static jint android_content_AssetManager_loadThemeAttributeValue( + JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve) +{ + ResTable::Theme* theme = reinterpret_cast(themeHandle); + const ResTable& res(theme->getResTable()); + + Res_value value; + // XXX value could be different in different configs! + uint32_t typeSpecFlags = 0; + ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags); + uint32_t ref = 0; + if (resolve) { + block = res.resolveReference(&value, block, &ref, &typeSpecFlags); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return 0; + } + } } + return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block; +} - env->SetObjectArrayElement(array, idx++, java_configuration); - env->DeleteLocalRef(java_configuration); - } - return array; +static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz, + jlong themeHandle) +{ + ResTable::Theme* theme = reinterpret_cast(themeHandle); + return theme->getChangingConfigurations(); } -static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr, - jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - - ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); - uint32_t* out_values = reinterpret_cast(out_values_ptr); - uint32_t* out_indices = reinterpret_cast(out_indices_ptr); - - jsize attrs_len = env->GetArrayLength(java_attrs); - jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); - if (attrs == nullptr) { - return; - } - - ApplyStyle(theme, xml_parser, static_cast(def_style_attr), - static_cast(def_style_resid), reinterpret_cast(attrs), attrs_len, - out_values, out_indices); - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); +static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz, + jlong themeHandle, jint pri, + jstring tag, jstring prefix) +{ + ResTable::Theme* theme = reinterpret_cast(themeHandle); + const ResTable& res(theme->getResTable()); + (void)res; + + // XXX Need to use params. + theme->dumpToLog(); } -static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint def_style_attr, jint def_style_resid, jintArray java_values, - jintArray java_attrs, jintArray out_java_values, - jintArray out_java_indices) { - const jsize attrs_len = env->GetArrayLength(java_attrs); - const jsize out_values_len = env->GetArrayLength(out_java_values); - if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); - return JNI_FALSE; - } - - jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); - if (attrs == nullptr) { - return JNI_FALSE; - } - - jint* values = nullptr; - jsize values_len = 0; - if (java_values != nullptr) { - values_len = env->GetArrayLength(java_values); - values = reinterpret_cast(env->GetPrimitiveArrayCritical(java_values, nullptr)); - if (values == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - return JNI_FALSE; - } - } - - jint* out_values = - reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); - if (out_values == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - if (values != nullptr) { - env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); - } - return JNI_FALSE; - } - - jint* out_indices = nullptr; - if (out_java_indices != nullptr) { - jsize out_indices_len = env->GetArrayLength(out_java_indices); - if (out_indices_len > attrs_len) { - out_indices = - reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); - if (out_indices == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - if (values != nullptr) { - env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); - } - env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); +static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz, + jlong themeToken, + jint defStyleAttr, + jint defStyleRes, + jintArray inValues, + jintArray attrs, + jintArray outValues, + jintArray outIndices) +{ + if (themeToken == 0) { + jniThrowNullPointerException(env, "theme token"); return JNI_FALSE; - } - } - } - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - - bool result = ResolveAttrs( - theme, static_cast(def_style_attr), static_cast(def_style_resid), - reinterpret_cast(values), values_len, reinterpret_cast(attrs), - attrs_len, reinterpret_cast(out_values), reinterpret_cast(out_indices)); - if (out_indices != nullptr) { - env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); - } - - env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); - if (values != nullptr) { - env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); - } - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - return result ? JNI_TRUE : JNI_FALSE; -} + } + if (attrs == NULL) { + jniThrowNullPointerException(env, "attrs"); + return JNI_FALSE; + } + if (outValues == NULL) { + jniThrowNullPointerException(env, "out values"); + return JNI_FALSE; + } -static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, - jlong xml_parser_ptr, jintArray java_attrs, - jintArray out_java_values, jintArray out_java_indices) { - const jsize attrs_len = env->GetArrayLength(java_attrs); - const jsize out_values_len = env->GetArrayLength(out_java_values); - if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); - return JNI_FALSE; - } - - jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); - if (attrs == nullptr) { - return JNI_FALSE; - } - - jint* out_values = - reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); - if (out_values == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - return JNI_FALSE; - } - - jint* out_indices = nullptr; - if (out_java_indices != nullptr) { - jsize out_indices_len = env->GetArrayLength(out_java_indices); - if (out_indices_len > attrs_len) { - out_indices = - reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); - if (out_indices == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); + const jsize NI = env->GetArrayLength(attrs); + const jsize NV = env->GetArrayLength(outValues); + if (NV < (NI*STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); return JNI_FALSE; - } } - } - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); + jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); + if (src == NULL) { + return JNI_FALSE; + } - bool result = RetrieveAttributes(assetmanager.get(), xml_parser, - reinterpret_cast(attrs), attrs_len, - reinterpret_cast(out_values), - reinterpret_cast(out_indices)); + jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0); + const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues); - if (out_indices != nullptr) { - env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); - } - env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - return result ? JNI_TRUE : JNI_FALSE; -} + jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + if (baseDest == NULL) { + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + return JNI_FALSE; + } -static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - return reinterpret_cast(assetmanager->NewTheme().release()); -} + jint* indices = NULL; + if (outIndices != NULL) { + if (env->GetArrayLength(outIndices) > NI) { + indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); + } + } + + ResTable::Theme* theme = reinterpret_cast(themeToken); + bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes, + (uint32_t*) srcValues, NSV, + (uint32_t*) src, NI, + (uint32_t*) baseDest, + (uint32_t*) indices); -static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { - delete reinterpret_cast(theme_ptr); + if (indices != NULL) { + env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); + } + env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); + env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0); + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + return result ? JNI_TRUE : JNI_FALSE; } -static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint resid, jboolean force) { - // AssetManager is accessed via the theme, so grab an explicit lock here. - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - theme->ApplyStyle(static_cast(resid), force); - - // TODO(adamlesinski): Consider surfacing exception when result is failure. - // CTS currently expects no exceptions from this method. - // std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid); - // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str()); +static void android_content_AssetManager_applyStyle(JNIEnv* env, jobject, jlong themeToken, + jint defStyleAttr, jint defStyleRes, jlong xmlParserToken, jintArray attrsObj, jint length, + jlong outValuesAddress, jlong outIndicesAddress) { + jint* attrs = env->GetIntArrayElements(attrsObj, 0); + ResTable::Theme* theme = reinterpret_cast(themeToken); + ResXMLParser* xmlParser = reinterpret_cast(xmlParserToken); + uint32_t* outValues = reinterpret_cast(static_cast(outValuesAddress)); + uint32_t* outIndices = reinterpret_cast(static_cast(outIndicesAddress)); + ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes, + reinterpret_cast(attrs), length, outValues, outIndices); + env->ReleaseIntArrayElements(attrsObj, attrs, JNI_ABORT); } -static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr, - jlong src_theme_ptr) { - Theme* dst_theme = reinterpret_cast(dst_theme_ptr); - Theme* src_theme = reinterpret_cast(src_theme_ptr); - if (!dst_theme->SetTo(*src_theme)) { - jniThrowException(env, "java/lang/IllegalArgumentException", - "Themes are from different AssetManagers"); - } +static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz, + jlong xmlParserToken, + jintArray attrs, + jintArray outValues, + jintArray outIndices) +{ + if (xmlParserToken == 0) { + jniThrowNullPointerException(env, "xmlParserToken"); + return JNI_FALSE; + } + if (attrs == NULL) { + jniThrowNullPointerException(env, "attrs"); + return JNI_FALSE; + } + if (outValues == NULL) { + jniThrowNullPointerException(env, "out values"); + return JNI_FALSE; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_FALSE; + } + const ResTable& res(am->getResources()); + ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken; + + const jsize NI = env->GetArrayLength(attrs); + const jsize NV = env->GetArrayLength(outValues); + if (NV < (NI*STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); + return JNI_FALSE; + } + + jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); + if (src == NULL) { + return JNI_FALSE; + } + + jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + if (baseDest == NULL) { + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + return JNI_FALSE; + } + + jint* indices = NULL; + if (outIndices != NULL) { + if (env->GetArrayLength(outIndices) > NI) { + indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); + } + } + + bool result = RetrieveAttributes(&res, xmlParser, + (uint32_t*) src, NI, + (uint32_t*) baseDest, + (uint32_t*) indices); + + if (indices != NULL) { + env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); + } + env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + return result ? JNI_TRUE : JNI_FALSE; } -static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { - reinterpret_cast(theme_ptr)->Clear(); +static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz, + jint id) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + const ResTable& res(am->getResources()); + + res.lock(); + const ResTable::bag_entry* defStyleEnt = NULL; + ssize_t bagOff = res.getBagLocked(id, &defStyleEnt); + res.unlock(); + + return static_cast(bagOff); } -static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint resid, jobject typed_value, - jboolean resolve_references) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - - Res_value value; - uint32_t flags; - ApkAssetsCookie cookie = theme->GetAttribute(static_cast(resid), &value, &flags); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - - uint32_t ref = 0u; - if (resolve_references) { - ResTable_config selected_config; - cookie = - theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - } - return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value); +static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz, + jint id, + jintArray outValues) +{ + if (outValues == NULL) { + jniThrowNullPointerException(env, "out values"); + return JNI_FALSE; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_FALSE; + } + const ResTable& res(am->getResources()); + ResTable_config config; + Res_value value; + ssize_t block; + + const jsize NV = env->GetArrayLength(outValues); + + jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + jint* dest = baseDest; + if (dest == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", ""); + return JNI_FALSE; + } + + // Now lock down the resource object and start pulling stuff from it. + res.lock(); + + const ResTable::bag_entry* arrayEnt = NULL; + uint32_t arrayTypeSetFlags = 0; + ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags); + const ResTable::bag_entry* endArrayEnt = arrayEnt + + (bagOff >= 0 ? bagOff : 0); + + int i = 0; + uint32_t typeSetFlags; + while (i < NV && arrayEnt < endArrayEnt) { + block = arrayEnt->stringBlock; + typeSetFlags = arrayTypeSetFlags; + config.density = 0; + value = arrayEnt->map.value; + + uint32_t resid = 0; + if (value.dataType != Res_value::TYPE_NULL) { + // Take care of resolving the found resource to its final value. + //printf("Resolving attribute reference\n"); + ssize_t newBlock = res.resolveReference(&value, block, &resid, + &typeSetFlags, &config); + if (kThrowOnBadId) { + if (newBlock == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return JNI_FALSE; + } + } + if (newBlock >= 0) block = newBlock; + } + + // Deal with the special @null value -- it turns back to TYPE_NULL. + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + } + + //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data); + + // Write the final value back to Java. + dest[STYLE_TYPE] = value.dataType; + dest[STYLE_DATA] = value.data; + dest[STYLE_ASSET_COOKIE] = reinterpret_cast(res.getTableCookie(block)); + dest[STYLE_RESOURCE_ID] = resid; + dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags; + dest[STYLE_DENSITY] = config.density; + dest += STYLE_NUM_ENTRIES; + i+= STYLE_NUM_ENTRIES; + arrayEnt++; + } + + i /= STYLE_NUM_ENTRIES; + + res.unlock(); + + env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); + + return i; } -static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint priority, jstring tag, jstring prefix) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - (void) theme; - (void) priority; - (void) tag; - (void) prefix; +static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz, + jint cookie, + jstring fileName) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return 0; + } + + int32_t assetCookie = static_cast(cookie); + Asset* a = assetCookie + ? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER) + : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return 0; + } + + const DynamicRefTable* dynamicRefTable = + am->getResources().getDynamicRefTableForCookie(assetCookie); + ResXMLTree* block = new ResXMLTree(dynamicRefTable); + status_t err = block->setTo(a->getBuffer(true), a->getLength(), true); + a->close(); + delete a; + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + + return reinterpret_cast(block); } -static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/, - jlong theme_ptr) { - Theme* theme = reinterpret_cast(theme_ptr); - return static_cast(theme->GetChangingConfigurations()); +static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz, + jint arrayResId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(arrayResId, &startOfBag); + if (N < 0) { + return NULL; + } + + jintArray array = env->NewIntArray(N * 2); + if (array == NULL) { + res.unlockBag(startOfBag); + return NULL; + } + + Res_value value; + const ResTable::bag_entry* bag = startOfBag; + for (size_t i = 0, j = 0; ((ssize_t)i)map.value; + + // Take care of resolving the found resource to its final value. + stringBlock = res.resolveReference(&value, bag->stringBlock, NULL); + if (value.dataType == Res_value::TYPE_STRING) { + stringIndex = value.data; + } + + if (kThrowOnBadId) { + if (stringBlock == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return array; + } + } + + //todo: It might be faster to allocate a C array to contain + // the blocknums and indices, put them in there and then + // do just one SetIntArrayRegion() + env->SetIntArrayRegion(array, j, 1, &stringBlock); + env->SetIntArrayRegion(array, j + 1, 1, &stringIndex); + j = j + 2; + } + res.unlockBag(startOfBag); + return array; } -static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - delete reinterpret_cast(asset_ptr); +static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz, + jint arrayResId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(arrayResId, &startOfBag); + if (N < 0) { + return NULL; + } + + jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL); + if (env->ExceptionCheck()) { + res.unlockBag(startOfBag); + return NULL; + } + + Res_value value; + const ResTable::bag_entry* bag = startOfBag; + size_t strLen = 0; + for (size_t i=0; ((ssize_t)i)map.value; + jstring str = NULL; + + // Take care of resolving the found resource to its final value. + ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return array; + } + } + if (value.dataType == Res_value::TYPE_STRING) { + const ResStringPool* pool = res.getTableStringBlock(block); + const char* str8 = pool->string8At(value.data, &strLen); + if (str8 != NULL) { + str = env->NewStringUTF(str8); + } else { + const char16_t* str16 = pool->stringAt(value.data, &strLen); + str = env->NewString(reinterpret_cast(str16), + strLen); + } + + // If one of our NewString{UTF} calls failed due to memory, an + // exception will be pending. + if (env->ExceptionCheck()) { + res.unlockBag(startOfBag); + return NULL; + } + + env->SetObjectArrayElement(array, i, str); + + // str is not NULL at that point, otherwise ExceptionCheck would have been true. + // If we have a large amount of strings in our array, we might + // overflow the local reference table of the VM. + env->DeleteLocalRef(str); + } + } + res.unlockBag(startOfBag); + return array; } -static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - Asset* asset = reinterpret_cast(asset_ptr); - uint8_t b; - ssize_t res = asset->read(&b, sizeof(b)); - return res == sizeof(b) ? static_cast(b) : -1; +static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz, + jint arrayResId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(arrayResId, &startOfBag); + if (N < 0) { + return NULL; + } + + jintArray array = env->NewIntArray(N); + if (array == NULL) { + res.unlockBag(startOfBag); + return NULL; + } + + Res_value value; + const ResTable::bag_entry* bag = startOfBag; + for (size_t i=0; ((ssize_t)i)map.value; + + // Take care of resolving the found resource to its final value. + ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return array; + } + } + if (value.dataType >= Res_value::TYPE_FIRST_INT + && value.dataType <= Res_value::TYPE_LAST_INT) { + int intVal = value.data; + env->SetIntArrayRegion(array, i, 1, &intVal); + } + } + res.unlockBag(startOfBag); + return array; } -static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer, - jint offset, jint len) { - if (len == 0) { - return 0; - } +static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz, + jint styleId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); - jsize buffer_len = env->GetArrayLength(java_buffer); - if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len || - (offset + len) > buffer_len) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); - return -1; - } + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(styleId, &startOfBag); + if (N < 0) { + return NULL; + } - Asset* asset = reinterpret_cast(asset_ptr); + jintArray array = env->NewIntArray(N); + if (array == NULL) { + res.unlockBag(startOfBag); + return NULL; + } - ScopedByteArrayRW byte_array(env, java_buffer); - if (byte_array.get() == nullptr) { - return -1; - } + const ResTable::bag_entry* bag = startOfBag; + for (size_t i=0; ((ssize_t)i)map.name.ident; + env->SetIntArrayRegion(array, i, 1, &resourceId); + } + res.unlockBag(startOfBag); + return array; +} - ssize_t res = asset->read(byte_array.get(), buffer_len); - if (res < 0) { - jniThrowException(env, "java/io/IOException", ""); - return -1; - } - return res > 0 ? static_cast(res) : -1; +static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem) +{ + if (isSystem) { + verifySystemIdmaps(); + } + AssetManager* am = new AssetManager(); + if (am == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", ""); + return; + } + + am->addDefaultAssets(); + + ALOGV("Created AssetManager %p for Java object %p\n", am, clazz); + env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast(am)); +} + +static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz) +{ + AssetManager* am = (AssetManager*) + (env->GetLongField(clazz, gAssetManagerOffsets.mObject)); + ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz); + if (am != NULL) { + delete am; + env->SetLongField(clazz, gAssetManagerOffsets.mObject, 0); + } } -static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset, - jint whence) { - Asset* asset = reinterpret_cast(asset_ptr); - return static_cast(asset->seek( - static_cast(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)))); +static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz) +{ + return Asset::getGlobalCount(); } -static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - Asset* asset = reinterpret_cast(asset_ptr); - return static_cast(asset->getLength()); +static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz) +{ + String8 alloc = Asset::getAssetAllocations(); + if (alloc.length() <= 0) { + return NULL; + } + + jstring str = env->NewStringUTF(alloc.string()); + return str; } -static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - Asset* asset = reinterpret_cast(asset_ptr); - return static_cast(asset->getRemainingLength()); +static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz) +{ + return AssetManager::getGlobalCount(); } // ---------------------------------------------------------------------------- -// JNI registration. +/* + * JNI registration. + */ static const JNINativeMethod gAssetManagerMethods[] = { - // AssetManager setup methods. - {"nativeCreate", "()J", (void*)NativeCreate}, - {"nativeDestroy", "(J)V", (void*)NativeDestroy}, - {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets}, - {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V", - (void*)NativeSetConfiguration}, - {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;", - (void*)NativeGetAssignedPackageIdentifiers}, - - // AssetManager file methods. - {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList}, - {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset}, - {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*)NativeOpenAssetFd}, - {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset}, - {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*)NativeOpenNonAssetFd}, - {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset}, - - // AssetManager resource methods. - {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue}, - {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I", - (void*)NativeGetResourceBagValue}, - {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes}, - {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;", - (void*)NativeGetResourceStringArray}, - {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo}, - {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray}, - {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize}, - {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray}, - - // AssetManager resource name/ID methods. - {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", - (void*)NativeGetResourceIdentifier}, - {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName}, - {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName}, - {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName}, - {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName}, - {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales}, - {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;", - (void*)NativeGetSizeConfigurations}, - - // Style attribute related methods. - {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle}, - {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs}, - {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes}, - - // Theme related methods. - {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate}, - {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy}, - {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle}, - {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy}, - {"nativeThemeClear", "(J)V", (void*)NativeThemeClear}, - {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I", - (void*)NativeThemeGetAttributeValue}, - {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump}, - {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations}, - - // AssetInputStream methods. - {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy}, - {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar}, - {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead}, - {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek}, - {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength}, - {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength}, - - // System/idmap related methods. - {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps}, - - // Global management/debug methods. - {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount}, - {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations}, - {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount}, + /* name, signature, funcPtr */ + + // Basic asset stuff. + { "openAsset", "(Ljava/lang/String;I)J", + (void*) android_content_AssetManager_openAsset }, + { "openAssetFd", "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*) android_content_AssetManager_openAssetFd }, + { "openNonAssetNative", "(ILjava/lang/String;I)J", + (void*) android_content_AssetManager_openNonAssetNative }, + { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*) android_content_AssetManager_openNonAssetFdNative }, + { "list", "(Ljava/lang/String;)[Ljava/lang/String;", + (void*) android_content_AssetManager_list }, + { "destroyAsset", "(J)V", + (void*) android_content_AssetManager_destroyAsset }, + { "readAssetChar", "(J)I", + (void*) android_content_AssetManager_readAssetChar }, + { "readAsset", "(J[BII)I", + (void*) android_content_AssetManager_readAsset }, + { "seekAsset", "(JJI)J", + (void*) android_content_AssetManager_seekAsset }, + { "getAssetLength", "(J)J", + (void*) android_content_AssetManager_getAssetLength }, + { "getAssetRemainingLength", "(J)J", + (void*) android_content_AssetManager_getAssetRemainingLength }, + { "addAssetPathNative", "(Ljava/lang/String;Z)I", + (void*) android_content_AssetManager_addAssetPath }, + { "addAssetFdNative", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)I", + (void*) android_content_AssetManager_addAssetFd }, + { "addOverlayPathNative", "(Ljava/lang/String;)I", + (void*) android_content_AssetManager_addOverlayPath }, + { "isUpToDate", "()Z", + (void*) android_content_AssetManager_isUpToDate }, + + // Resources. + { "getLocales", "()[Ljava/lang/String;", + (void*) android_content_AssetManager_getLocales }, + { "getNonSystemLocales", "()[Ljava/lang/String;", + (void*) android_content_AssetManager_getNonSystemLocales }, + { "getSizeConfigurations", "()[Landroid/content/res/Configuration;", + (void*) android_content_AssetManager_getSizeConfigurations }, + { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIIII)V", + (void*) android_content_AssetManager_setConfiguration }, + { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + (void*) android_content_AssetManager_getResourceIdentifier }, + { "getResourceName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourceName }, + { "getResourcePackageName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourcePackageName }, + { "getResourceTypeName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourceTypeName }, + { "getResourceEntryName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourceEntryName }, + { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I", + (void*) android_content_AssetManager_loadResourceValue }, + { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I", + (void*) android_content_AssetManager_loadResourceBagValue }, + { "getStringBlockCount","()I", + (void*) android_content_AssetManager_getStringBlockCount }, + { "getNativeStringBlock","(I)J", + (void*) android_content_AssetManager_getNativeStringBlock }, + { "getCookieName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getCookieName }, + { "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;", + (void*) android_content_AssetManager_getAssignedPackageIdentifiers }, + + // Themes. + { "newTheme", "()J", + (void*) android_content_AssetManager_newTheme }, + { "deleteTheme", "(J)V", + (void*) android_content_AssetManager_deleteTheme }, + { "applyThemeStyle", "(JIZ)V", + (void*) android_content_AssetManager_applyThemeStyle }, + { "copyTheme", "(JJ)V", + (void*) android_content_AssetManager_copyTheme }, + { "clearTheme", "(J)V", + (void*) android_content_AssetManager_clearTheme }, + { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I", + (void*) android_content_AssetManager_loadThemeAttributeValue }, + { "getThemeChangingConfigurations", "(J)I", + (void*) android_content_AssetManager_getThemeChangingConfigurations }, + { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V", + (void*) android_content_AssetManager_dumpTheme }, + { "applyStyle","(JIIJ[IIJJ)V", + (void*) android_content_AssetManager_applyStyle }, + { "resolveAttrs","(JII[I[I[I[I)Z", + (void*) android_content_AssetManager_resolveAttrs }, + { "retrieveAttributes","(J[I[I[I)Z", + (void*) android_content_AssetManager_retrieveAttributes }, + { "getArraySize","(I)I", + (void*) android_content_AssetManager_getArraySize }, + { "retrieveArray","(I[I)I", + (void*) android_content_AssetManager_retrieveArray }, + + // XML files. + { "openXmlAssetNative", "(ILjava/lang/String;)J", + (void*) android_content_AssetManager_openXmlAssetNative }, + + // Arrays. + { "getArrayStringResource","(I)[Ljava/lang/String;", + (void*) android_content_AssetManager_getArrayStringResource }, + { "getArrayStringInfo","(I)[I", + (void*) android_content_AssetManager_getArrayStringInfo }, + { "getArrayIntResource","(I)[I", + (void*) android_content_AssetManager_getArrayIntResource }, + { "getStyleAttributes","(I)[I", + (void*) android_content_AssetManager_getStyleAttributes }, + + // Bookkeeping. + { "init", "(Z)V", + (void*) android_content_AssetManager_init }, + { "destroy", "()V", + (void*) android_content_AssetManager_destroy }, + { "getGlobalAssetCount", "()I", + (void*) android_content_AssetManager_getGlobalAssetCount }, + { "getAssetAllocations", "()Ljava/lang/String;", + (void*) android_content_AssetManager_getAssetAllocations }, + { "getGlobalAssetManagerCount", "()I", + (void*) android_content_AssetManager_getGlobalAssetManagerCount }, }; -int register_android_content_AssetManager(JNIEnv* env) { - jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets"); - gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J"); - - jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); - gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); - gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); - gTypedValueOffsets.mString = - GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;"); - gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); - gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); - gTypedValueOffsets.mChangingConfigurations = - GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I"); - gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); - - jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); - gAssetFileDescriptorOffsets.mFd = - GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;"); - gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); - gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); - - jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); - gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); - - jclass stringClass = FindClassOrDie(env, "java/lang/String"); - g_stringClass = MakeGlobalRefOrDie(env, stringClass); - - jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); - gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); - gSparseArrayOffsets.constructor = - GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "", "()V"); - gSparseArrayOffsets.put = - GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V"); - - jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); - gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); - gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "", "()V"); - gConfigurationOffsets.mSmallestScreenWidthDpOffset = - GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I"); - gConfigurationOffsets.mScreenWidthDpOffset = - GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I"); - gConfigurationOffsets.mScreenHeightDpOffset = - GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I"); - - return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, - NELEM(gAssetManagerMethods)); +int register_android_content_AssetManager(JNIEnv* env) +{ + jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); + gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); + gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); + gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string", + "Ljava/lang/CharSequence;"); + gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); + gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); + gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue, + "changingConfigurations", "I"); + gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); + + jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); + gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd", + "Landroid/os/ParcelFileDescriptor;"); + gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); + gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); + + jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); + gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); + + jclass stringClass = FindClassOrDie(env, "java/lang/String"); + g_stringClass = MakeGlobalRefOrDie(env, stringClass); + + jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); + gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); + gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, + "", "()V"); + gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", + "(ILjava/lang/Object;)V"); + + jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); + gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); + gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, + "", "()V"); + gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, + "smallestScreenWidthDp", "I"); + gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, + "screenWidthDp", "I"); + gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass, + "screenHeightDp", "I"); + + return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, + NELEM(gAssetManagerMethods)); } }; // namespace android diff --git a/core/jni/include/android_runtime/android_util_AssetManager.h b/core/jni/include/android_runtime/android_util_AssetManager.h index 2c1e3579eb92..8dd933707a6a 100644 --- a/core/jni/include/android_runtime/android_util_AssetManager.h +++ b/core/jni/include/android_runtime/android_util_AssetManager.h @@ -14,20 +14,17 @@ * limitations under the License. */ -#ifndef ANDROID_RUNTIME_ASSETMANAGER_H -#define ANDROID_RUNTIME_ASSETMANAGER_H +#ifndef android_util_AssetManager_H +#define android_util_AssetManager_H -#include "androidfw/AssetManager2.h" -#include "androidfw/MutexGuard.h" +#include #include "jni.h" namespace android { -extern AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); -extern Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); -extern Guarded* AssetManagerForNdkAssetManager(AAssetManager* assetmanager); +extern AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject assetMgr); -} // namespace android +} -#endif // ANDROID_RUNTIME_ASSETMANAGER_H +#endif diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 2fc8e952707b..415d3e36adf9 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -265,6 +265,8 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, FindEntryResult* out_entry) { + ATRACE_CALL(); + // Might use this if density_override != 0. ResTable_config density_override_config; @@ -427,7 +429,9 @@ ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_valu for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && in_out_value->data != 0u && iteration < kMaxIterations; iteration++) { - *out_last_reference = in_out_value->data; + if (out_last_reference != nullptr) { + *out_last_reference = in_out_value->data; + } uint32_t new_flags = 0u; cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/, in_out_value, in_out_selected_config, &new_flags); diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index f912af4f7190..60e3845d98a9 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -20,18 +20,13 @@ #include -#include "androidfw/AssetManager2.h" #include "androidfw/AttributeFinder.h" +#include "androidfw/ResourceTypes.h" constexpr bool kDebugStyles = false; namespace android { -// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. -static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { - return cookie != kInvalidCookie ? static_cast(cookie + 1) : static_cast(-1); -} - class XmlAttributeFinder : public BackTrackingAttributeFinder { public: @@ -49,53 +44,58 @@ class XmlAttributeFinder }; class BagAttributeFinder - : public BackTrackingAttributeFinder { + : public BackTrackingAttributeFinder { public: - BagAttributeFinder(const ResolvedBag* bag) - : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr, - bag != nullptr ? bag->entries + bag->entry_count : nullptr) { - } + BagAttributeFinder(const ResTable::bag_entry* start, + const ResTable::bag_entry* end) + : BackTrackingAttributeFinder(start, end) {} - inline uint32_t GetAttribute(const ResolvedBag::Entry* entry) const { - return entry->key; + inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const { + return entry->map.name.ident; } }; -bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, - uint32_t* src_values, size_t src_values_length, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { +bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, + uint32_t def_style_res, uint32_t* src_values, + size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, + uint32_t* out_indices) { if (kDebugStyles) { ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr, def_style_res); } - AssetManager2* assetmanager = theme->GetAssetManager(); + const ResTable& res = theme->getResTable(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_flags = 0u; + uint32_t def_style_bag_type_set_flags = 0; if (def_style_attr != 0) { Res_value value; - if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { + if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) { if (value.dataType == Res_value::TYPE_REFERENCE) { def_style_res = value.data; } } } - // Retrieve the default style bag, if requested. - const ResolvedBag* default_style_bag = nullptr; - if (def_style_res != 0) { - default_style_bag = assetmanager->GetBag(def_style_res); - if (default_style_bag != nullptr) { - def_style_flags |= default_style_bag->type_spec_flags; - } - } + // Now lock down the resource object and start pulling stuff from it. + res.lock(); - BagAttributeFinder def_style_attr_finder(default_style_bag); + // Retrieve the default style bag, if requested. + const ResTable::bag_entry* def_style_start = nullptr; + uint32_t def_style_type_set_flags = 0; + ssize_t bag_off = def_style_res != 0 + ? res.getBagLocked(def_style_res, &def_style_start, + &def_style_type_set_flags) + : -1; + def_style_type_set_flags |= def_style_bag_type_set_flags; + const ResTable::bag_entry* const def_style_end = + def_style_start + (bag_off >= 0 ? bag_off : 0); + BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -106,7 +106,7 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ApkAssetsCookie cookie = kInvalidCookie; + ssize_t block = -1; uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; @@ -122,14 +122,15 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, value.dataType = Res_value::TYPE_ATTRIBUTE; value.data = src_values[ii]; if (kDebugStyles) { - ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data); + ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, + value.data); } } else { - const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident); - if (entry != def_style_attr_finder.end()) { - cookie = entry->cookie; - type_set_flags = def_style_flags; - value = entry->value; + const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident); + if (def_style_entry != def_style_end) { + block = def_style_entry->stringBlock; + type_set_flags = def_style_type_set_flags; + value = def_style_entry->map.value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -139,26 +140,22 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } + ssize_t new_block = + theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); + if (new_block >= 0) block = new_block; if (kDebugStyles) { ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data); } } else if (value.data != Res_value::DATA_NULL_EMPTY) { - // If we still don't have a value for this attribute, try to find it in the theme! - ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); - if (new_cookie != kInvalidCookie) { + // If we still don't have a value for this attribute, try to find + // it in the theme! + ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); + if (new_block >= 0) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_cookie = - assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } + new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); + if (new_block >= 0) block = new_block; if (kDebugStyles) { ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -172,7 +169,7 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + block = -1; } if (kDebugStyles) { @@ -182,7 +179,9 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + out_values[STYLE_ASSET_COOKIE] = + block != -1 ? static_cast(res.getTableCookie(block)) + : static_cast(-1); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -196,80 +195,90 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, out_values += STYLE_NUM_ENTRIES; } + res.unlock(); + if (out_indices != nullptr) { out_indices[0] = indices_idx; } return true; } -void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { - ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, - def_style_attr, def_style_resid, xml_parser); + ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", + theme, def_style_attr, def_style_res, xml_parser); } - AssetManager2* assetmanager = theme->GetAssetManager(); + const ResTable& res = theme->getResTable(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_flags = 0u; + uint32_t def_style_bag_type_set_flags = 0; if (def_style_attr != 0) { Res_value value; - if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { + if (theme->getAttribute(def_style_attr, &value, + &def_style_bag_type_set_flags) >= 0) { if (value.dataType == Res_value::TYPE_REFERENCE) { - def_style_resid = value.data; + def_style_res = value.data; } } } - // Retrieve the style resource ID associated with the current XML tag's style attribute. - uint32_t style_resid = 0u; - uint32_t style_flags = 0u; + // Retrieve the style class associated with the current XML tag. + int style = 0; + uint32_t style_bag_type_set_flags = 0; if (xml_parser != nullptr) { ssize_t idx = xml_parser->indexOfStyle(); if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) { if (value.dataType == value.TYPE_ATTRIBUTE) { - // Resolve the attribute with out theme. - if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) { + if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) { value.dataType = Res_value::TYPE_NULL; } } - if (value.dataType == value.TYPE_REFERENCE) { - style_resid = value.data; + style = value.data; } } } - // Retrieve the default style bag, if requested. - const ResolvedBag* default_style_bag = nullptr; - if (def_style_resid != 0) { - default_style_bag = assetmanager->GetBag(def_style_resid); - if (default_style_bag != nullptr) { - def_style_flags |= default_style_bag->type_spec_flags; - } - } + // Now lock down the resource object and start pulling stuff from it. + res.lock(); - BagAttributeFinder def_style_attr_finder(default_style_bag); + // Retrieve the default style bag, if requested. + const ResTable::bag_entry* def_style_attr_start = nullptr; + uint32_t def_style_type_set_flags = 0; + ssize_t bag_off = def_style_res != 0 + ? res.getBagLocked(def_style_res, &def_style_attr_start, + &def_style_type_set_flags) + : -1; + def_style_type_set_flags |= def_style_bag_type_set_flags; + const ResTable::bag_entry* const def_style_attr_end = + def_style_attr_start + (bag_off >= 0 ? bag_off : 0); + BagAttributeFinder def_style_attr_finder(def_style_attr_start, + def_style_attr_end); // Retrieve the style class bag, if requested. - const ResolvedBag* xml_style_bag = nullptr; - if (style_resid != 0) { - xml_style_bag = assetmanager->GetBag(style_resid); - if (xml_style_bag != nullptr) { - style_flags |= xml_style_bag->type_spec_flags; - } - } - - BagAttributeFinder xml_style_attr_finder(xml_style_bag); + const ResTable::bag_entry* style_attr_start = nullptr; + uint32_t style_type_set_flags = 0; + bag_off = + style != 0 + ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags) + : -1; + style_type_set_flags |= style_bag_type_set_flags; + const ResTable::bag_entry* const style_attr_end = + style_attr_start + (bag_off >= 0 ? bag_off : 0); + BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end); // Retrieve the XML attributes, if requested. + static const ssize_t kXmlBlock = 0x10000000; XmlAttributeFinder xml_attr_finder(xml_parser); + const size_t xml_attr_end = + xml_parser != nullptr ? xml_parser->getAttributeCount() : 0; // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -280,8 +289,8 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ApkAssetsCookie cookie = kInvalidCookie; - uint32_t type_set_flags = 0u; + ssize_t block = kXmlBlock; + uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -293,7 +302,7 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, // Walk through the xml attributes looking for the requested attribute. const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); - if (xml_attr_idx != xml_attr_finder.end()) { + if (xml_attr_idx != xml_attr_end) { // We found the attribute we were looking for. xml_parser->getAttributeValue(xml_attr_idx, &value); if (kDebugStyles) { @@ -303,12 +312,12 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the style class values looking for the requested attribute. - const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident); - if (entry != xml_style_attr_finder.end()) { + const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident); + if (style_attr_entry != style_attr_end) { // We found the attribute we were looking for. - cookie = entry->cookie; - type_set_flags = style_flags; - value = entry->value; + block = style_attr_entry->stringBlock; + type_set_flags = style_type_set_flags; + value = style_attr_entry->map.value; if (kDebugStyles) { ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -317,25 +326,25 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the default style values looking for the requested attribute. - const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident); - if (entry != def_style_attr_finder.end()) { + const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident); + if (def_style_attr_entry != def_style_attr_end) { // We found the attribute we were looking for. - cookie = entry->cookie; - type_set_flags = def_style_flags; - value = entry->value; + block = def_style_attr_entry->stringBlock; + type_set_flags = style_type_set_flags; + value = def_style_attr_entry->map.value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } } } - uint32_t resid = 0u; + uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; + ssize_t new_block = + theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); + if (new_block >= 0) { + block = new_block; } if (kDebugStyles) { @@ -343,15 +352,14 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, } } else if (value.data != Res_value::DATA_NULL_EMPTY) { // If we still don't have a value for this attribute, try to find it in the theme! - ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); - if (new_cookie != kInvalidCookie) { + ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); + if (new_block >= 0) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_cookie = - assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; + new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); + if (new_block >= 0) { + block = new_block; } if (kDebugStyles) { @@ -367,7 +375,7 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + block = kXmlBlock; } if (kDebugStyles) { @@ -377,7 +385,9 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + out_values[STYLE_ASSET_COOKIE] = + block != kXmlBlock ? static_cast(res.getTableCookie(block)) + : static_cast(-1); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -392,28 +402,36 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, out_values += STYLE_NUM_ENTRIES; } + res.unlock(); + // out_indices must NOT be nullptr. out_indices[0] = indices_idx; } -bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { +bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, + uint32_t* attrs, size_t attrs_length, + uint32_t* out_values, uint32_t* out_indices) { ResTable_config config; Res_value value; int indices_idx = 0; + // Now lock down the resource object and start pulling stuff from it. + res->lock(); + // Retrieve the XML attributes, if requested. const size_t xml_attr_count = xml_parser->getAttributeCount(); size_t ix = 0; uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix); + static const ssize_t kXmlBlock = 0x10000000; + // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - ApkAssetsCookie cookie = kInvalidCookie; - uint32_t type_set_flags = 0u; + ssize_t block = kXmlBlock; + uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -432,27 +450,28 @@ bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, u cur_xml_attr = xml_parser->getAttributeNameResID(ix); } - uint32_t resid = 0u; + uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } + // printf("Resolving attribute reference\n"); + ssize_t new_block = res->resolveReference(&value, block, &resid, + &type_set_flags, &config); + if (new_block >= 0) block = new_block; } // Deal with the special @null value -- it turns back to TYPE_NULL. if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + block = kXmlBlock; } // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + out_values[STYLE_ASSET_COOKIE] = + block != kXmlBlock ? static_cast(res->getTableCookie(block)) + : static_cast(-1); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -466,6 +485,8 @@ bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, u out_values += STYLE_NUM_ENTRIES; } + res->unlock(); + if (out_indices != nullptr) { out_indices[0] = indices_idx; } diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index e08848f891f6..28548e27baf0 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -324,6 +324,8 @@ bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_i bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, FindEntryResult* out_entry) const { + ATRACE_CALL(); + // If the type IDs are offset in this package, we need to take that into account when searching // for a type. const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h index 03fad4947dfe..f281921824e7 100644 --- a/libs/androidfw/include/androidfw/AttributeFinder.h +++ b/libs/androidfw/include/androidfw/AttributeFinder.h @@ -58,7 +58,6 @@ class BackTrackingAttributeFinder { BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end); Iterator Find(uint32_t attr); - inline Iterator end(); private: void JumpToClosestAttribute(uint32_t package_id); @@ -202,11 +201,6 @@ Iterator BackTrackingAttributeFinder::Find(uint32_t attr) { return end_; } -template -Iterator BackTrackingAttributeFinder::end() { - return end_; -} - } // namespace android #endif // ANDROIDFW_ATTRIBUTE_FINDER_H diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h index 35ef98d8c704..69b760414846 100644 --- a/libs/androidfw/include/androidfw/AttributeResolution.h +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -17,8 +17,7 @@ #ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H #define ANDROIDFW_ATTRIBUTERESOLUTION_H -#include "androidfw/AssetManager2.h" -#include "androidfw/ResourceTypes.h" +#include namespace android { @@ -43,19 +42,19 @@ enum { // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid, +bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, uint32_t* src_values, size_t src_values_length, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` is NOT optional and must NOT be nullptr. -void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, +bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); } // namespace android diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 1775f5070f4e..965e2dbd2fb2 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -45,17 +45,16 @@ struct FindEntryResult { // A pointer to the resource table entry for this resource. // If the size of the entry is > sizeof(ResTable_entry), it can be cast to // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry; + const ResTable_entry* entry = nullptr; - // The configuration for which the resulting entry was defined. This points to a structure that - // is already swapped to host endianness. - const ResTable_config* config; + // The configuration for which the resulting entry was defined. + const ResTable_config* config = nullptr; - // The bitmask of configuration axis with which the resource value varies. - uint32_t type_flags; + // Stores the resulting bitmask of configuration axis with which the resource value varies. + uint32_t type_flags = 0u; // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table; + const DynamicRefTable* dynamic_ref_table = nullptr; // The string pool reference to the type's name. This uses a different string pool than // the global string pool, but this is hidden from the caller. diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h deleted file mode 100644 index 64924f433245..000000000000 --- a/libs/androidfw/include/androidfw/MutexGuard.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROIDFW_MUTEXGUARD_H -#define ANDROIDFW_MUTEXGUARD_H - -#include -#include - -#include "android-base/macros.h" - -namespace android { - -template -class ScopedLock; - -// Owns the guarded object and protects access to it via a mutex. -// The guarded object is inaccessible via this class. -// The mutex is locked and the object accessed via the ScopedLock class. -// -// NOTE: The template parameter T should not be a raw pointer, since ownership -// is ambiguous and error-prone. Instead use an std::unique_ptr<>. -// -// Example use: -// -// Guarded shared_string("hello"); -// { -// ScopedLock locked_string(shared_string); -// *locked_string += " world"; -// } -// -template -class Guarded { - static_assert(!std::is_pointer::value, "T must not be a raw pointer"); - - public: - explicit Guarded() : guarded_() { - } - - template - explicit Guarded(const T& guarded, - typename std::enable_if::value>::type = void()) - : guarded_(guarded) { - } - - template - explicit Guarded(T&& guarded, - typename std::enable_if::value>::type = void()) - : guarded_(std::move(guarded)) { - } - - private: - friend class ScopedLock; - - DISALLOW_COPY_AND_ASSIGN(Guarded); - - std::mutex lock_; - T guarded_; -}; - -template -class ScopedLock { - public: - explicit ScopedLock(Guarded& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) { - } - - T& operator*() { - return guarded_; - } - - T* operator->() { - return &guarded_; - } - - T* get() { - return &guarded_; - } - - private: - DISALLOW_COPY_AND_ASSIGN(ScopedLock); - - std::lock_guard lock_; - T& guarded_; -}; - -} // namespace android - -#endif // ANDROIDFW_MUTEXGUARD_H diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index cc3053798e7b..2d73ce8f8ee3 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -21,7 +21,6 @@ #include "android-base/file.h" #include "android-base/logging.h" #include "android-base/macros.h" -#include "androidfw/AssetManager2.h" #include "TestHelpers.h" #include "data/styles/R.h" @@ -33,14 +32,15 @@ namespace android { class AttributeResolutionTest : public ::testing::Test { public: virtual void SetUp() override { - styles_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); - ASSERT_NE(nullptr, styles_assets_); - assetmanager_.SetApkAssets({styles_assets_.get()}); + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString( + GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); + ASSERT_EQ(NO_ERROR, table_.add(contents.data(), contents.size(), + 1 /*cookie*/, true /*copyData*/)); } protected: - std::unique_ptr styles_assets_; - AssetManager2 assetmanager_; + ResTable table_; }; class AttributeResolutionXmlTest : public AttributeResolutionTest { @@ -48,12 +48,13 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { virtual void SetUp() override { AttributeResolutionTest::SetUp(); - std::unique_ptr asset = - assetmanager_.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, asset); + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", + "res/layout/layout.xml", &contents)); - ASSERT_EQ(NO_ERROR, - xml_parser_.setTo(asset->getBuffer(true), asset->getLength(), true /*copyData*/)); + ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(), + true /*copyData*/)); // Skip to the first tag. while (xml_parser_.next() != ResXMLParser::START_TAG) { @@ -65,14 +66,14 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { }; TEST_F(AttributeResolutionTest, Theme) { - std::unique_ptr theme = assetmanager_.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); + ResTable::Theme theme(table_); + ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_empty}}; std::array values; - ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/, + ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/, nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(), attrs.size(), values.data(), nullptr /*out_indices*/)); @@ -125,8 +126,8 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { R::attr::attr_four, R::attr::attr_empty}}; std::array values; - ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(), - values.data(), nullptr /*out_indices*/)); + ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(), + nullptr /*out_indices*/)); uint32_t* values_cursor = values.data(); EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]); @@ -170,15 +171,15 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { } TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) { - std::unique_ptr theme = assetmanager_.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); + ResTable::Theme theme(table_); + ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}}; std::array values; std::array indices; - ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), + ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(), attrs.size(), values.data(), indices.data()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index a8abcb5df86c..7149beef797f 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -33,12 +33,12 @@ void GetResourceBenchmarkOld(const std::vector& paths, const ResTab } } - // Make sure to force creation of the ResTable first, or else the configuration doesn't get set. - const ResTable& table = assetmanager.getResources(true); if (config != nullptr) { assetmanager.setConfiguration(*config); } + const ResTable& table = assetmanager.getResources(true); + Res_value value; ResTable_config selected_config; uint32_t flags; diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp index e70d5ea0d566..98e9a42d944d 100644 --- a/native/android/asset_manager.cpp +++ b/native/android/asset_manager.cpp @@ -18,11 +18,9 @@ #include #include -#include #include #include #include -#include #include #include "jni.h" @@ -37,20 +35,21 @@ using namespace android; // ----- struct AAssetDir { - std::unique_ptr mAssetDir; + AssetDir* mAssetDir; size_t mCurFileIndex; String8 mCachedFileName; - explicit AAssetDir(std::unique_ptr dir) : - mAssetDir(std::move(dir)), mCurFileIndex(0) { } + explicit AAssetDir(AssetDir* dir) : mAssetDir(dir), mCurFileIndex(0) { } + ~AAssetDir() { delete mAssetDir; } }; // ----- struct AAsset { - std::unique_ptr mAsset; + Asset* mAsset; - explicit AAsset(std::unique_ptr asset) : mAsset(std::move(asset)) { } + explicit AAsset(Asset* asset) : mAsset(asset) { } + ~AAsset() { delete mAsset; } }; // -------------------- Public native C API -------------------- @@ -105,18 +104,19 @@ AAsset* AAssetManager_open(AAssetManager* amgr, const char* filename, int mode) return NULL; } - ScopedLock locked_mgr(*AssetManagerForNdkAssetManager(amgr)); - std::unique_ptr asset = locked_mgr->Open(filename, amMode); - if (asset == nullptr) { - return nullptr; + AssetManager* mgr = static_cast(amgr); + Asset* asset = mgr->open(filename, amMode); + if (asset == NULL) { + return NULL; } - return new AAsset(std::move(asset)); + + return new AAsset(asset); } AAssetDir* AAssetManager_openDir(AAssetManager* amgr, const char* dirName) { - ScopedLock locked_mgr(*AssetManagerForNdkAssetManager(amgr)); - return new AAssetDir(locked_mgr->OpenDir(dirName)); + AssetManager* mgr = static_cast(amgr); + return new AAssetDir(mgr->openDir(dirName)); } /** diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index 52d0e08e4e7f..b32be736533b 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -24,9 +24,8 @@ #include #include -#include #include -#include +#include #include #include @@ -1665,22 +1664,18 @@ nFileA3DCreateFromAssetStream(JNIEnv *_env, jobject _this, jlong con, jlong nati static jlong nFileA3DCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path) { - Guarded* mgr = AssetManagerForJavaObject(_env, _assetMgr); + AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - std::unique_ptr asset; - { - ScopedLock locked_mgr(*mgr); - asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); - if (asset == nullptr) { - return 0; - } + Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; } - jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset.release()); + jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset); return id; } @@ -1757,25 +1752,22 @@ static jlong nFontCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path, jfloat fontSize, jint dpi) { - Guarded* mgr = AssetManagerForJavaObject(_env, _assetMgr); + AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - std::unique_ptr asset; - { - ScopedLock locked_mgr(*mgr); - asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); - if (asset == nullptr) { - return 0; - } + Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; } jlong id = (jlong)(uintptr_t)rsFontCreateFromMemory((RsContext)con, str.c_str(), str.length(), fontSize, dpi, asset->getBuffer(false), asset->getLength()); + delete asset; return id; } -- cgit v1.2.3-59-g8ed1b From dcb3c6559b09ec89771858ec27a787027da9af50 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 23 Jan 2017 12:58:11 -0800 Subject: Replace AssetManager with AssetManager2 implementation Test: atest CtsContentTestCases:android.content.res.cts Test: make libandroidfw_tests Change-Id: I572eb13c6a4372c7f656f5912821cececd5bf3d4 --- core/java/android/content/pm/PackageParser.java | 19 +- core/java/android/content/res/ApkAssets.java | 221 ++ core/java/android/content/res/AssetManager.java | 1321 +++++---- core/java/android/content/res/Resources.java | 9 +- core/java/android/content/res/ResourcesImpl.java | 22 +- core/java/android/content/res/TypedArray.java | 185 +- core/java/android/content/res/XmlBlock.java | 8 +- core/jni/Android.bp | 3 +- core/jni/AndroidRuntime.cpp | 2 + core/jni/android/graphics/FontFamily.cpp | 29 +- core/jni/android_app_NativeActivity.cpp | 2 +- core/jni/android_content_res_ApkAssets.cpp | 150 + core/jni/android_util_AssetManager.cpp | 2883 +++++++++----------- .../android_runtime/android_util_AssetManager.h | 15 +- libs/androidfw/AssetManager2.cpp | 6 +- libs/androidfw/AttributeResolution.cpp | 263 +- libs/androidfw/LoadedArsc.cpp | 2 - libs/androidfw/include/androidfw/AttributeFinder.h | 6 + .../include/androidfw/AttributeResolution.h | 11 +- libs/androidfw/include/androidfw/LoadedArsc.h | 13 +- libs/androidfw/include/androidfw/MutexGuard.h | 101 + libs/androidfw/tests/AttributeResolution_test.cpp | 39 +- libs/androidfw/tests/BenchmarkHelpers.cpp | 4 +- native/android/asset_manager.cpp | 28 +- rs/jni/android_renderscript_RenderScript.cpp | 30 +- 25 files changed, 2876 insertions(+), 2496 deletions(-) create mode 100644 core/java/android/content/res/ApkAssets.java create mode 100644 core/jni/android_content_res_ApkAssets.cpp create mode 100644 libs/androidfw/include/androidfw/MutexGuard.h (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 4efd08134065..6805bb8d0eae 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -54,6 +54,7 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper; import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.SplitAssetDependencyLoader; import android.content.pm.split.SplitAssetLoader; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -1575,21 +1576,19 @@ public class PackageParser { int flags) throws PackageParserException { final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); - AssetManager assets = null; + ApkAssets apkAssets = null; XmlResourceParser parser = null; try { - assets = newConfiguredAssetManager(); - int cookie = fd != null - ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath); - if (cookie == 0) { + try { + apkAssets = fd != null + ? ApkAssets.loadFromFd(fd, debugPathName, false, false) + : ApkAssets.loadFromPath(apkPath); + } catch (IOException e) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse " + apkPath); } - final DisplayMetrics metrics = new DisplayMetrics(); - metrics.setToDefaults(); - - parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); + parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME); final SigningDetails signingDetails; if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) { @@ -1615,7 +1614,7 @@ public class PackageParser { "Failed to parse " + apkPath, e); } finally { IoUtils.closeQuietly(parser); - IoUtils.closeQuietly(assets); + IoUtils.closeQuietly(apkAssets); } } diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java new file mode 100644 index 000000000000..b087c48d8d4c --- /dev/null +++ b/core/java/android/content/res/ApkAssets.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.content.res; + +import android.annotation.NonNull; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; + +import java.io.FileDescriptor; +import java.io.IOException; + +/** + * The loaded, immutable, in-memory representation of an APK. + * + * The main implementation is native C++ and there is very little API surface exposed here. The APK + * is mainly accessed via {@link AssetManager}. + * + * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers, + * making the creation of AssetManagers very cheap. + * @hide + */ +public final class ApkAssets implements AutoCloseable { + @GuardedBy("this") private long mNativePtr; + @GuardedBy("this") private StringBlock mStringBlock; + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException { + return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @param system When true, the APK is loaded as a system APK (framework). + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system) + throws IOException { + return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @param system When true, the APK is loaded as a system APK (framework). + * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are + * loaded as a shared library. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system, + boolean forceSharedLibrary) throws IOException { + return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications. + * + * Performs a dup of the underlying fd, so you must take care of still closing + * the FileDescriptor yourself (and can do that whenever you want). + * + * @param fd The FileDescriptor of an open, readable APK. + * @param friendlyName The friendly name used to identify this ApkAssets when logging. + * @param system When true, the APK is loaded as a system APK (framework). + * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are + * loaded as a shared library. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd, + @NonNull String friendlyName, boolean system, boolean forceSharedLibrary) + throws IOException { + return new ApkAssets(fd, friendlyName, system, forceSharedLibrary); + } + + /** + * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path + * is encoded within the IDMAP. + * + * @param idmapPath Path to the IDMAP of an overlay APK. + * @param system When true, the APK is loaded as a system APK (framework). + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system) + throws IOException { + return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/); + } + + private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) + throws IOException { + Preconditions.checkNotNull(path, "path"); + mNativePtr = nativeLoad(path, system, forceSharedLib, overlay); + mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); + } + + private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system, + boolean forceSharedLib) throws IOException { + Preconditions.checkNotNull(fd, "fd"); + Preconditions.checkNotNull(friendlyName, "friendlyName"); + mNativePtr = nativeLoadFromFd(fd, friendlyName, system, forceSharedLib); + mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); + } + + @NonNull String getAssetPath() { + synchronized (this) { + ensureValidLocked(); + return nativeGetAssetPath(mNativePtr); + } + } + + CharSequence getStringFromPool(int idx) { + synchronized (this) { + ensureValidLocked(); + return mStringBlock.get(idx); + } + } + + /** + * Retrieve a parser for a compiled XML file. This is associated with a single APK and + * NOT a full AssetManager. This means that shared-library references will not be + * dynamically assigned runtime package IDs. + * + * @param fileName The path to the file within the APK. + * @return An XmlResourceParser. + * @throws IOException if the file was not found or an error occurred retrieving it. + */ + public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException { + Preconditions.checkNotNull(fileName, "fileName"); + synchronized (this) { + ensureValidLocked(); + long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName); + try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) { + XmlResourceParser parser = block.newParser(); + // If nativeOpenXml doesn't throw, it will always return a valid native pointer, + // which makes newParser always return non-null. But let's be paranoid. + if (parser == null) { + throw new AssertionError("block.newParser() returned a null parser"); + } + return parser; + } + } + } + + /** + * Returns false if the underlying APK was changed since this ApkAssets was loaded. + */ + public boolean isUpToDate() { + synchronized (this) { + ensureValidLocked(); + return nativeIsUpToDate(mNativePtr); + } + } + + /** + * Closes the ApkAssets and destroys the underlying native implementation. Further use of the + * ApkAssets object will cause exceptions to be thrown. + * + * Calling close on an already closed ApkAssets does nothing. + */ + @Override + public void close() { + synchronized (this) { + if (mNativePtr == 0) { + return; + } + + mStringBlock = null; + nativeDestroy(mNativePtr); + mNativePtr = 0; + } + } + + @Override + protected void finalize() throws Throwable { + if (mNativePtr != 0) { + nativeDestroy(mNativePtr); + } + } + + private void ensureValidLocked() { + if (mNativePtr == 0) { + throw new RuntimeException("ApkAssets is closed"); + } + } + + private static native long nativeLoad( + @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) + throws IOException; + private static native long nativeLoadFromFd(@NonNull FileDescriptor fd, + @NonNull String friendlyName, boolean system, boolean forceSharedLib) + throws IOException; + private static native void nativeDestroy(long ptr); + private static native @NonNull String nativeGetAssetPath(long ptr); + private static native long nativeGetStringBlock(long ptr); + private static native boolean nativeIsUpToDate(long ptr); + private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException; +} diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 78665609bdd4..78370f4d56a8 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -18,9 +18,11 @@ package android.content.res; import android.annotation.AnyRes; import android.annotation.ArrayRes; +import android.annotation.AttrRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; +import android.annotation.StyleRes; import android.content.pm.ActivityInfo; import android.content.res.Configuration.NativeConfig; import android.os.ParcelFileDescriptor; @@ -28,10 +30,18 @@ import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; -import java.io.FileDescriptor; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; + +import java.io.BufferedReader; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.channels.FileLock; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; /** @@ -42,7 +52,17 @@ import java.util.HashMap; * bytes. */ public final class AssetManager implements AutoCloseable { - /* modes used when opening an asset */ + private static final String TAG = "AssetManager"; + private static final boolean DEBUG_REFS = false; + + private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk"; + + private static final Object sSync = new Object(); + + // Not private for LayoutLib's BridgeAssetManager. + @GuardedBy("sSync") static AssetManager sSystem = null; + + @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0]; /** * Mode for {@link #open(String, int)}: no specific information about how @@ -65,87 +85,279 @@ public final class AssetManager implements AutoCloseable { */ public static final int ACCESS_BUFFER = 3; - private static final String TAG = "AssetManager"; - private static final boolean localLOGV = false || false; - - private static final boolean DEBUG_REFS = false; - - private static final Object sSync = new Object(); - /*package*/ static AssetManager sSystem = null; + @GuardedBy("this") private final TypedValue mValue = new TypedValue(); + @GuardedBy("this") private final long[] mOffsets = new long[2]; - private final TypedValue mValue = new TypedValue(); - private final long[] mOffsets = new long[2]; - - // For communication with native code. - private long mObject; + // Pointer to native implementation, stuffed inside a long. + @GuardedBy("this") private long mObject; + + // The loaded asset paths. + @GuardedBy("this") private ApkAssets[] mApkAssets; + + // Debug/reference counting implementation. + @GuardedBy("this") private boolean mOpen = true; + @GuardedBy("this") private int mNumRefs = 1; + @GuardedBy("this") private HashMap mRefStacks; - private StringBlock mStringBlocks[] = null; - - private int mNumRefs = 1; - private boolean mOpen = true; - private HashMap mRefStacks; - /** * Create a new AssetManager containing only the basic system assets. * Applications will not generally use this method, instead retrieving the * appropriate asset manager with {@link Resources#getAssets}. Not for * use by applications. - * {@hide} + * @hide */ public AssetManager() { - synchronized (this) { - if (DEBUG_REFS) { - mNumRefs = 0; - incRefsLocked(this.hashCode()); - } - init(false); - if (localLOGV) Log.v(TAG, "New asset manager: " + this); - ensureSystemAssets(); + final ApkAssets[] assets; + synchronized (sSync) { + createSystemAssetsInZygoteLocked(); + assets = sSystemApkAssets; } - } - private static void ensureSystemAssets() { - synchronized (sSync) { - if (sSystem == null) { - AssetManager system = new AssetManager(true); - system.makeStringBlocks(null); - sSystem = system; - } + mObject = nativeCreate(); + if (DEBUG_REFS) { + mNumRefs = 0; + incRefsLocked(hashCode()); } + + // Always set the framework resources. + setApkAssets(assets, false /*invalidateCaches*/); } - - private AssetManager(boolean isSystem) { + + /** + * Private constructor that doesn't call ensureSystemAssets. + * Used for the creation of system assets. + */ + @SuppressWarnings("unused") + private AssetManager(boolean sentinel) { + mObject = nativeCreate(); if (DEBUG_REFS) { - synchronized (this) { - mNumRefs = 0; - incRefsLocked(this.hashCode()); + mNumRefs = 0; + incRefsLocked(hashCode()); + } + } + + /** + * This must be called from Zygote so that system assets are shared by all applications. + * @hide + */ + private static void createSystemAssetsInZygoteLocked() { + if (sSystem != null) { + return; + } + + // Make sure that all IDMAPs are up to date. + nativeVerifySystemIdmaps(); + + try { + ArrayList apkAssets = new ArrayList<>(); + apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/)); + + // Load all static RROs. + try (FileInputStream fis = new FileInputStream( + "/data/resource-cache/overlays.list"); + BufferedReader br = new BufferedReader(new InputStreamReader(fis))) { + // Acquire a lock so that any idmap scanning doesn't impact the current set. + try (FileLock flock = fis.getChannel().lock(0, Long.MAX_VALUE, + true /*shared*/)) { + for (String line; (line = br.readLine()) != null; ) { + String idmapPath = line.split(" ")[1]; + apkAssets.add( + ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/)); + } + } } + + sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]); + sSystem = new AssetManager(true /*sentinel*/); + sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/); + } catch (IOException e) { + throw new IllegalStateException("Failed to create system AssetManager", e); } - init(true); - if (localLOGV) Log.v(TAG, "New asset manager: " + this); } /** * Return a global shared asset manager that provides access to only * system assets (no application assets). - * {@hide} + * @hide */ public static AssetManager getSystem() { - ensureSystemAssets(); - return sSystem; + synchronized (sSync) { + createSystemAssetsInZygoteLocked(); + return sSystem; + } } /** * Close this asset manager. */ + @Override public void close() { - synchronized(this) { - //System.out.println("Release: num=" + mNumRefs - // + ", released=" + mReleased); - if (mOpen) { - mOpen = false; - decRefsLocked(this.hashCode()); + synchronized (this) { + if (!mOpen) { + return; + } + + mOpen = false; + decRefsLocked(hashCode()); + } + } + + /** + * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)} + * family of methods. + * + * @param apkAssets The new set of paths. + * @param invalidateCaches Whether to invalidate any caches. This should almost always be true. + * Set this to false if you are appending new resources + * (not new configurations). + * @hide + */ + public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) { + Preconditions.checkNotNull(apkAssets, "apkAssets"); + synchronized (this) { + ensureValidLocked(); + mApkAssets = apkAssets; + nativeSetApkAssets(mObject, apkAssets, invalidateCaches); + if (invalidateCaches) { + // Invalidate all caches. + invalidateCachesLocked(-1); + } + } + } + + /** + * Invalidates the caches in this AssetManager according to the bitmask `diff`. + * + * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}. + * @see ActivityInfo.Config + */ + private void invalidateCachesLocked(int diff) { + // TODO(adamlesinski): Currently there are no caches to invalidate in Java code. + } + + /** + * @hide + */ + public @NonNull ApkAssets[] getApkAssets() { + synchronized (this) { + ensureValidLocked(); + return mApkAssets; + } + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addAssetPath(String path) { + return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/); + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addAssetPathAsSharedLibrary(String path) { + return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/); + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addOverlayPath(String path) { + return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/); + } + + private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) { + Preconditions.checkNotNull(path, "path"); + synchronized (this) { + ensureOpenLocked(); + final int count = mApkAssets.length; + for (int i = 0; i < count; i++) { + if (mApkAssets[i].getAssetPath().equals(path)) { + return i + 1; + } + } + + final ApkAssets assets; + try { + if (overlay) { + // TODO(b/70343104): This hardcoded path will be removed once + // addAssetPathInternal is deleted. + final String idmapPath = "/data/resource-cache/" + + path.substring(1).replace('/', '@') + + "@idmap"; + assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/); + } else { + assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib); + } + } catch (IOException e) { + return 0; } + + final ApkAssets[] newApkAssets = Arrays.copyOf(mApkAssets, count + 1); + newApkAssets[count] = assets; + setApkAssets(newApkAssets, true); + return count + 1; + } + } + + /** + * Ensures that the native implementation has not been destroyed. + * The AssetManager may have been closed, but references to it still exist + * and therefore the native implementation is not destroyed. + */ + private void ensureValidLocked() { + if (mObject == 0) { + throw new RuntimeException("AssetManager has been destroyed"); + } + } + + /** + * Ensures that the AssetManager has not been explicitly closed. If this method passes, + * then this implies that ensureValidLocked() also passes. + */ + private void ensureOpenLocked() { + if (!mOpen) { + throw new RuntimeException("AssetManager has been closed"); + } + } + + /** + * Populates {@code outValue} with the data associated a particular + * resource identifier for the current configuration. + * + * @param resId the resource identifier to load + * @param densityDpi the density bucket for which to load the resource + * @param outValue the typed value in which to put the data + * @param resolveRefs {@code true} to resolve references, {@code false} + * to leave them unresolved + * @return {@code true} if the data was loaded into {@code outValue}, + * {@code false} otherwise + */ + boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, + boolean resolveRefs) { + Preconditions.checkNotNull(outValue, "outValue"); + synchronized (this) { + ensureValidLocked(); + final int cookie = nativeGetResourceValue( + mObject, resId, (short) densityDpi, outValue, resolveRefs); + if (cookie <= 0) { + return false; + } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); + } + return true; } } @@ -156,8 +368,7 @@ public final class AssetManager implements AutoCloseable { * @param resId the resource identifier to load * @return the string value, or {@code null} */ - @Nullable - final CharSequence getResourceText(@StringRes int resId) { + @Nullable CharSequence getResourceText(@StringRes int resId) { synchronized (this) { final TypedValue outValue = mValue; if (getResourceValue(resId, 0, outValue, true)) { @@ -172,15 +383,15 @@ public final class AssetManager implements AutoCloseable { * identifier for the current configuration. * * @param resId the resource identifier to load - * @param bagEntryId + * @param bagEntryId the index into the bag to load * @return the string value, or {@code null} */ - @Nullable - final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { + @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { synchronized (this) { + ensureValidLocked(); final TypedValue outValue = mValue; - final int block = loadResourceBagValue(resId, bagEntryId, outValue, true); - if (block < 0) { + final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue); + if (cookie <= 0) { return null; } @@ -189,52 +400,60 @@ public final class AssetManager implements AutoCloseable { outValue.changingConfigurations); if (outValue.type == TypedValue.TYPE_STRING) { - return mStringBlocks[block].get(outValue.data); + return mApkAssets[cookie - 1].getStringFromPool(outValue.data); } return outValue.coerceToString(); } } + int getResourceArraySize(@ArrayRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceArraySize(mObject, resId); + } + } + /** - * Retrieves the string array associated with a particular resource - * identifier for the current configuration. + * Populates `outData` with array elements of `resId`. `outData` is normally + * used with + * {@link TypedArray}. * - * @param resId the resource identifier of the string array - * @return the string array, or {@code null} + * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES} + * long, + * with the indices of the data representing the type, value, asset cookie, + * resource ID, + * configuration change mask, and density of the element. + * + * @param resId The resource ID of an array resource. + * @param outData The array to populate with data. + * @return The length of the array. + * + * @see TypedArray#STYLE_TYPE + * @see TypedArray#STYLE_DATA + * @see TypedArray#STYLE_ASSET_COOKIE + * @see TypedArray#STYLE_RESOURCE_ID + * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS + * @see TypedArray#STYLE_DENSITY */ - @Nullable - final String[] getResourceStringArray(@ArrayRes int resId) { - return getArrayStringResource(resId); + int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) { + Preconditions.checkNotNull(outData, "outData"); + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceArray(mObject, resId, outData); + } } /** - * Populates {@code outValue} with the data associated a particular - * resource identifier for the current configuration. + * Retrieves the string array associated with a particular resource + * identifier for the current configuration. * - * @param resId the resource identifier to load - * @param densityDpi the density bucket for which to load the resource - * @param outValue the typed value in which to put the data - * @param resolveRefs {@code true} to resolve references, {@code false} - * to leave them unresolved - * @return {@code true} if the data was loaded into {@code outValue}, - * {@code false} otherwise + * @param resId the resource identifier of the string array + * @return the string array, or {@code null} */ - final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, - boolean resolveRefs) { + @Nullable String[] getResourceStringArray(@ArrayRes int resId) { synchronized (this) { - final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs); - if (block < 0) { - return false; - } - - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); - - if (outValue.type == TypedValue.TYPE_STRING) { - outValue.string = mStringBlocks[block].get(outValue.data); - } - return true; + ensureValidLocked(); + return nativeGetResourceStringArray(mObject, resId); } } @@ -244,26 +463,48 @@ public final class AssetManager implements AutoCloseable { * * @param resId the resource id of the string array */ - final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { + @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { synchronized (this) { - final int[] rawInfoArray = getArrayStringInfo(resId); + ensureValidLocked(); + final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId); if (rawInfoArray == null) { return null; } + final int rawInfoArrayLen = rawInfoArray.length; final int infoArrayLen = rawInfoArrayLen / 2; - int block; - int index; final CharSequence[] retArray = new CharSequence[infoArrayLen]; for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) { - block = rawInfoArray[i]; - index = rawInfoArray[i + 1]; - retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null; + int cookie = rawInfoArray[i]; + int index = rawInfoArray[i + 1]; + retArray[j] = (index >= 0 && cookie > 0) + ? mApkAssets[cookie - 1].getStringFromPool(index) : null; } return retArray; } } + @Nullable int[] getResourceIntArray(@ArrayRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceIntArray(mObject, resId); + } + } + + /** + * Get the attributes for a style resource. These are the <item> + * elements in + * a <style> resource. + * @param resId The resource ID of the style + * @return An array of attribute IDs. + */ + @AttrRes int[] getStyleAttributes(@StyleRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetStyleAttributes(mObject, resId); + } + } + /** * Populates {@code outValue} with the data associated with a particular * resource identifier for the current configuration. Resolves theme @@ -277,73 +518,88 @@ public final class AssetManager implements AutoCloseable { * @return {@code true} if the data was loaded into {@code outValue}, * {@code false} otherwise */ - final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, + boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, boolean resolveRefs) { - final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs); - if (block < 0) { - return false; + Preconditions.checkNotNull(outValue, "outValue"); + synchronized (this) { + ensureValidLocked(); + final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue, + resolveRefs); + if (cookie <= 0) { + return false; + } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); + } + return true; + } + } + + void dumpTheme(long theme, int priority, String tag, String prefix) { + synchronized (this) { + ensureValidLocked(); + nativeThemeDump(mObject, theme, priority, tag, prefix); } + } - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); + @Nullable String getResourceName(@AnyRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceName(mObject, resId); + } + } - if (outValue.type == TypedValue.TYPE_STRING) { - final StringBlock[] blocks = ensureStringBlocks(); - outValue.string = blocks[block].get(outValue.data); + @Nullable String getResourcePackageName(@AnyRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourcePackageName(mObject, resId); } - return true; } - /** - * Ensures the string blocks are loaded. - * - * @return the string blocks - */ - @NonNull - final StringBlock[] ensureStringBlocks() { + @Nullable String getResourceTypeName(@AnyRes int resId) { synchronized (this) { - if (mStringBlocks == null) { - makeStringBlocks(sSystem.mStringBlocks); - } - return mStringBlocks; + ensureValidLocked(); + return nativeGetResourceTypeName(mObject, resId); } } - /*package*/ final void makeStringBlocks(StringBlock[] seed) { - final int seedNum = (seed != null) ? seed.length : 0; - final int num = getStringBlockCount(); - mStringBlocks = new StringBlock[num]; - if (localLOGV) Log.v(TAG, "Making string blocks for " + this - + ": " + num); - for (int i=0; i Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len; + ensureOpen(); + return nativeAssetReadChar(mAssetNativePtr); } - public final void close() throws IOException { - synchronized (AssetManager.this) { - if (mAsset != 0) { - destroyAsset(mAsset); - mAsset = 0; - decRefsLocked(hashCode()); - } - } - } - public final void mark(int readlimit) { - mMarkPos = seekAsset(mAsset, 0, 0); - } - public final void reset() throws IOException { - seekAsset(mAsset, mMarkPos, -1); - } - public final int read(byte[] b) throws IOException { - return readAsset(mAsset, b, 0, b.length); + + @Override + public final int read(@NonNull byte[] b) throws IOException { + ensureOpen(); + Preconditions.checkNotNull(b, "b"); + return nativeAssetRead(mAssetNativePtr, b, 0, b.length); } - public final int read(byte[] b, int off, int len) throws IOException { - return readAsset(mAsset, b, off, len); + + @Override + public final int read(@NonNull byte[] b, int off, int len) throws IOException { + ensureOpen(); + Preconditions.checkNotNull(b, "b"); + return nativeAssetRead(mAssetNativePtr, b, off, len); } + + @Override public final long skip(long n) throws IOException { - long pos = seekAsset(mAsset, 0, 0); - if ((pos+n) > mLength) { - n = mLength-pos; + ensureOpen(); + long pos = nativeAssetSeek(mAssetNativePtr, 0, 0); + if ((pos + n) > mLength) { + n = mLength - pos; } if (n > 0) { - seekAsset(mAsset, n, 0); + nativeAssetSeek(mAssetNativePtr, n, 0); } return n; } - protected void finalize() throws Throwable - { - close(); + @Override + public final int available() throws IOException { + ensureOpen(); + final long len = nativeAssetGetRemainingLength(mAssetNativePtr); + return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len; } - private long mAsset; - private long mLength; - private long mMarkPos; - } - - /** - * Add an additional set of assets to the asset manager. This can be - * either a directory or ZIP file. Not for use by applications. Returns - * the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public final int addAssetPath(String path) { - return addAssetPathInternal(path, false); - } - - /** - * Add an application assets to the asset manager and loading it as shared library. - * This can be either a directory or ZIP file. Not for use by applications. Returns - * the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public final int addAssetPathAsSharedLibrary(String path) { - return addAssetPathInternal(path, true); - } - - private final int addAssetPathInternal(String path, boolean appAsLib) { - synchronized (this) { - int res = addAssetPathNative(path, appAsLib); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final boolean markSupported() { + return true; } - } - - private native final int addAssetPathNative(String path, boolean appAsLib); - /** - * Add an additional set of assets to the asset manager from an already open - * FileDescriptor. Not for use by applications. - * This does not give full AssetManager functionality for these assets, - * since the origin of the file is not known for purposes of sharing, - * overlay resolution, and other features. However it does allow you - * to do simple access to the contents of the given fd as an apk file. - * Performs a dup of the underlying fd, so you must take care of still closing - * the FileDescriptor yourself (and can do that whenever you want). - * Returns the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public int addAssetFd(FileDescriptor fd, String debugPathName) { - return addAssetFdInternal(fd, debugPathName, false); - } - - private int addAssetFdInternal(FileDescriptor fd, String debugPathName, - boolean appAsLib) { - synchronized (this) { - int res = addAssetFdNative(fd, debugPathName, appAsLib); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final void mark(int readlimit) { + ensureOpen(); + mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0); } - } - - private native int addAssetFdNative(FileDescriptor fd, String debugPathName, - boolean appAsLib); - - /** - * Add a set of assets to overlay an already added set of assets. - * - * This is only intended for application resources. System wide resources - * are handled before any Java code is executed. - * - * {@hide} - */ - public final int addOverlayPath(String idmapPath) { - synchronized (this) { - int res = addOverlayPathNative(idmapPath); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final void reset() throws IOException { + ensureOpen(); + nativeAssetSeek(mAssetNativePtr, mMarkPos, -1); } - } - /** - * See addOverlayPath. - * - * {@hide} - */ - public native final int addOverlayPathNative(String idmapPath); + @Override + public final void close() throws IOException { + if (mAssetNativePtr != 0) { + nativeAssetDestroy(mAssetNativePtr); + mAssetNativePtr = 0; - /** - * Add multiple sets of assets to the asset manager at once. See - * {@link #addAssetPath(String)} for more information. Returns array of - * cookies for each added asset with 0 indicating failure, or null if - * the input array of paths is null. - * {@hide} - */ - public final int[] addAssetPaths(String[] paths) { - if (paths == null) { - return null; + synchronized (AssetManager.this) { + decRefsLocked(hashCode()); + } + } } - int[] cookies = new int[paths.length]; - for (int i = 0; i < paths.length; i++) { - cookies[i] = addAssetPath(paths[i]); + @Override + protected void finalize() throws Throwable { + close(); } - return cookies; + private void ensureOpen() { + if (mAssetNativePtr == 0) { + throw new IllegalStateException("AssetInputStream is closed"); + } + } } /** * Determine whether the state in this asset manager is up-to-date with * the files on the filesystem. If false is returned, you need to * instantiate a new AssetManager class to see the new data. - * {@hide} + * @hide */ - public native final boolean isUpToDate(); + public boolean isUpToDate() { + for (ApkAssets apkAssets : getApkAssets()) { + if (!apkAssets.isUpToDate()) { + return false; + } + } + return true; + } /** * Get the locales that this asset manager contains data for. @@ -784,7 +1071,12 @@ public final class AssetManager implements AutoCloseable { * are of the form {@code ll_CC} where {@code ll} is a two letter language code, * and {@code CC} is a two letter country code. */ - public native final String[] getLocales(); + public String[] getLocales() { + synchronized (this) { + ensureValidLocked(); + return nativeGetLocales(mObject, false /*excludeSystem*/); + } + } /** * Same as getLocales(), except that locales that are only provided by the system (i.e. those @@ -794,131 +1086,57 @@ public final class AssetManager implements AutoCloseable { * assets support Cherokee and French, getLocales() would return * [Cherokee, English, French, German], while getNonSystemLocales() would return * [Cherokee, French]. - * {@hide} + * @hide */ - public native final String[] getNonSystemLocales(); - - /** {@hide} */ - public native final Configuration[] getSizeConfigurations(); + public String[] getNonSystemLocales() { + synchronized (this) { + ensureValidLocked(); + return nativeGetLocales(mObject, true /*excludeSystem*/); + } + } /** - * Change the configuation used when retrieving resources. Not for use by - * applications. - * {@hide} + * @hide */ - public native final void setConfiguration(int mcc, int mnc, String locale, - int orientation, int touchscreen, int density, int keyboard, - int keyboardHidden, int navigation, int screenWidth, int screenHeight, - int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, - int screenLayout, int uiMode, int colorMode, int majorVersion); + Configuration[] getSizeConfigurations() { + synchronized (this) { + ensureValidLocked(); + return nativeGetSizeConfigurations(mObject); + } + } /** - * Retrieve the resource identifier for the given resource name. + * Change the configuration used when retrieving resources. Not for use by + * applications. + * @hide */ - /*package*/ native final int getResourceIdentifier(String name, - String defType, - String defPackage); + public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation, + int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, + int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, + int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) { + synchronized (this) { + ensureValidLocked(); + nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density, + keyboard, keyboardHidden, navigation, screenWidth, screenHeight, + smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode, + colorMode, majorVersion); + } + } - /*package*/ native final String getResourceName(int resid); - /*package*/ native final String getResourcePackageName(int resid); - /*package*/ native final String getResourceTypeName(int resid); - /*package*/ native final String getResourceEntryName(int resid); - - private native final long openAsset(String fileName, int accessMode); - private final native ParcelFileDescriptor openAssetFd(String fileName, - long[] outOffsets) throws IOException; - private native final long openNonAssetNative(int cookie, String fileName, - int accessMode); - private native ParcelFileDescriptor openNonAssetFdNative(int cookie, - String fileName, long[] outOffsets) throws IOException; - private native final void destroyAsset(long asset); - private native final int readAssetChar(long asset); - private native final int readAsset(long asset, byte[] b, int off, int len); - private native final long seekAsset(long asset, long offset, int whence); - private native final long getAssetLength(long asset); - private native final long getAssetRemainingLength(long asset); - - /** Returns true if the resource was found, filling in mRetStringBlock and - * mRetData. */ - private native final int loadResourceValue(int ident, short density, TypedValue outValue, - boolean resolve); - /** Returns true if the resource was found, filling in mRetStringBlock and - * mRetData. */ - private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue, - boolean resolve); - /*package*/ static final int STYLE_NUM_ENTRIES = 6; - /*package*/ static final int STYLE_TYPE = 0; - /*package*/ static final int STYLE_DATA = 1; - /*package*/ static final int STYLE_ASSET_COOKIE = 2; - /*package*/ static final int STYLE_RESOURCE_ID = 3; - - /* Offset within typed data array for native changingConfigurations. */ - static final int STYLE_CHANGING_CONFIGURATIONS = 4; - - /*package*/ static final int STYLE_DENSITY = 5; - /*package*/ native static final void applyStyle(long theme, - int defStyleAttr, int defStyleRes, long xmlParser, - int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress); - /*package*/ native static final boolean resolveAttrs(long theme, - int defStyleAttr, int defStyleRes, int[] inValues, - int[] inAttrs, int[] outValues, int[] outIndices); - /*package*/ native final boolean retrieveAttributes( - long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); - /*package*/ native final int getArraySize(int resource); - /*package*/ native final int retrieveArray(int resource, int[] outValues); - private native final int getStringBlockCount(); - private native final long getNativeStringBlock(int block); - - /** - * {@hide} - */ - public native final String getCookieName(int cookie); - - /** - * {@hide} - */ - public native final SparseArray getAssignedPackageIdentifiers(); - - /** - * {@hide} - */ - public native static final int getGlobalAssetCount(); - - /** - * {@hide} - */ - public native static final String getAssetAllocations(); - /** - * {@hide} + * @hide */ - public native static final int getGlobalAssetManagerCount(); - - private native final long newTheme(); - private native final void deleteTheme(long theme); - /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force); - /*package*/ native static final void copyTheme(long dest, long source); - /*package*/ native static final void clearTheme(long theme); - /*package*/ native static final int loadThemeAttributeValue(long theme, int ident, - TypedValue outValue, - boolean resolve); - /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix); - /*package*/ native static final @NativeConfig int getThemeChangingConfigurations(long theme); - - private native final long openXmlAssetNative(int cookie, String fileName); - - private native final String[] getArrayStringResource(int arrayRes); - private native final int[] getArrayStringInfo(int arrayRes); - /*package*/ native final int[] getArrayIntResource(int arrayRes); - /*package*/ native final int[] getStyleAttributes(int themeRes); - - private native final void init(boolean isSystem); - private native final void destroy(); - - private final void incRefsLocked(long id) { + public SparseArray getAssignedPackageIdentifiers() { + synchronized (this) { + ensureValidLocked(); + return nativeGetAssignedPackageIdentifiers(mObject); + } + } + + private void incRefsLocked(long id) { if (DEBUG_REFS) { if (mRefStacks == null) { - mRefStacks = new HashMap(); + mRefStacks = new HashMap<>(); } RuntimeException ex = new RuntimeException(); ex.fillInStackTrace(); @@ -926,16 +1144,117 @@ public final class AssetManager implements AutoCloseable { } mNumRefs++; } - - private final void decRefsLocked(long id) { + + private void decRefsLocked(long id) { if (DEBUG_REFS && mRefStacks != null) { mRefStacks.remove(id); } mNumRefs--; - //System.out.println("Dec streams: mNumRefs=" + mNumRefs - // + " mReleased=" + mReleased); - if (mNumRefs == 0) { - destroy(); + if (mNumRefs == 0 && mObject != 0) { + nativeDestroy(mObject); + mObject = 0; } } + + // AssetManager setup native methods. + private static native long nativeCreate(); + private static native void nativeDestroy(long ptr); + private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, + boolean invalidateCaches); + private static native void nativeSetConfiguration(long ptr, int mcc, int mnc, + @Nullable String locale, int orientation, int touchscreen, int density, int keyboard, + int keyboardHidden, int navigation, int screenWidth, int screenHeight, + int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, + int uiMode, int colorMode, int majorVersion); + private static native @NonNull SparseArray nativeGetAssignedPackageIdentifiers( + long ptr); + + // File native methods. + private static native @Nullable String[] nativeList(long ptr, @NonNull String path) + throws IOException; + private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode); + private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr, + @NonNull String fileName, long[] outOffsets) throws IOException; + private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName, + int accessMode); + private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie, + @NonNull String fileName, @NonNull long[] outOffsets) throws IOException; + private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName); + + // Primitive resource native methods. + private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density, + @NonNull TypedValue outValue, boolean resolveReferences); + private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId, + @NonNull TypedValue outValue); + + private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr, + @StyleRes int resId); + private static native @Nullable String[] nativeGetResourceStringArray(long ptr, + @ArrayRes int resId); + private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr, + @ArrayRes int resId); + private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId); + private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId); + private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId, + @NonNull int[] outValues); + + // Resource name/ID native methods. + private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name, + @Nullable String defType, @Nullable String defPackage); + private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid); + private static native @Nullable String nativeGetResourcePackageName(long ptr, + @AnyRes int resid); + private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid); + private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid); + private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem); + private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr); + + // Style attribute retrieval native methods. + private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, + @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs, + long outValuesAddress, long outIndicesAddress); + private static native boolean nativeResolveAttrs(long ptr, long themePtr, + @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, + @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); + private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr, + @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); + + // Theme related native methods + private static native long nativeThemeCreate(long ptr); + private static native void nativeThemeDestroy(long themePtr); + private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, + boolean force); + static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr); + static native void nativeThemeClear(long themePtr); + private static native int nativeThemeGetAttributeValue(long ptr, long themePtr, + @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve); + private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag, + String prefix); + static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr); + + // AssetInputStream related native methods. + private static native void nativeAssetDestroy(long assetPtr); + private static native int nativeAssetReadChar(long assetPtr); + private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len); + private static native long nativeAssetSeek(long assetPtr, long offset, int whence); + private static native long nativeAssetGetLength(long assetPtr); + private static native long nativeAssetGetRemainingLength(long assetPtr); + + private static native void nativeVerifySystemIdmaps(); + + // Global debug native methods. + /** + * @hide + */ + public static native int getGlobalAssetCount(); + + /** + * @hide + */ + public static native String getAssetAllocations(); + + /** + * @hide + */ + public static native int getGlobalAssetManagerCount(); } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index e173653cd961..8f58891ed556 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -590,7 +590,7 @@ public class Resources { */ @NonNull public int[] getIntArray(@ArrayRes int id) throws NotFoundException { - int[] res = mResourcesImpl.getAssets().getArrayIntResource(id); + int[] res = mResourcesImpl.getAssets().getResourceIntArray(id); if (res != null) { return res; } @@ -613,13 +613,13 @@ public class Resources { @NonNull public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException { final ResourcesImpl impl = mResourcesImpl; - int len = impl.getAssets().getArraySize(id); + int len = impl.getAssets().getResourceArraySize(id); if (len < 0) { throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id)); } TypedArray array = TypedArray.obtain(this, len); - array.mLength = impl.getAssets().retrieveArray(id, array.mData); + array.mLength = impl.getAssets().getResourceArray(id, array.mData); array.mIndices[0] = 0; return array; @@ -1789,8 +1789,7 @@ public class Resources { // out the attributes from the XML file (applying type information // contained in the resources and such). XmlBlock.Parser parser = (XmlBlock.Parser)set; - mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, attrs, - array.mData, array.mIndices); + mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices); array.mXml = parser; diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 97cb78bc4243..2a4b278bdca8 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -168,7 +168,6 @@ public class ResourcesImpl { mDisplayAdjustments = displayAdjustments; mConfiguration.setToDefaults(); updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo()); - mAssets.ensureStringBlocks(); } public DisplayAdjustments getDisplayAdjustments() { @@ -1274,8 +1273,7 @@ public class ResourcesImpl { void applyStyle(int resId, boolean force) { synchronized (mKey) { - AssetManager.applyThemeStyle(mTheme, resId, force); - + mAssets.applyStyleToTheme(mTheme, resId, force); mThemeResId = resId; mKey.append(resId, force); } @@ -1284,7 +1282,7 @@ public class ResourcesImpl { void setTo(ThemeImpl other) { synchronized (mKey) { synchronized (other.mKey) { - AssetManager.copyTheme(mTheme, other.mTheme); + AssetManager.nativeThemeCopy(mTheme, other.mTheme); mThemeResId = other.mThemeResId; mKey.setTo(other.getKey()); @@ -1307,12 +1305,10 @@ public class ResourcesImpl { // out the attributes from the XML file (applying type information // contained in the resources and such). final XmlBlock.Parser parser = (XmlBlock.Parser) set; - AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes, - parser != null ? parser.mParseState : 0, - attrs, attrs.length, array.mDataAddress, array.mIndicesAddress); + mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs, + array.mDataAddress, array.mIndicesAddress); array.mTheme = wrapper; array.mXml = parser; - return array; } } @@ -1329,7 +1325,7 @@ public class ResourcesImpl { } final TypedArray array = TypedArray.obtain(wrapper.getResources(), len); - AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); + mAssets.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); array.mTheme = wrapper; array.mXml = null; return array; @@ -1349,14 +1345,14 @@ public class ResourcesImpl { @Config int getChangingConfigurations() { synchronized (mKey) { final @NativeConfig int nativeChangingConfig = - AssetManager.getThemeChangingConfigurations(mTheme); + AssetManager.nativeThemeGetChangingConfigurations(mTheme); return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig); } } public void dump(int priority, String tag, String prefix) { synchronized (mKey) { - AssetManager.dumpTheme(mTheme, priority, tag, prefix); + mAssets.dumpTheme(mTheme, priority, tag, prefix); } } @@ -1385,13 +1381,13 @@ public class ResourcesImpl { */ void rebase() { synchronized (mKey) { - AssetManager.clearTheme(mTheme); + AssetManager.nativeThemeClear(mTheme); // Reapply the same styles in the same order. for (int i = 0; i < mKey.mCount; i++) { final int resId = mKey.mResId[i]; final boolean force = mKey.mForce[i]; - AssetManager.applyThemeStyle(mTheme, resId, force); + mAssets.applyStyleToTheme(mTheme, resId, force); } } } diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index f33c75168a5f..cbb3c6df0558 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -61,6 +61,15 @@ public class TypedArray { return attrs; } + // STYLE_ prefixed constants are offsets within the typed data array. + static final int STYLE_NUM_ENTRIES = 6; + static final int STYLE_TYPE = 0; + static final int STYLE_DATA = 1; + static final int STYLE_ASSET_COOKIE = 2; + static final int STYLE_RESOURCE_ID = 3; + static final int STYLE_CHANGING_CONFIGURATIONS = 4; + static final int STYLE_DENSITY = 5; + private final Resources mResources; private DisplayMetrics mMetrics; private AssetManager mAssets; @@ -78,7 +87,7 @@ public class TypedArray { private void resize(int len) { mLength = len; - final int dataLen = len * AssetManager.STYLE_NUM_ENTRIES; + final int dataLen = len * STYLE_NUM_ENTRIES; final int indicesLen = len + 1; final VMRuntime runtime = VMRuntime.getRuntime(); if (mDataAddress == 0 || mData.length < dataLen) { @@ -166,9 +175,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -203,9 +212,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -242,14 +251,13 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_STRING) { - final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; + final int cookie = data[index + STYLE_ASSET_COOKIE]; if (cookie < 0) { - return mXml.getPooledString( - data[index+AssetManager.STYLE_DATA]).toString(); + return mXml.getPooledString(data[index + STYLE_DATA]).toString(); } } return null; @@ -274,11 +282,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); + data[index + STYLE_CHANGING_CONFIGURATIONS]); if ((changingConfigs & ~allowedChangingConfigs) != 0) { return null; } @@ -320,14 +328,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA] != 0; + return data[index + STYLE_DATA] != 0; } final TypedValue v = mValue; @@ -359,14 +367,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } final TypedValue v = mValue; @@ -396,16 +404,16 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FLOAT) { - return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]); + return Float.intBitsToFloat(data[index + STYLE_DATA]); } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } final TypedValue v = mValue; @@ -446,15 +454,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_STRING) { final TypedValue value = mValue; if (getValueAt(index, value)) { @@ -498,7 +506,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -533,7 +541,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -564,15 +572,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -612,15 +620,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimension( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -661,15 +668,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelOffset( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -711,15 +717,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index+AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -755,16 +760,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index+AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -795,15 +799,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } return defValue; @@ -833,15 +836,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FRACTION) { - return TypedValue.complexToFraction( - data[index+AssetManager.STYLE_DATA], base, pbase); + return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -874,10 +876,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) { - final int resid = data[index+AssetManager.STYLE_RESOURCE_ID]; + if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) { + final int resid = data[index + STYLE_RESOURCE_ID]; if (resid != 0) { return resid; } @@ -902,10 +904,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { - return data[index + AssetManager.STYLE_DATA]; + if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { + return data[index + STYLE_DATA]; } return defValue; } @@ -939,7 +941,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -975,7 +977,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -1006,7 +1008,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { return mResources.getTextArray(value.resourceId); } return null; @@ -1027,7 +1029,7 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue); + return getValueAt(index * STYLE_NUM_ENTRIES, outValue); } /** @@ -1043,8 +1045,8 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; - return mData[index + AssetManager.STYLE_TYPE]; + index *= STYLE_NUM_ENTRIES; + return mData[index + STYLE_TYPE]; } /** @@ -1063,9 +1065,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; return type != TypedValue.TYPE_NULL; } @@ -1084,11 +1086,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; return type != TypedValue.TYPE_NULL - || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; + || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; } /** @@ -1109,7 +1111,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { return value; } return null; @@ -1181,16 +1183,16 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * AssetManager.STYLE_NUM_ENTRIES; - if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { + final int index = i * STYLE_NUM_ENTRIES; + if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { // Not an attribute, ignore. continue; } // Null the entry so that we can safely call getZzz(). - data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL; + data[index + STYLE_TYPE] = TypedValue.TYPE_NULL; - final int attr = data[index + AssetManager.STYLE_DATA]; + final int attr = data[index + STYLE_DATA]; if (attr == 0) { // Useless data, ignore. continue; @@ -1231,45 +1233,44 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * AssetManager.STYLE_NUM_ENTRIES; - final int type = data[index + AssetManager.STYLE_TYPE]; + final int index = i * STYLE_NUM_ENTRIES; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { continue; } changingConfig |= ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); + data[index + STYLE_CHANGING_CONFIGURATIONS]); } return changingConfig; } private boolean getValueAt(int index, TypedValue outValue) { final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return false; } outValue.type = type; - outValue.data = data[index+AssetManager.STYLE_DATA]; - outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; - outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID]; + outValue.data = data[index + STYLE_DATA]; + outValue.assetCookie = data[index + STYLE_ASSET_COOKIE]; + outValue.resourceId = data[index + STYLE_RESOURCE_ID]; outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); - outValue.density = data[index+AssetManager.STYLE_DENSITY]; + data[index + STYLE_CHANGING_CONFIGURATIONS]); + outValue.density = data[index + STYLE_DENSITY]; outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null; return true; } private CharSequence loadStringValueAt(int index) { final int[] data = mData; - final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; + final int cookie = data[index + STYLE_ASSET_COOKIE]; if (cookie < 0) { if (mXml != null) { - return mXml.getPooledString( - data[index+AssetManager.STYLE_DATA]); + return mXml.getPooledString(data[index + STYLE_DATA]); } return null; } - return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]); + return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]); } /** @hide */ diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java index e6b957414ea8..d4ccffb83ca5 100644 --- a/core/java/android/content/res/XmlBlock.java +++ b/core/java/android/content/res/XmlBlock.java @@ -16,6 +16,7 @@ package android.content.res; +import android.annotation.Nullable; import android.util.TypedValue; import com.android.internal.util.XmlUtils; @@ -33,7 +34,7 @@ import java.io.Reader; * * {@hide} */ -final class XmlBlock { +final class XmlBlock implements AutoCloseable { private static final boolean DEBUG=false; public XmlBlock(byte[] data) { @@ -48,6 +49,7 @@ final class XmlBlock { mStrings = new StringBlock(nativeGetStringBlock(mNative), false); } + @Override public void close() { synchronized (this) { if (mOpen) { @@ -478,13 +480,13 @@ final class XmlBlock { * are doing! The given native object must exist for the entire lifetime * of this newly creating XmlBlock. */ - XmlBlock(AssetManager assets, long xmlBlock) { + XmlBlock(@Nullable AssetManager assets, long xmlBlock) { mAssets = assets; mNative = xmlBlock; mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false); } - private final AssetManager mAssets; + private @Nullable final AssetManager mAssets; private final long mNative; /*package*/ final StringBlock mStrings; private boolean mOpen = true; diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 543acc7c2158..19cc2d0f5358 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -110,8 +110,8 @@ cc_library_shared { "android_util_AssetManager.cpp", "android_util_Binder.cpp", "android_util_EventLog.cpp", - "android_util_MemoryIntArray.cpp", "android_util_Log.cpp", + "android_util_MemoryIntArray.cpp", "android_util_PathParser.cpp", "android_util_Process.cpp", "android_util_StringBlock.cpp", @@ -189,6 +189,7 @@ cc_library_shared { "android_backup_FileBackupHelperBase.cpp", "android_backup_BackupHelperDispatcher.cpp", "android_app_backup_FullBackup.cpp", + "android_content_res_ApkAssets.cpp", "android_content_res_ObbScanner.cpp", "android_content_res_Configuration.cpp", "android_animation_PropertyValuesHolder.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index aa9a82415f97..e3b5c8f3136a 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -122,6 +122,7 @@ extern int register_android_util_MemoryIntArray(JNIEnv* env); extern int register_android_util_PathParser(JNIEnv* env); extern int register_android_content_StringBlock(JNIEnv* env); extern int register_android_content_XmlBlock(JNIEnv* env); +extern int register_android_content_res_ApkAssets(JNIEnv* env); extern int register_android_graphics_Canvas(JNIEnv* env); extern int register_android_graphics_CanvasProperty(JNIEnv* env); extern int register_android_graphics_ColorFilter(JNIEnv* env); @@ -1340,6 +1341,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_AssetManager), REG_JNI(register_android_content_StringBlock), REG_JNI(register_android_content_XmlBlock), + REG_JNI(register_android_content_res_ApkAssets), REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_Hyphenator), REG_JNI(register_android_text_MeasuredParagraph), diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index dd3e6f02e9fe..c50026ea570e 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include "Utils.h" #include "FontUtils.h" @@ -224,7 +224,8 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b NPE_CHECK_RETURN_ZERO(env, jpath); NativeFamilyBuilder* builder = reinterpret_cast(builderPtr); - AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr); + + Guarded* mgr = AssetManagerForJavaObject(env, jassetMgr); if (NULL == mgr) { builder->axes.clear(); return false; @@ -236,27 +237,33 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b return false; } - Asset* asset; - if (isAsset) { - asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); - } else { - asset = cookie ? mgr->openNonAsset(static_cast(cookie), str.c_str(), - Asset::ACCESS_BUFFER) : mgr->openNonAsset(str.c_str(), Asset::ACCESS_BUFFER); + std::unique_ptr asset; + { + ScopedLock locked_mgr(*mgr); + if (isAsset) { + asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); + } else if (cookie > 0) { + // Valid java cookies are 1-based, but AssetManager cookies are 0-based. + asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast(cookie - 1), + Asset::ACCESS_BUFFER); + } else { + asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER); + } } - if (NULL == asset) { + if (nullptr == asset) { builder->axes.clear(); return false; } const void* buf = asset->getBuffer(false); if (NULL == buf) { - delete asset; builder->axes.clear(); return false; } - sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset)); + sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, + asset.release())); return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); } diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 09e37e1a3de6..49a24a30f77e 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -361,7 +361,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName code->sdkVersion = sdkVersion; code->javaAssetManager = env->NewGlobalRef(jAssetMgr); - code->assetManager = assetManagerForJavaObject(env, jAssetMgr); + code->assetManager = NdkAssetManagerForJavaObject(env, jAssetMgr); if (obbDir != NULL) { dirStr = env->GetStringUTFChars(obbDir, NULL); diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp new file mode 100644 index 000000000000..c0f151b71c93 --- /dev/null +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "android-base/macros.h" +#include "android-base/stringprintf.h" +#include "android-base/unique_fd.h" +#include "androidfw/ApkAssets.h" +#include "utils/misc.h" + +#include "core_jni_helpers.h" +#include "jni.h" +#include "nativehelper/ScopedUtfChars.h" + +using ::android::base::unique_fd; + +namespace android { + +static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboolean system, + jboolean force_shared_lib, jboolean overlay) { + ScopedUtfChars path(env, java_path); + if (path.c_str() == nullptr) { + return 0; + } + + std::unique_ptr apk_assets; + if (overlay) { + apk_assets = ApkAssets::LoadOverlay(path.c_str(), system); + } else if (force_shared_lib) { + apk_assets = ApkAssets::LoadAsSharedLibrary(path.c_str(), system); + } else { + apk_assets = ApkAssets::Load(path.c_str(), system); + } + + if (apk_assets == nullptr) { + std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str()); + jniThrowException(env, "java/io/IOException", error_msg.c_str()); + return 0; + } + return reinterpret_cast(apk_assets.release()); +} + +static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor, + jstring friendly_name, jboolean system, jboolean force_shared_lib) { + ScopedUtfChars friendly_name_utf8(env, friendly_name); + if (friendly_name_utf8.c_str() == nullptr) { + return 0; + } + + int fd = jniGetFDFromFileDescriptor(env, file_descriptor); + if (fd < 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); + return 0; + } + + unique_fd dup_fd(::dup(fd)); + if (dup_fd < 0) { + jniThrowIOException(env, errno); + return 0; + } + + std::unique_ptr apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), + friendly_name_utf8.c_str(), + system, force_shared_lib); + if (apk_assets == nullptr) { + std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d", + friendly_name_utf8.c_str(), dup_fd.get()); + jniThrowException(env, "java/io/IOException", error_msg.c_str()); + return 0; + } + return reinterpret_cast(apk_assets.release()); +} + +static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + delete reinterpret_cast(ptr); +} + +static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + return env->NewStringUTF(apk_assets->GetPath().c_str()); +} + +static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + return reinterpret_cast(apk_assets->GetLoadedArsc()->GetStringPool()); +} + +static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + (void)apk_assets; + return JNI_TRUE; +} + +static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) { + ScopedUtfChars path_utf8(env, file_name); + if (path_utf8.c_str() == nullptr) { + return 0; + } + + const ApkAssets* apk_assets = reinterpret_cast(ptr); + std::unique_ptr asset = apk_assets->Open(path_utf8.c_str(), + Asset::AccessMode::ACCESS_RANDOM); + if (asset == nullptr) { + jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str()); + return 0; + } + + // DynamicRefTable is only needed when looking up resource references. Opening an XML file + // directly from an ApkAssets has no notion of proper resource references. + std::unique_ptr xml_tree = util::make_unique(nullptr /*dynamicRefTable*/); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); + asset.reset(); + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + return reinterpret_cast(xml_tree.release()); +} + +// JNI registration. +static const JNINativeMethod gApkAssetsMethods[] = { + {"nativeLoad", "(Ljava/lang/String;ZZZ)J", (void*)NativeLoad}, + {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZ)J", + (void*)NativeLoadFromFd}, + {"nativeDestroy", "(J)V", (void*)NativeDestroy}, + {"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath}, + {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock}, + {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate}, + {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml}, +}; + +int register_android_content_res_ApkAssets(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods, + arraysize(gApkAssetsMethods)); +} + +} // namespace android diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index c6828c4f60de..403937be7088 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -1,1846 +1,1437 @@ -/* //device/libs/android_runtime/android_util_AssetManager.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ +/* + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #define LOG_TAG "asset" -#include - #include #include #include -#include -#include #include #include +#include +#include #include // for AID_SYSTEM +#include "android-base/logging.h" +#include "android-base/properties.h" +#include "android-base/stringprintf.h" +#include "android_runtime/android_util_AssetManager.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_util_Binder.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" +#include "androidfw/AssetManager2.h" #include "androidfw/AttributeResolution.h" +#include "androidfw/MutexGuard.h" #include "androidfw/ResourceTypes.h" -#include "android_runtime/AndroidRuntime.h" -#include "android_util_Binder.h" #include "core_jni_helpers.h" #include "jni.h" -#include -#include -#include +#include "nativehelper/JNIHelp.h" +#include "nativehelper/ScopedPrimitiveArray.h" +#include "nativehelper/ScopedStringChars.h" +#include "nativehelper/ScopedUtfChars.h" #include "utils/Log.h" -#include "utils/misc.h" #include "utils/String8.h" +#include "utils/misc.h" extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap); extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap); +using ::android::base::StringPrintf; namespace android { -static const bool kThrowOnBadId = false; - // ---------------------------------------------------------------------------- -static struct typedvalue_offsets_t -{ - jfieldID mType; - jfieldID mData; - jfieldID mString; - jfieldID mAssetCookie; - jfieldID mResourceId; - jfieldID mChangingConfigurations; - jfieldID mDensity; +static struct typedvalue_offsets_t { + jfieldID mType; + jfieldID mData; + jfieldID mString; + jfieldID mAssetCookie; + jfieldID mResourceId; + jfieldID mChangingConfigurations; + jfieldID mDensity; } gTypedValueOffsets; -static struct assetfiledescriptor_offsets_t -{ - jfieldID mFd; - jfieldID mStartOffset; - jfieldID mLength; +static struct assetfiledescriptor_offsets_t { + jfieldID mFd; + jfieldID mStartOffset; + jfieldID mLength; } gAssetFileDescriptorOffsets; -static struct assetmanager_offsets_t -{ - jfieldID mObject; +static struct assetmanager_offsets_t { + jfieldID mObject; } gAssetManagerOffsets; -static struct sparsearray_offsets_t -{ - jclass classObject; - jmethodID constructor; - jmethodID put; +static struct { + jfieldID native_ptr; +} gApkAssetsFields; + +static struct sparsearray_offsets_t { + jclass classObject; + jmethodID constructor; + jmethodID put; } gSparseArrayOffsets; -static struct configuration_offsets_t -{ - jclass classObject; - jmethodID constructor; - jfieldID mSmallestScreenWidthDpOffset; - jfieldID mScreenWidthDpOffset; - jfieldID mScreenHeightDpOffset; +static struct configuration_offsets_t { + jclass classObject; + jmethodID constructor; + jfieldID mSmallestScreenWidthDpOffset; + jfieldID mScreenWidthDpOffset; + jfieldID mScreenHeightDpOffset; } gConfigurationOffsets; -jclass g_stringClass = NULL; +jclass g_stringClass = nullptr; // ---------------------------------------------------------------------------- -static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, - const Res_value& value, uint32_t ref, ssize_t block, - uint32_t typeSpecFlags, ResTable_config* config = NULL); - -jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, - const Res_value& value, uint32_t ref, ssize_t block, - uint32_t typeSpecFlags, ResTable_config* config) -{ - env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType); - env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie, - static_cast(table->getTableCookie(block))); - env->SetIntField(outValue, gTypedValueOffsets.mData, value.data); - env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL); - env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref); - env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations, - typeSpecFlags); - if (config != NULL) { - env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density); - } - return block; +// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. +constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { + return cookie != kInvalidCookie ? static_cast(cookie + 1) : -1; } -// This is called by zygote (running as user root) as part of preloadResources. -static void verifySystemIdmaps() -{ - pid_t pid; - char system_id[10]; - - snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM); - - switch (pid = fork()) { - case -1: - ALOGE("failed to fork for idmap: %s", strerror(errno)); - break; - case 0: // child - { - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata; - - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - - capheader.version = _LINUX_CAPABILITY_VERSION; - capheader.pid = 0; - - if (capget(&capheader, &capdata) != 0) { - ALOGE("capget: %s\n", strerror(errno)); - exit(1); - } - - capdata.effective = capdata.permitted; - if (capset(&capheader, &capdata) != 0) { - ALOGE("capset: %s\n", strerror(errno)); - exit(1); - } - - if (setgid(AID_SYSTEM) != 0) { - ALOGE("setgid: %s\n", strerror(errno)); - exit(1); - } - - if (setuid(AID_SYSTEM) != 0) { - ALOGE("setuid: %s\n", strerror(errno)); - exit(1); - } - - // Generic idmap parameters - const char* argv[8]; - int argc = 0; - struct stat st; - - memset(argv, NULL, sizeof(argv)); - argv[argc++] = AssetManager::IDMAP_BIN; - argv[argc++] = "--scan"; - argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; - argv[argc++] = AssetManager::TARGET_APK_PATH; - argv[argc++] = AssetManager::IDMAP_DIR; - - // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, - // use OVERLAY_DIR/ in addition to OVERLAY_DIR. - char subdir[PROP_VALUE_MAX]; - int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir); - if (len > 0) { - String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir; - if (stat(overlayPath.string(), &st) == 0) { - argv[argc++] = overlayPath.string(); - } - } - if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::OVERLAY_DIR; - } - - // Finally, invoke idmap (if any overlay directory exists) - if (argc > 5) { - execv(AssetManager::IDMAP_BIN, (char* const*)argv); - ALOGE("failed to execv for idmap: %s", strerror(errno)); - exit(1); // should never get here - } else { - exit(0); - } - } - break; - default: // parent - waitpid(pid, NULL, 0); - break; - } +constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) { + return cookie > 0 ? static_cast(cookie - 1) : kInvalidCookie; } -// ---------------------------------------------------------------------------- - -// this guy is exported to other jni routines -AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj) -{ - jlong amHandle = env->GetLongField(obj, gAssetManagerOffsets.mObject); - AssetManager* am = reinterpret_cast(amHandle); - if (am != NULL) { - return am; - } - jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); - return NULL; +// This is called by zygote (running as user root) as part of preloadResources. +static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { + switch (pid_t pid = fork()) { + case -1: + PLOG(ERROR) << "failed to fork for idmap"; + break; + + // child + case 0: { + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata; + + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + + capheader.version = _LINUX_CAPABILITY_VERSION; + capheader.pid = 0; + + if (capget(&capheader, &capdata) != 0) { + PLOG(ERROR) << "capget"; + exit(1); + } + + capdata.effective = capdata.permitted; + if (capset(&capheader, &capdata) != 0) { + PLOG(ERROR) << "capset"; + exit(1); + } + + if (setgid(AID_SYSTEM) != 0) { + PLOG(ERROR) << "setgid"; + exit(1); + } + + if (setuid(AID_SYSTEM) != 0) { + PLOG(ERROR) << "setuid"; + exit(1); + } + + // Generic idmap parameters + const char* argv[8]; + int argc = 0; + struct stat st; + + memset(argv, 0, sizeof(argv)); + argv[argc++] = AssetManager::IDMAP_BIN; + argv[argc++] = "--scan"; + argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; + argv[argc++] = AssetManager::TARGET_APK_PATH; + argv[argc++] = AssetManager::IDMAP_DIR; + + // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, + // use OVERLAY_DIR/ in addition to OVERLAY_DIR. + std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY, + ""); + if (!overlay_theme_path.empty()) { + overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path; + if (stat(overlay_theme_path.c_str(), &st) == 0) { + argv[argc++] = overlay_theme_path.c_str(); + } + } + + if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::OVERLAY_DIR; + } + + // Finally, invoke idmap (if any overlay directory exists) + if (argc > 5) { + execv(AssetManager::IDMAP_BIN, (char* const*)argv); + PLOG(ERROR) << "failed to execv for idmap"; + exit(1); // should never get here + } else { + exit(0); + } + } break; + + // parent + default: + waitpid(pid, nullptr, 0); + break; + } } -static jlong android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz, - jstring fileName, jint mode) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openAsset in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Empty file name"); - return -1; - } - - if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM - && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return -1; - } - - Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return -1; - } - - //printf("Created Asset Stream: %p\n", a); - - return reinterpret_cast(a); +static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref, + uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType); + env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie, + ApkAssetsCookieToJavaCookie(cookie)); + env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data); + env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr); + env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref); + env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags); + if (config != nullptr) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density); + } + return static_cast(ApkAssetsCookieToJavaCookie(cookie)); } -static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets) -{ - off64_t startOffset, length; - int fd = a->openFileDescriptor(&startOffset, &length); - delete a; - - if (fd < 0) { - jniThrowException(env, "java/io/FileNotFoundException", - "This file can not be opened as a file descriptor; it is probably compressed"); - return NULL; - } - - jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0); - if (offsets == NULL) { - close(fd); - return NULL; - } - - offsets[0] = startOffset; - offsets[1] = length; - - env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0); +// ---------------------------------------------------------------------------- - jobject fileDesc = jniCreateFileDescriptor(env, fd); - if (fileDesc == NULL) { - close(fd); - return NULL; - } +// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance. +struct GuardedAssetManager : public ::AAssetManager { + Guarded guarded_assetmanager; +}; - return newParcelFileDescriptor(env, fileDesc); +::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { + jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject); + ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle); + if (am == nullptr) { + jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); + return nullptr; + } + return am; } -static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz, - jstring fileName, jlongArray outOffsets) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ALOGV("openAssetFd in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - //printf("Created Asset Stream: %p\n", a); - - return returnParcelFileDescriptor(env, a, outOffsets); +Guarded* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) { + if (assetmanager == nullptr) { + return nullptr; + } + return &reinterpret_cast(assetmanager)->guarded_assetmanager; } -static jlong android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName, - jint mode) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return -1; - } - - if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM - && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return -1; - } - - Asset* a = cookie - ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), - (Asset::AccessMode)mode) - : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return -1; - } - - //printf("Created Asset Stream: %p\n", a); - - return reinterpret_cast(a); +Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { + return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager)); } -static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName, - jlongArray outOffsets) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ALOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - Asset* a = cookie - ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), Asset::ACCESS_RANDOM) - : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - //printf("Created Asset Stream: %p\n", a); - - return returnParcelFileDescriptor(env, a, outOffsets); +static Guarded& AssetManagerFromLong(jlong ptr) { + return *AssetManagerForNdkAssetManager(reinterpret_cast(ptr)); } -static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz, - jstring fileName) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - AssetDir* dir = am->openDir(fileName8.c_str()); - - if (dir == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - size_t N = dir->getFileCount(); - - jobjectArray array = env->NewObjectArray(dir->getFileCount(), - g_stringClass, NULL); - if (array == NULL) { - delete dir; - return NULL; - } - - for (size_t i=0; igetFileName(i); - jstring str = env->NewStringUTF(name.string()); - if (str == NULL) { - delete dir; - return NULL; - } - env->SetObjectArrayElement(array, i, str); - env->DeleteLocalRef(str); - } - - delete dir; - - return array; +static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr asset, + jlongArray out_offsets) { + off64_t start_offset, length; + int fd = asset->openFileDescriptor(&start_offset, &length); + asset.reset(); + + if (fd < 0) { + jniThrowException(env, "java/io/FileNotFoundException", + "This file can not be opened as a file descriptor; it is probably " + "compressed"); + return nullptr; + } + + jlong* offsets = reinterpret_cast(env->GetPrimitiveArrayCritical(out_offsets, 0)); + if (offsets == nullptr) { + close(fd); + return nullptr; + } + + offsets[0] = start_offset; + offsets[1] = length; + + env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0); + + jobject file_desc = jniCreateFileDescriptor(env, fd); + if (file_desc == nullptr) { + close(fd); + return nullptr; + } + return newParcelFileDescriptor(env, file_desc); } -static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - //printf("Destroying Asset Stream: %p\n", a); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return; - } - - delete a; +static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) { + return Asset::getGlobalCount(); } -static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - uint8_t b; - ssize_t res = a->read(&b, 1); - return res == 1 ? b : -1; +static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) { + String8 alloc = Asset::getAssetAllocations(); + if (alloc.length() <= 0) { + return nullptr; + } + return env->NewStringUTF(alloc.string()); } -static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz, - jlong assetHandle, jbyteArray bArray, - jint off, jint len) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL || bArray == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - if (len == 0) { - return 0; - } - - jsize bLen = env->GetArrayLength(bArray); - if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); - return -1; - } - - jbyte* b = env->GetByteArrayElements(bArray, NULL); - ssize_t res = a->read(b+off, len); - env->ReleaseByteArrayElements(bArray, b, 0); - - if (res > 0) return static_cast(res); - - if (res < 0) { - jniThrowException(env, "java/io/IOException", ""); - } - return -1; +static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) { + // TODO(adamlesinski): Switch to AssetManager2. + return AssetManager::getGlobalCount(); } -static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz, - jlong assetHandle, - jlong offset, jint whence) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - return a->seek( - offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)); +static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) { + // AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and + // AssetManager2 in a contiguous block (GuardedAssetManager). + return reinterpret_cast(new GuardedAssetManager()); } -static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - return a->getLength(); +static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + delete reinterpret_cast(ptr); } -static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; +static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jobjectArray apk_assets_array, jboolean invalidate_caches) { + const jsize apk_assets_len = env->GetArrayLength(apk_assets_array); + std::vector apk_assets; + apk_assets.reserve(apk_assets_len); + for (jsize i = 0; i < apk_assets_len; i++) { + jobject obj = env->GetObjectArrayElement(apk_assets_array, i); + if (obj == nullptr) { + std::string msg = StringPrintf("ApkAssets at index %d is null", i); + jniThrowNullPointerException(env, msg.c_str()); + return; } - return a->getRemainingLength(); -} - -static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz, - jstring path, jboolean appAsLib) -{ - ScopedUtfChars path8(env, path); - if (path8.c_str() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; + jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr); + if (env->ExceptionCheck()) { + return; } + apk_assets.push_back(reinterpret_cast(apk_assets_native_ptr)); + } - int32_t cookie; - bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib); - - return (res) ? static_cast(cookie) : 0; + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + assetmanager->SetApkAssets(apk_assets, invalidate_caches); } -static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz, - jstring idmapPath) -{ - ScopedUtfChars idmapPath8(env, idmapPath); - if (idmapPath8.c_str() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - int32_t cookie; - bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie); - - return (res) ? (jint)cookie : 0; +static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc, + jstring locale, jint orientation, jint touchscreen, jint density, + jint keyboard, jint keyboard_hidden, jint navigation, + jint screen_width, jint screen_height, + jint smallest_screen_width_dp, jint screen_width_dp, + jint screen_height_dp, jint screen_layout, jint ui_mode, + jint color_mode, jint major_version) { + ResTable_config configuration; + memset(&configuration, 0, sizeof(configuration)); + configuration.mcc = static_cast(mcc); + configuration.mnc = static_cast(mnc); + configuration.orientation = static_cast(orientation); + configuration.touchscreen = static_cast(touchscreen); + configuration.density = static_cast(density); + configuration.keyboard = static_cast(keyboard); + configuration.inputFlags = static_cast(keyboard_hidden); + configuration.navigation = static_cast(navigation); + configuration.screenWidth = static_cast(screen_width); + configuration.screenHeight = static_cast(screen_height); + configuration.smallestScreenWidthDp = static_cast(smallest_screen_width_dp); + configuration.screenWidthDp = static_cast(screen_width_dp); + configuration.screenHeightDp = static_cast(screen_height_dp); + configuration.screenLayout = static_cast(screen_layout); + configuration.uiMode = static_cast(ui_mode); + configuration.colorMode = static_cast(color_mode); + configuration.sdkVersion = static_cast(major_version); + + if (locale != nullptr) { + ScopedUtfChars locale_utf8(env, locale); + CHECK(locale_utf8.c_str() != nullptr); + configuration.setBcp47Locale(locale_utf8.c_str()); + } + + // Constants duplicated from Java class android.content.res.Configuration. + static const jint kScreenLayoutRoundMask = 0x300; + static const jint kScreenLayoutRoundShift = 8; + + // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer + // in C++. We must extract the round qualifier out of the Java screenLayout and put it + // into screenLayout2. + configuration.screenLayout2 = + static_cast((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + assetmanager->SetConfiguration(configuration); } -static jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz, - jobject fileDescriptor, jstring debugPathName, - jboolean appAsLib) -{ - ScopedUtfChars debugPathName8(env, debugPathName); +static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); - int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (fd < 0) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); - return 0; - } + jobject sparse_array = + env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor); - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } + if (sparse_array == nullptr) { + // An exception is pending. + return nullptr; + } - int dupfd = ::dup(fd); - if (dupfd < 0) { - jniThrowIOException(env, errno); - return 0; + assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) { + jstring jpackage_name = env->NewStringUTF(package_name.c_str()); + if (jpackage_name == nullptr) { + // An exception is pending. + return; } - int32_t cookie; - bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib); - - return (res) ? static_cast(cookie) : 0; -} - -static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_TRUE; - } - return am->isUpToDate() ? JNI_TRUE : JNI_FALSE; + env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast(package_id), + jpackage_name); + }); + return sparse_array; } -static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales) -{ - Vector locales; - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; +static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) { + ScopedUtfChars path_utf8(env, path); + if (path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + std::vector all_file_paths; + { + StringPiece normalized_path = path_utf8.c_str(); + if (normalized_path.data()[0] == '/') { + normalized_path = normalized_path.substr(1); + } + std::string root_path = StringPrintf("assets/%s", normalized_path.data()); + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + for (const ApkAssets* assets : assetmanager->GetApkAssets()) { + assets->ForEachFile(root_path, [&](const StringPiece& file_path, FileType type) { + if (type == FileType::kFileTypeRegular) { + all_file_paths.push_back(file_path.to_string()); + } + }); } + } - am->getLocales(&locales, includeSystemLocales); + jobjectArray array = env->NewObjectArray(all_file_paths.size(), g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } - const int N = locales.size(); + jsize index = 0; + for (const std::string& file_path : all_file_paths) { + jstring java_string = env->NewStringUTF(file_path.c_str()); - jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL); - if (result == NULL) { - return NULL; + // Check for errors creating the strings (if malformed or no memory). + if (env->ExceptionCheck()) { + return nullptr; } - for (int i=0; iNewStringUTF(locales[i].string()); - if (str == NULL) { - return NULL; - } - env->SetObjectArrayElement(result, i, str); - env->DeleteLocalRef(str); - } + env->SetObjectArrayElement(array, index++, java_string); - return result; + // If we have a large amount of string in our array, we might overflow the + // local reference table of the VM. + env->DeleteLocalRef(java_string); + } + return array; } -static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz) -{ - return getLocales(env, clazz, true /* include system locales */); +static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, + jint access_mode) { + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && + access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset = + assetmanager->Open(asset_path_utf8.c_str(), static_cast(access_mode)); + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + return reinterpret_cast(asset.release()); } -static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz) -{ - return getLocales(env, clazz, false /* don't include system locales */); +static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, + jlongArray out_offsets) { + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return nullptr; + } + return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); } -static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) { - jobject result = env->NewObject(gConfigurationOffsets.classObject, - gConfigurationOffsets.constructor); - if (result == NULL) { - return NULL; - } - - env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, - config.smallestScreenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); - - return result; +static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path, jint access_mode) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && + access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, + static_cast(access_mode)); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), + static_cast(access_mode)); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + return reinterpret_cast(asset.release()); } -static jobjectArray getSizeConfigurationsInternal(JNIEnv* env, - const Vector& configs) { - const int N = configs.size(); - jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL); - if (result == NULL) { - return NULL; - } - - for (int i=0; iDeleteLocalRef(result); - return NULL; - } - - env->SetObjectArrayElement(result, i, config); - env->DeleteLocalRef(config); - } - - return result; +static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path, jlongArray out_offsets) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return nullptr; + } + return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); } -static jobjectArray android_content_AssetManager_getSizeConfigurations(JNIEnv* env, jobject clazz) { - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - const ResTable& res(am->getResources()); - Vector configs; - res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */); - - return getSizeConfigurationsInternal(env, configs); +static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + + // May be nullptr. + const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie); + + std::unique_ptr xml_tree = util::make_unique(dynamic_ref_table); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); + asset.reset(); + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + return reinterpret_cast(xml_tree.release()); } -static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz, - jint mcc, jint mnc, - jstring locale, jint orientation, - jint touchscreen, jint density, - jint keyboard, jint keyboardHidden, - jint navigation, - jint screenWidth, jint screenHeight, - jint smallestScreenWidthDp, - jint screenWidthDp, jint screenHeightDp, - jint screenLayout, jint uiMode, - jint colorMode, jint sdkVersion) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return; - } - - ResTable_config config; - memset(&config, 0, sizeof(config)); - - const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL; - - // Constants duplicated from Java class android.content.res.Configuration. - static const jint kScreenLayoutRoundMask = 0x300; - static const jint kScreenLayoutRoundShift = 8; - - config.mcc = (uint16_t)mcc; - config.mnc = (uint16_t)mnc; - config.orientation = (uint8_t)orientation; - config.touchscreen = (uint8_t)touchscreen; - config.density = (uint16_t)density; - config.keyboard = (uint8_t)keyboard; - config.inputFlags = (uint8_t)keyboardHidden; - config.navigation = (uint8_t)navigation; - config.screenWidth = (uint16_t)screenWidth; - config.screenHeight = (uint16_t)screenHeight; - config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp; - config.screenWidthDp = (uint16_t)screenWidthDp; - config.screenHeightDp = (uint16_t)screenHeightDp; - config.screenLayout = (uint8_t)screenLayout; - config.uiMode = (uint8_t)uiMode; - config.colorMode = (uint8_t)colorMode; - config.sdkVersion = (uint16_t)sdkVersion; - config.minorVersion = 0; - - // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer - // in C++. We must extract the round qualifier out of the Java screenLayout and put it - // into screenLayout2. - config.screenLayout2 = - (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); - - am->setConfiguration(config, locale8); - - if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8); +static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jshort density, jobject typed_value, + jboolean resolve_references) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Res_value value; + ResTable_config selected_config; + uint32_t flags; + ApkAssetsCookie cookie = + assetmanager->GetResource(static_cast(resid), false /*may_be_bag*/, + static_cast(density), &value, &selected_config, &flags); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t ref = static_cast(resid); + if (resolve_references) { + cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + } + return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value); } -static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz, - jstring name, - jstring defType, - jstring defPackage) -{ - ScopedStringChars name16(env, name); - if (name16.get() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - const char16_t* defType16 = reinterpret_cast(defType) - ? reinterpret_cast(env->GetStringChars(defType, NULL)) - : NULL; - jsize defTypeLen = defType - ? env->GetStringLength(defType) : 0; - const char16_t* defPackage16 = reinterpret_cast(defPackage) - ? reinterpret_cast(env->GetStringChars(defPackage, - NULL)) - : NULL; - jsize defPackageLen = defPackage - ? env->GetStringLength(defPackage) : 0; - - jint ident = am->getResources().identifierForName( - reinterpret_cast(name16.get()), name16.size(), - defType16, defTypeLen, defPackage16, defPackageLen); - - if (defPackage16) { - env->ReleaseStringChars(defPackage, - reinterpret_cast(defPackage16)); - } - if (defType16) { - env->ReleaseStringChars(defType, - reinterpret_cast(defType16)); - } - - return ident; +static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jint bag_entry_id, jobject typed_value) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t type_spec_flags = bag->type_spec_flags; + ApkAssetsCookie cookie = kInvalidCookie; + const Res_value* bag_value = nullptr; + for (const ResolvedBag::Entry& entry : bag) { + if (entry.key == static_cast(bag_entry_id)) { + cookie = entry.cookie; + bag_value = &entry.value; + + // Keep searching (the old implementation did that). + } + } + + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + Res_value value = *bag_value; + uint32_t ref = static_cast(resid); + ResTable_config selected_config; + cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value); } -static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - String16 str; - if (name.package != NULL) { - str.setTo(name.package, name.packageLen); - } - if (name.type8 != NULL || name.type != NULL) { - if (str.size() > 0) { - char16_t div = ':'; - str.append(&div, 1); - } - if (name.type8 != NULL) { - str.append(String16(name.type8, name.typeLen)); - } else { - str.append(name.type, name.typeLen); - } - } - if (name.name8 != NULL || name.name != NULL) { - if (str.size() > 0) { - char16_t div = '/'; - str.append(&div, 1); - } - if (name.name8 != NULL) { - str.append(String16(name.name8, name.nameLen)); - } else { - str.append(name.name, name.nameLen); - } - } - - return env->NewString((const jchar*)str.string(), str.size()); +static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count); + if (env->ExceptionCheck()) { + return nullptr; + } + + for (uint32_t i = 0; i < bag->entry_count; i++) { + jint attr_resid = bag->entries[i].key; + env->SetIntArrayRegion(array, i, 1, &attr_resid); + } + return array; } -static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - if (name.package != NULL) { - return env->NewString((const jchar*)name.package, name.packageLen); - } - - return NULL; +static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } + + for (uint32_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + + // Resolve any references to their final value. + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return nullptr; + } + + if (value.dataType == Res_value::TYPE_STRING) { + const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie]; + const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool(); + + jstring java_string = nullptr; + size_t str_len; + const char* str_utf8 = pool->string8At(value.data, &str_len); + if (str_utf8 != nullptr) { + java_string = env->NewStringUTF(str_utf8); + } else { + const char16_t* str_utf16 = pool->stringAt(value.data, &str_len); + java_string = env->NewString(reinterpret_cast(str_utf16), str_len); + } + + // Check for errors creating the strings (if malformed or no memory). + if (env->ExceptionCheck()) { + return nullptr; + } + + env->SetObjectArrayElement(array, i, java_string); + + // If we have a large amount of string in our array, we might overflow the + // local reference table of the VM. + env->DeleteLocalRef(java_string); + } + } + return array; } -static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - if (name.type8 != NULL) { - return env->NewStringUTF(name.type8); - } - - if (name.type != NULL) { - return env->NewString((const jchar*)name.type, name.typeLen); - } +static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count * 2); + if (array == nullptr) { + return nullptr; + } + + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); + if (buffer == nullptr) { + return nullptr; + } + + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); + return nullptr; + } + + jint string_index = -1; + if (value.dataType == Res_value::TYPE_STRING) { + string_index = static_cast(value.data); + } + + buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie); + buffer[(i * 2) + 1] = string_index; + } + env->ReleasePrimitiveArrayCritical(array, buffer, 0); + return array; +} - return NULL; +static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count); + if (array == nullptr) { + return nullptr; + } + + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); + if (buffer == nullptr) { + return nullptr; + } + + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); + return nullptr; + } + + if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) { + buffer[i] = static_cast(value.data); + } + } + env->ReleasePrimitiveArrayCritical(array, buffer, 0); + return array; } -static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } +static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return -1; + } + return static_cast(bag->entry_count); +} - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } +static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jintArray out_data) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return -1; + } - if (name.name8 != NULL) { - return env->NewStringUTF(name.name8); - } + const jsize out_data_length = env->GetArrayLength(out_data); + if (env->ExceptionCheck()) { + return -1; + } - if (name.name != NULL) { - return env->NewString((const jchar*)name.name, name.nameLen); - } + if (static_cast(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough"); + return -1; + } - return NULL; + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(out_data, nullptr)); + if (buffer == nullptr) { + return -1; + } + + jint* cursor = buffer; + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + selected_config.density = 0; + uint32_t flags = bag->type_spec_flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT); + return -1; + } + + // Deal with the special @null value -- it turns back to TYPE_NULL. + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + } + + cursor[STYLE_TYPE] = static_cast(value.dataType); + cursor[STYLE_DATA] = static_cast(value.data); + cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + cursor[STYLE_RESOURCE_ID] = static_cast(ref); + cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast(flags); + cursor[STYLE_DENSITY] = static_cast(selected_config.density); + cursor += STYLE_NUM_ENTRIES; + } + env->ReleasePrimitiveArrayCritical(out_data, buffer, 0); + return static_cast(bag->entry_count); } -static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz, - jint ident, - jshort density, - jobject outValue, - jboolean resolve) -{ - if (outValue == NULL) { - jniThrowNullPointerException(env, "outValue"); - return 0; - } - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - Res_value value; - ResTable_config config; - uint32_t typeSpecFlags; - ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - uint32_t ref = ident; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - } - if (block >= 0) { - return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config); - } - - return static_cast(block); +static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name, + jstring def_type, jstring def_package) { + ScopedUtfChars name_utf8(env, name); + if (name_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + std::string type; + if (def_type != nullptr) { + ScopedUtfChars type_utf8(env, def_type); + CHECK(type_utf8.c_str() != nullptr); + type = type_utf8.c_str(); + } + + std::string package; + if (def_package != nullptr) { + ScopedUtfChars package_utf8(env, def_package); + CHECK(package_utf8.c_str() != nullptr); + package = package_utf8.c_str(); + } + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + return static_cast(assetmanager->GetResourceId(name_utf8.c_str(), type, package)); } -static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz, - jint ident, jint bagEntryId, - jobject outValue, jboolean resolve) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); +static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } - // Now lock down the resource object and start pulling stuff from it. - res.lock(); + std::string result; + if (name.package != nullptr) { + result.append(name.package, name.package_len); + } - ssize_t block = -1; - Res_value value; - - const ResTable::bag_entry* entry = NULL; - uint32_t typeSpecFlags; - ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags); - - for (ssize_t i=0; imap.name.ident) { - block = entry->stringBlock; - value = entry->map.value; - } - entry++; + if (name.type != nullptr || name.type16 != nullptr) { + if (!result.empty()) { + result += ":"; } - res.unlock(); - - if (block < 0) { - return static_cast(block); + if (name.type != nullptr) { + result.append(name.type, name.type_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len)); } + } - uint32_t ref = ident; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } + if (name.entry != nullptr || name.entry16 != nullptr) { + if (!result.empty()) { + result += "/"; } - if (block >= 0) { - return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags); - } - - return static_cast(block); -} - -static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - return am->getResources().getTableCount(); -} -static jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz, - jint block) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; + if (name.entry != nullptr) { + result.append(name.entry, name.entry_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); } - return reinterpret_cast(am->getResources().getTableStringBlock(block)); + } + return env->NewStringUTF(result.c_str()); } -static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz, - jint cookie) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - String8 name(am->getAssetPath(static_cast(cookie))); - if (name.length() == 0) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name"); - return NULL; - } - jstring str = env->NewStringUTF(name.string()); - return str; +static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.package != nullptr) { + return env->NewStringUTF(name.package); + } + return nullptr; } -static jobject android_content_AssetManager_getAssignedPackageIdentifiers(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - const ResTable& res = am->getResources(); - - jobject sparseArray = env->NewObject(gSparseArrayOffsets.classObject, - gSparseArrayOffsets.constructor); - const size_t N = res.getBasePackageCount(); - for (size_t i = 0; i < N; i++) { - const String16 name = res.getBasePackageName(i); - env->CallVoidMethod( - sparseArray, gSparseArrayOffsets.put, - static_cast(res.getBasePackageId(i)), - env->NewString(reinterpret_cast(name.string()), - name.size())); - } - return sparseArray; +static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.type != nullptr) { + return env->NewStringUTF(name.type); + } else if (name.type16 != nullptr) { + return env->NewString(reinterpret_cast(name.type16), name.type_len); + } + return nullptr; } -static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - return reinterpret_cast(new ResTable::Theme(am->getResources())); +static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.entry != nullptr) { + return env->NewStringUTF(name.entry); + } else if (name.entry16 != nullptr) { + return env->NewString(reinterpret_cast(name.entry16), name.entry_len); + } + return nullptr; } -static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz, - jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - delete theme; +static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr, + jboolean exclude_system) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::set locales = + assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/); + + jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } + + size_t idx = 0; + for (const std::string& locale : locales) { + jstring java_string = env->NewStringUTF(locale.c_str()); + if (java_string == nullptr) { + return nullptr; + } + env->SetObjectArrayElement(array, idx++, java_string); + env->DeleteLocalRef(java_string); + } + return array; } -static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz, - jlong themeHandle, - jint styleRes, - jboolean force) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - theme->applyStyle(styleRes, force ? true : false); +static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) { + jobject result = + env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor); + if (result == nullptr) { + return nullptr; + } + + env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, + config.smallestScreenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); + return result; } -static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz, - jlong destHandle, jlong srcHandle) -{ - ResTable::Theme* dest = reinterpret_cast(destHandle); - ResTable::Theme* src = reinterpret_cast(srcHandle); - dest->setTo(*src); -} +static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::set configurations = + assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/); -static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - theme->clear(); -} + jobjectArray array = + env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr); + if (array == nullptr) { + return nullptr; + } -static jint android_content_AssetManager_loadThemeAttributeValue( - JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - const ResTable& res(theme->getResTable()); - - Res_value value; - // XXX value could be different in different configs! - uint32_t typeSpecFlags = 0; - ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags); - uint32_t ref = 0; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } + size_t idx = 0; + for (const ResTable_config& configuration : configurations) { + jobject java_configuration = ConstructConfigurationObject(env, configuration); + if (java_configuration == nullptr) { + return nullptr; } - return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block; -} -static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz, - jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - return theme->getChangingConfigurations(); + env->SetObjectArrayElement(array, idx++, java_configuration); + env->DeleteLocalRef(java_configuration); + } + return array; } -static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz, - jlong themeHandle, jint pri, - jstring tag, jstring prefix) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - const ResTable& res(theme->getResTable()); - (void)res; - - // XXX Need to use params. - theme->dumpToLog(); +static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr, + jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); + uint32_t* out_values = reinterpret_cast(out_values_ptr); + uint32_t* out_indices = reinterpret_cast(out_indices_ptr); + + jsize attrs_len = env->GetArrayLength(java_attrs); + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return; + } + + ApplyStyle(theme, xml_parser, static_cast(def_style_attr), + static_cast(def_style_resid), reinterpret_cast(attrs), attrs_len, + out_values, out_indices); + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); } -static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz, - jlong themeToken, - jint defStyleAttr, - jint defStyleRes, - jintArray inValues, - jintArray attrs, - jintArray outValues, - jintArray outIndices) -{ - if (themeToken == 0) { - jniThrowNullPointerException(env, "theme token"); - return JNI_FALSE; - } - if (attrs == NULL) { - jniThrowNullPointerException(env, "attrs"); - return JNI_FALSE; - } - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - const jsize NI = env->GetArrayLength(attrs); - const jsize NV = env->GetArrayLength(outValues); - if (NV < (NI*STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); +static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint def_style_attr, jint def_style_resid, jintArray java_values, + jintArray java_attrs, jintArray out_java_values, + jintArray out_java_indices) { + const jsize attrs_len = env->GetArrayLength(java_attrs); + const jsize out_values_len = env->GetArrayLength(out_java_values); + if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); + return JNI_FALSE; + } + + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return JNI_FALSE; + } + + jint* values = nullptr; + jsize values_len = 0; + if (java_values != nullptr) { + values_len = env->GetArrayLength(java_values); + values = reinterpret_cast(env->GetPrimitiveArrayCritical(java_values, nullptr)); + if (values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return JNI_FALSE; + } + } + + jint* out_values = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); + if (out_values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + return JNI_FALSE; + } + + jint* out_indices = nullptr; + if (out_java_indices != nullptr) { + jsize out_indices_len = env->GetArrayLength(out_java_indices); + if (out_indices_len > attrs_len) { + out_indices = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); + if (out_indices == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); return JNI_FALSE; - } + } + } + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + bool result = ResolveAttrs( + theme, static_cast(def_style_attr), static_cast(def_style_resid), + reinterpret_cast(values), values_len, reinterpret_cast(attrs), + attrs_len, reinterpret_cast(out_values), reinterpret_cast(out_indices)); + if (out_indices != nullptr) { + env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); + } + + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return result ? JNI_TRUE : JNI_FALSE; +} - jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); - if (src == NULL) { +static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jlong xml_parser_ptr, jintArray java_attrs, + jintArray out_java_values, jintArray out_java_indices) { + const jsize attrs_len = env->GetArrayLength(java_attrs); + const jsize out_values_len = env->GetArrayLength(out_java_values); + if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); + return JNI_FALSE; + } + + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return JNI_FALSE; + } + + jint* out_values = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); + if (out_values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return JNI_FALSE; + } + + jint* out_indices = nullptr; + if (out_java_indices != nullptr) { + jsize out_indices_len = env->GetArrayLength(out_java_indices); + if (out_indices_len > attrs_len) { + out_indices = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); + if (out_indices == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); return JNI_FALSE; + } } + } - jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0); - const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues); + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - if (baseDest == NULL) { - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return JNI_FALSE; - } - - jint* indices = NULL; - if (outIndices != NULL) { - if (env->GetArrayLength(outIndices) > NI) { - indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); - } - } + bool result = RetrieveAttributes(assetmanager.get(), xml_parser, + reinterpret_cast(attrs), attrs_len, + reinterpret_cast(out_values), + reinterpret_cast(out_indices)); - ResTable::Theme* theme = reinterpret_cast(themeToken); - bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes, - (uint32_t*) srcValues, NSV, - (uint32_t*) src, NI, - (uint32_t*) baseDest, - (uint32_t*) indices); - - if (indices != NULL) { - env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); - } - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0); - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return result ? JNI_TRUE : JNI_FALSE; + if (out_indices != nullptr) { + env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); + } + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return result ? JNI_TRUE : JNI_FALSE; } -static void android_content_AssetManager_applyStyle(JNIEnv* env, jobject, jlong themeToken, - jint defStyleAttr, jint defStyleRes, jlong xmlParserToken, jintArray attrsObj, jint length, - jlong outValuesAddress, jlong outIndicesAddress) { - jint* attrs = env->GetIntArrayElements(attrsObj, 0); - ResTable::Theme* theme = reinterpret_cast(themeToken); - ResXMLParser* xmlParser = reinterpret_cast(xmlParserToken); - uint32_t* outValues = reinterpret_cast(static_cast(outValuesAddress)); - uint32_t* outIndices = reinterpret_cast(static_cast(outIndicesAddress)); - ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes, - reinterpret_cast(attrs), length, outValues, outIndices); - env->ReleaseIntArrayElements(attrsObj, attrs, JNI_ABORT); +static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + return reinterpret_cast(assetmanager->NewTheme().release()); } -static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz, - jlong xmlParserToken, - jintArray attrs, - jintArray outValues, - jintArray outIndices) -{ - if (xmlParserToken == 0) { - jniThrowNullPointerException(env, "xmlParserToken"); - return JNI_FALSE; - } - if (attrs == NULL) { - jniThrowNullPointerException(env, "attrs"); - return JNI_FALSE; - } - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_FALSE; - } - const ResTable& res(am->getResources()); - ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken; - - const jsize NI = env->GetArrayLength(attrs); - const jsize NV = env->GetArrayLength(outValues); - if (NV < (NI*STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); - return JNI_FALSE; - } - - jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); - if (src == NULL) { - return JNI_FALSE; - } - - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - if (baseDest == NULL) { - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return JNI_FALSE; - } - - jint* indices = NULL; - if (outIndices != NULL) { - if (env->GetArrayLength(outIndices) > NI) { - indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); - } - } - - bool result = RetrieveAttributes(&res, xmlParser, - (uint32_t*) src, NI, - (uint32_t*) baseDest, - (uint32_t*) indices); - - if (indices != NULL) { - env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); - } - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return result ? JNI_TRUE : JNI_FALSE; +static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { + delete reinterpret_cast(theme_ptr); } -static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz, - jint id) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - res.lock(); - const ResTable::bag_entry* defStyleEnt = NULL; - ssize_t bagOff = res.getBagLocked(id, &defStyleEnt); - res.unlock(); - - return static_cast(bagOff); +static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint resid, jboolean force) { + // AssetManager is accessed via the theme, so grab an explicit lock here. + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + theme->ApplyStyle(static_cast(resid), force); + + // TODO(adamlesinski): Consider surfacing exception when result is failure. + // CTS currently expects no exceptions from this method. + // std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid); + // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str()); } -static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz, - jint id, - jintArray outValues) -{ - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_FALSE; - } - const ResTable& res(am->getResources()); - ResTable_config config; - Res_value value; - ssize_t block; - - const jsize NV = env->GetArrayLength(outValues); - - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - jint* dest = baseDest; - if (dest == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", ""); - return JNI_FALSE; - } - - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - - const ResTable::bag_entry* arrayEnt = NULL; - uint32_t arrayTypeSetFlags = 0; - ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags); - const ResTable::bag_entry* endArrayEnt = arrayEnt + - (bagOff >= 0 ? bagOff : 0); - - int i = 0; - uint32_t typeSetFlags; - while (i < NV && arrayEnt < endArrayEnt) { - block = arrayEnt->stringBlock; - typeSetFlags = arrayTypeSetFlags; - config.density = 0; - value = arrayEnt->map.value; - - uint32_t resid = 0; - if (value.dataType != Res_value::TYPE_NULL) { - // Take care of resolving the found resource to its final value. - //printf("Resolving attribute reference\n"); - ssize_t newBlock = res.resolveReference(&value, block, &resid, - &typeSetFlags, &config); - if (kThrowOnBadId) { - if (newBlock == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return JNI_FALSE; - } - } - if (newBlock >= 0) block = newBlock; - } - - // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; - } - - //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data); - - // Write the final value back to Java. - dest[STYLE_TYPE] = value.dataType; - dest[STYLE_DATA] = value.data; - dest[STYLE_ASSET_COOKIE] = reinterpret_cast(res.getTableCookie(block)); - dest[STYLE_RESOURCE_ID] = resid; - dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags; - dest[STYLE_DENSITY] = config.density; - dest += STYLE_NUM_ENTRIES; - i+= STYLE_NUM_ENTRIES; - arrayEnt++; - } - - i /= STYLE_NUM_ENTRIES; - - res.unlock(); - - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - - return i; +static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr, + jlong src_theme_ptr) { + Theme* dst_theme = reinterpret_cast(dst_theme_ptr); + Theme* src_theme = reinterpret_cast(src_theme_ptr); + if (!dst_theme->SetTo(*src_theme)) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "Themes are from different AssetManagers"); + } } -static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return 0; - } - - int32_t assetCookie = static_cast(cookie); - Asset* a = assetCookie - ? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER) - : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return 0; - } - - const DynamicRefTable* dynamicRefTable = - am->getResources().getDynamicRefTableForCookie(assetCookie); - ResXMLTree* block = new ResXMLTree(dynamicRefTable); - status_t err = block->setTo(a->getBuffer(true), a->getLength(), true); - a->close(); - delete a; - - if (err != NO_ERROR) { - jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); - return 0; - } - - return reinterpret_cast(block); +static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { + reinterpret_cast(theme_ptr)->Clear(); } -static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N * 2); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - for (size_t i = 0, j = 0; ((ssize_t)i)map.value; - - // Take care of resolving the found resource to its final value. - stringBlock = res.resolveReference(&value, bag->stringBlock, NULL); - if (value.dataType == Res_value::TYPE_STRING) { - stringIndex = value.data; - } - - if (kThrowOnBadId) { - if (stringBlock == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - - //todo: It might be faster to allocate a C array to contain - // the blocknums and indices, put them in there and then - // do just one SetIntArrayRegion() - env->SetIntArrayRegion(array, j, 1, &stringBlock); - env->SetIntArrayRegion(array, j + 1, 1, &stringIndex); - j = j + 2; - } - res.unlockBag(startOfBag); - return array; +static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint resid, jobject typed_value, + jboolean resolve_references) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie = theme->GetAttribute(static_cast(resid), &value, &flags); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t ref = 0u; + if (resolve_references) { + ResTable_config selected_config; + cookie = + theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + } + return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value); } -static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL); - if (env->ExceptionCheck()) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - size_t strLen = 0; - for (size_t i=0; ((ssize_t)i)map.value; - jstring str = NULL; - - // Take care of resolving the found resource to its final value. - ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - if (value.dataType == Res_value::TYPE_STRING) { - const ResStringPool* pool = res.getTableStringBlock(block); - const char* str8 = pool->string8At(value.data, &strLen); - if (str8 != NULL) { - str = env->NewStringUTF(str8); - } else { - const char16_t* str16 = pool->stringAt(value.data, &strLen); - str = env->NewString(reinterpret_cast(str16), - strLen); - } - - // If one of our NewString{UTF} calls failed due to memory, an - // exception will be pending. - if (env->ExceptionCheck()) { - res.unlockBag(startOfBag); - return NULL; - } - - env->SetObjectArrayElement(array, i, str); - - // str is not NULL at that point, otherwise ExceptionCheck would have been true. - // If we have a large amount of strings in our array, we might - // overflow the local reference table of the VM. - env->DeleteLocalRef(str); - } - } - res.unlockBag(startOfBag); - return array; +static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint priority, jstring tag, jstring prefix) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + (void) theme; + (void) priority; + (void) tag; + (void) prefix; } -static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - for (size_t i=0; ((ssize_t)i)map.value; - - // Take care of resolving the found resource to its final value. - ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - if (value.dataType >= Res_value::TYPE_FIRST_INT - && value.dataType <= Res_value::TYPE_LAST_INT) { - int intVal = value.data; - env->SetIntArrayRegion(array, i, 1, &intVal); - } - } - res.unlockBag(startOfBag); - return array; +static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/, + jlong theme_ptr) { + Theme* theme = reinterpret_cast(theme_ptr); + return static_cast(theme->GetChangingConfigurations()); } -static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz, - jint styleId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(styleId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } +static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + delete reinterpret_cast(asset_ptr); +} - const ResTable::bag_entry* bag = startOfBag; - for (size_t i=0; ((ssize_t)i)map.name.ident; - env->SetIntArrayRegion(array, i, 1, &resourceId); - } - res.unlockBag(startOfBag); - return array; +static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + uint8_t b; + ssize_t res = asset->read(&b, sizeof(b)); + return res == sizeof(b) ? static_cast(b) : -1; } -static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem) -{ - if (isSystem) { - verifySystemIdmaps(); - } - AssetManager* am = new AssetManager(); - if (am == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", ""); - return; - } +static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer, + jint offset, jint len) { + if (len == 0) { + return 0; + } - am->addDefaultAssets(); + jsize buffer_len = env->GetArrayLength(java_buffer); + if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len || + offset > buffer_len - len) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); + return -1; + } - ALOGV("Created AssetManager %p for Java object %p\n", am, clazz); - env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast(am)); -} + ScopedByteArrayRW byte_array(env, java_buffer); + if (byte_array.get() == nullptr) { + return -1; + } -static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz) -{ - AssetManager* am = (AssetManager*) - (env->GetLongField(clazz, gAssetManagerOffsets.mObject)); - ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz); - if (am != NULL) { - delete am; - env->SetLongField(clazz, gAssetManagerOffsets.mObject, 0); - } + Asset* asset = reinterpret_cast(asset_ptr); + ssize_t res = asset->read(byte_array.get() + offset, len); + if (res < 0) { + jniThrowException(env, "java/io/IOException", ""); + return -1; + } + return res > 0 ? static_cast(res) : -1; } -static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz) -{ - return Asset::getGlobalCount(); +static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset, + jint whence) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->seek( + static_cast(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)))); } -static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz) -{ - String8 alloc = Asset::getAssetAllocations(); - if (alloc.length() <= 0) { - return NULL; - } - - jstring str = env->NewStringUTF(alloc.string()); - return str; +static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->getLength()); } -static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz) -{ - return AssetManager::getGlobalCount(); +static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->getRemainingLength()); } // ---------------------------------------------------------------------------- -/* - * JNI registration. - */ +// JNI registration. static const JNINativeMethod gAssetManagerMethods[] = { - /* name, signature, funcPtr */ - - // Basic asset stuff. - { "openAsset", "(Ljava/lang/String;I)J", - (void*) android_content_AssetManager_openAsset }, - { "openAssetFd", "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*) android_content_AssetManager_openAssetFd }, - { "openNonAssetNative", "(ILjava/lang/String;I)J", - (void*) android_content_AssetManager_openNonAssetNative }, - { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*) android_content_AssetManager_openNonAssetFdNative }, - { "list", "(Ljava/lang/String;)[Ljava/lang/String;", - (void*) android_content_AssetManager_list }, - { "destroyAsset", "(J)V", - (void*) android_content_AssetManager_destroyAsset }, - { "readAssetChar", "(J)I", - (void*) android_content_AssetManager_readAssetChar }, - { "readAsset", "(J[BII)I", - (void*) android_content_AssetManager_readAsset }, - { "seekAsset", "(JJI)J", - (void*) android_content_AssetManager_seekAsset }, - { "getAssetLength", "(J)J", - (void*) android_content_AssetManager_getAssetLength }, - { "getAssetRemainingLength", "(J)J", - (void*) android_content_AssetManager_getAssetRemainingLength }, - { "addAssetPathNative", "(Ljava/lang/String;Z)I", - (void*) android_content_AssetManager_addAssetPath }, - { "addAssetFdNative", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)I", - (void*) android_content_AssetManager_addAssetFd }, - { "addOverlayPathNative", "(Ljava/lang/String;)I", - (void*) android_content_AssetManager_addOverlayPath }, - { "isUpToDate", "()Z", - (void*) android_content_AssetManager_isUpToDate }, - - // Resources. - { "getLocales", "()[Ljava/lang/String;", - (void*) android_content_AssetManager_getLocales }, - { "getNonSystemLocales", "()[Ljava/lang/String;", - (void*) android_content_AssetManager_getNonSystemLocales }, - { "getSizeConfigurations", "()[Landroid/content/res/Configuration;", - (void*) android_content_AssetManager_getSizeConfigurations }, - { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIIII)V", - (void*) android_content_AssetManager_setConfiguration }, - { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", - (void*) android_content_AssetManager_getResourceIdentifier }, - { "getResourceName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceName }, - { "getResourcePackageName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourcePackageName }, - { "getResourceTypeName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceTypeName }, - { "getResourceEntryName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceEntryName }, - { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadResourceValue }, - { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadResourceBagValue }, - { "getStringBlockCount","()I", - (void*) android_content_AssetManager_getStringBlockCount }, - { "getNativeStringBlock","(I)J", - (void*) android_content_AssetManager_getNativeStringBlock }, - { "getCookieName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getCookieName }, - { "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;", - (void*) android_content_AssetManager_getAssignedPackageIdentifiers }, - - // Themes. - { "newTheme", "()J", - (void*) android_content_AssetManager_newTheme }, - { "deleteTheme", "(J)V", - (void*) android_content_AssetManager_deleteTheme }, - { "applyThemeStyle", "(JIZ)V", - (void*) android_content_AssetManager_applyThemeStyle }, - { "copyTheme", "(JJ)V", - (void*) android_content_AssetManager_copyTheme }, - { "clearTheme", "(J)V", - (void*) android_content_AssetManager_clearTheme }, - { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadThemeAttributeValue }, - { "getThemeChangingConfigurations", "(J)I", - (void*) android_content_AssetManager_getThemeChangingConfigurations }, - { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V", - (void*) android_content_AssetManager_dumpTheme }, - { "applyStyle","(JIIJ[IIJJ)V", - (void*) android_content_AssetManager_applyStyle }, - { "resolveAttrs","(JII[I[I[I[I)Z", - (void*) android_content_AssetManager_resolveAttrs }, - { "retrieveAttributes","(J[I[I[I)Z", - (void*) android_content_AssetManager_retrieveAttributes }, - { "getArraySize","(I)I", - (void*) android_content_AssetManager_getArraySize }, - { "retrieveArray","(I[I)I", - (void*) android_content_AssetManager_retrieveArray }, - - // XML files. - { "openXmlAssetNative", "(ILjava/lang/String;)J", - (void*) android_content_AssetManager_openXmlAssetNative }, - - // Arrays. - { "getArrayStringResource","(I)[Ljava/lang/String;", - (void*) android_content_AssetManager_getArrayStringResource }, - { "getArrayStringInfo","(I)[I", - (void*) android_content_AssetManager_getArrayStringInfo }, - { "getArrayIntResource","(I)[I", - (void*) android_content_AssetManager_getArrayIntResource }, - { "getStyleAttributes","(I)[I", - (void*) android_content_AssetManager_getStyleAttributes }, - - // Bookkeeping. - { "init", "(Z)V", - (void*) android_content_AssetManager_init }, - { "destroy", "()V", - (void*) android_content_AssetManager_destroy }, - { "getGlobalAssetCount", "()I", - (void*) android_content_AssetManager_getGlobalAssetCount }, - { "getAssetAllocations", "()Ljava/lang/String;", - (void*) android_content_AssetManager_getAssetAllocations }, - { "getGlobalAssetManagerCount", "()I", - (void*) android_content_AssetManager_getGlobalAssetManagerCount }, + // AssetManager setup methods. + {"nativeCreate", "()J", (void*)NativeCreate}, + {"nativeDestroy", "(J)V", (void*)NativeDestroy}, + {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets}, + {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V", + (void*)NativeSetConfiguration}, + {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;", + (void*)NativeGetAssignedPackageIdentifiers}, + + // AssetManager file methods. + {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList}, + {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset}, + {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*)NativeOpenAssetFd}, + {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset}, + {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*)NativeOpenNonAssetFd}, + {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset}, + + // AssetManager resource methods. + {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue}, + {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I", + (void*)NativeGetResourceBagValue}, + {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes}, + {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;", + (void*)NativeGetResourceStringArray}, + {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo}, + {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray}, + {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize}, + {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray}, + + // AssetManager resource name/ID methods. + {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + (void*)NativeGetResourceIdentifier}, + {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName}, + {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName}, + {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName}, + {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName}, + {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales}, + {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;", + (void*)NativeGetSizeConfigurations}, + + // Style attribute related methods. + {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle}, + {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs}, + {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes}, + + // Theme related methods. + {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate}, + {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy}, + {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle}, + {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy}, + {"nativeThemeClear", "(J)V", (void*)NativeThemeClear}, + {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I", + (void*)NativeThemeGetAttributeValue}, + {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump}, + {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations}, + + // AssetInputStream methods. + {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy}, + {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar}, + {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead}, + {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek}, + {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength}, + {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength}, + + // System/idmap related methods. + {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps}, + + // Global management/debug methods. + {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount}, + {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations}, + {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount}, }; -int register_android_content_AssetManager(JNIEnv* env) -{ - jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); - gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); - gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); - gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string", - "Ljava/lang/CharSequence;"); - gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); - gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); - gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue, - "changingConfigurations", "I"); - gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); - - jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); - gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd", - "Landroid/os/ParcelFileDescriptor;"); - gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); - gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); - - jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); - gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); - - jclass stringClass = FindClassOrDie(env, "java/lang/String"); - g_stringClass = MakeGlobalRefOrDie(env, stringClass); - - jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); - gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); - gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, - "", "()V"); - gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", - "(ILjava/lang/Object;)V"); - - jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); - gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); - gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, - "", "()V"); - gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, - "smallestScreenWidthDp", "I"); - gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, - "screenWidthDp", "I"); - gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass, - "screenHeightDp", "I"); - - return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, - NELEM(gAssetManagerMethods)); +int register_android_content_AssetManager(JNIEnv* env) { + jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets"); + gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J"); + + jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); + gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); + gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); + gTypedValueOffsets.mString = + GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;"); + gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); + gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); + gTypedValueOffsets.mChangingConfigurations = + GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I"); + gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); + + jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); + gAssetFileDescriptorOffsets.mFd = + GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;"); + gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); + gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); + + jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); + gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); + + jclass stringClass = FindClassOrDie(env, "java/lang/String"); + g_stringClass = MakeGlobalRefOrDie(env, stringClass); + + jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); + gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); + gSparseArrayOffsets.constructor = + GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "", "()V"); + gSparseArrayOffsets.put = + GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V"); + + jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); + gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); + gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "", "()V"); + gConfigurationOffsets.mSmallestScreenWidthDpOffset = + GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I"); + gConfigurationOffsets.mScreenWidthDpOffset = + GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I"); + gConfigurationOffsets.mScreenHeightDpOffset = + GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I"); + + return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, + NELEM(gAssetManagerMethods)); } }; // namespace android diff --git a/core/jni/include/android_runtime/android_util_AssetManager.h b/core/jni/include/android_runtime/android_util_AssetManager.h index 8dd933707a6a..2c1e3579eb92 100644 --- a/core/jni/include/android_runtime/android_util_AssetManager.h +++ b/core/jni/include/android_runtime/android_util_AssetManager.h @@ -14,17 +14,20 @@ * limitations under the License. */ -#ifndef android_util_AssetManager_H -#define android_util_AssetManager_H +#ifndef ANDROID_RUNTIME_ASSETMANAGER_H +#define ANDROID_RUNTIME_ASSETMANAGER_H -#include +#include "androidfw/AssetManager2.h" +#include "androidfw/MutexGuard.h" #include "jni.h" namespace android { -extern AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject assetMgr); +extern AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); +extern Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); +extern Guarded* AssetManagerForNdkAssetManager(AAssetManager* assetmanager); -} +} // namespace android -#endif +#endif // ANDROID_RUNTIME_ASSETMANAGER_H diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 415d3e36adf9..2fc8e952707b 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -265,8 +265,6 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, FindEntryResult* out_entry) { - ATRACE_CALL(); - // Might use this if density_override != 0. ResTable_config density_override_config; @@ -429,9 +427,7 @@ ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_valu for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && in_out_value->data != 0u && iteration < kMaxIterations; iteration++) { - if (out_last_reference != nullptr) { - *out_last_reference = in_out_value->data; - } + *out_last_reference = in_out_value->data; uint32_t new_flags = 0u; cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/, in_out_value, in_out_selected_config, &new_flags); diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index 60e3845d98a9..f912af4f7190 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -20,13 +20,18 @@ #include +#include "androidfw/AssetManager2.h" #include "androidfw/AttributeFinder.h" -#include "androidfw/ResourceTypes.h" constexpr bool kDebugStyles = false; namespace android { +// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. +static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { + return cookie != kInvalidCookie ? static_cast(cookie + 1) : static_cast(-1); +} + class XmlAttributeFinder : public BackTrackingAttributeFinder { public: @@ -44,58 +49,53 @@ class XmlAttributeFinder }; class BagAttributeFinder - : public BackTrackingAttributeFinder { + : public BackTrackingAttributeFinder { public: - BagAttributeFinder(const ResTable::bag_entry* start, - const ResTable::bag_entry* end) - : BackTrackingAttributeFinder(start, end) {} + BagAttributeFinder(const ResolvedBag* bag) + : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr, + bag != nullptr ? bag->entries + bag->entry_count : nullptr) { + } - inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const { - return entry->map.name.ident; + inline uint32_t GetAttribute(const ResolvedBag::Entry* entry) const { + return entry->key; } }; -bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, - uint32_t def_style_res, uint32_t* src_values, - size_t src_values_length, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, - uint32_t* out_indices) { +bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, + uint32_t* src_values, size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr, def_style_res); } - const ResTable& res = theme->getResTable(); + AssetManager2* assetmanager = theme->GetAssetManager(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_bag_type_set_flags = 0; + uint32_t def_style_flags = 0u; if (def_style_attr != 0) { Res_value value; - if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) { + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { if (value.dataType == Res_value::TYPE_REFERENCE) { def_style_res = value.data; } } } - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - // Retrieve the default style bag, if requested. - const ResTable::bag_entry* def_style_start = nullptr; - uint32_t def_style_type_set_flags = 0; - ssize_t bag_off = def_style_res != 0 - ? res.getBagLocked(def_style_res, &def_style_start, - &def_style_type_set_flags) - : -1; - def_style_type_set_flags |= def_style_bag_type_set_flags; - const ResTable::bag_entry* const def_style_end = - def_style_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end); + const ResolvedBag* default_style_bag = nullptr; + if (def_style_res != 0) { + default_style_bag = assetmanager->GetBag(def_style_res); + if (default_style_bag != nullptr) { + def_style_flags |= default_style_bag->type_spec_flags; + } + } + + BagAttributeFinder def_style_attr_finder(default_style_bag); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -106,7 +106,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ssize_t block = -1; + ApkAssetsCookie cookie = kInvalidCookie; uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; @@ -122,15 +122,14 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, value.dataType = Res_value::TYPE_ATTRIBUTE; value.data = src_values[ii]; if (kDebugStyles) { - ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, - value.data); + ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data); } } else { - const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident); - if (def_style_entry != def_style_end) { - block = def_style_entry->stringBlock; - type_set_flags = def_style_type_set_flags; - value = def_style_entry->map.value; + const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident); + if (entry != def_style_attr_finder.end()) { + cookie = entry->cookie; + type_set_flags = def_style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -140,22 +139,26 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ssize_t new_block = - theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); - if (new_block >= 0) block = new_block; + ApkAssetsCookie new_cookie = + theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } if (kDebugStyles) { ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data); } } else if (value.data != Res_value::DATA_NULL_EMPTY) { - // If we still don't have a value for this attribute, try to find - // it in the theme! - ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); - if (new_block >= 0) { + // If we still don't have a value for this attribute, try to find it in the theme! + ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); + if (new_cookie != kInvalidCookie) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); - if (new_block >= 0) block = new_block; + new_cookie = + assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } if (kDebugStyles) { ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -169,7 +172,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = -1; + cookie = kInvalidCookie; } if (kDebugStyles) { @@ -179,9 +182,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != -1 ? static_cast(res.getTableCookie(block)) - : static_cast(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -195,90 +196,80 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, out_values += STYLE_NUM_ENTRIES; } - res.unlock(); - if (out_indices != nullptr) { out_indices[0] = indices_idx; } return true; } -void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { - ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", - theme, def_style_attr, def_style_res, xml_parser); + ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, + def_style_attr, def_style_resid, xml_parser); } - const ResTable& res = theme->getResTable(); + AssetManager2* assetmanager = theme->GetAssetManager(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_bag_type_set_flags = 0; + uint32_t def_style_flags = 0u; if (def_style_attr != 0) { Res_value value; - if (theme->getAttribute(def_style_attr, &value, - &def_style_bag_type_set_flags) >= 0) { + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { if (value.dataType == Res_value::TYPE_REFERENCE) { - def_style_res = value.data; + def_style_resid = value.data; } } } - // Retrieve the style class associated with the current XML tag. - int style = 0; - uint32_t style_bag_type_set_flags = 0; + // Retrieve the style resource ID associated with the current XML tag's style attribute. + uint32_t style_resid = 0u; + uint32_t style_flags = 0u; if (xml_parser != nullptr) { ssize_t idx = xml_parser->indexOfStyle(); if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) { if (value.dataType == value.TYPE_ATTRIBUTE) { - if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) { + // Resolve the attribute with out theme. + if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) { value.dataType = Res_value::TYPE_NULL; } } + if (value.dataType == value.TYPE_REFERENCE) { - style = value.data; + style_resid = value.data; } } } - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - // Retrieve the default style bag, if requested. - const ResTable::bag_entry* def_style_attr_start = nullptr; - uint32_t def_style_type_set_flags = 0; - ssize_t bag_off = def_style_res != 0 - ? res.getBagLocked(def_style_res, &def_style_attr_start, - &def_style_type_set_flags) - : -1; - def_style_type_set_flags |= def_style_bag_type_set_flags; - const ResTable::bag_entry* const def_style_attr_end = - def_style_attr_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder def_style_attr_finder(def_style_attr_start, - def_style_attr_end); + const ResolvedBag* default_style_bag = nullptr; + if (def_style_resid != 0) { + default_style_bag = assetmanager->GetBag(def_style_resid); + if (default_style_bag != nullptr) { + def_style_flags |= default_style_bag->type_spec_flags; + } + } + + BagAttributeFinder def_style_attr_finder(default_style_bag); // Retrieve the style class bag, if requested. - const ResTable::bag_entry* style_attr_start = nullptr; - uint32_t style_type_set_flags = 0; - bag_off = - style != 0 - ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags) - : -1; - style_type_set_flags |= style_bag_type_set_flags; - const ResTable::bag_entry* const style_attr_end = - style_attr_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end); + const ResolvedBag* xml_style_bag = nullptr; + if (style_resid != 0) { + xml_style_bag = assetmanager->GetBag(style_resid); + if (xml_style_bag != nullptr) { + style_flags |= xml_style_bag->type_spec_flags; + } + } + + BagAttributeFinder xml_style_attr_finder(xml_style_bag); // Retrieve the XML attributes, if requested. - static const ssize_t kXmlBlock = 0x10000000; XmlAttributeFinder xml_attr_finder(xml_parser); - const size_t xml_attr_end = - xml_parser != nullptr ? xml_parser->getAttributeCount() : 0; // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -289,8 +280,8 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ssize_t block = kXmlBlock; - uint32_t type_set_flags = 0; + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0u; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -302,7 +293,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s // Walk through the xml attributes looking for the requested attribute. const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); - if (xml_attr_idx != xml_attr_end) { + if (xml_attr_idx != xml_attr_finder.end()) { // We found the attribute we were looking for. xml_parser->getAttributeValue(xml_attr_idx, &value); if (kDebugStyles) { @@ -312,12 +303,12 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the style class values looking for the requested attribute. - const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident); - if (style_attr_entry != style_attr_end) { + const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident); + if (entry != xml_style_attr_finder.end()) { // We found the attribute we were looking for. - block = style_attr_entry->stringBlock; - type_set_flags = style_type_set_flags; - value = style_attr_entry->map.value; + cookie = entry->cookie; + type_set_flags = style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -326,25 +317,25 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the default style values looking for the requested attribute. - const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident); - if (def_style_attr_entry != def_style_attr_end) { + const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident); + if (entry != def_style_attr_finder.end()) { // We found the attribute we were looking for. - block = def_style_attr_entry->stringBlock; - type_set_flags = style_type_set_flags; - value = def_style_attr_entry->map.value; + cookie = entry->cookie; + type_set_flags = def_style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } } } - uint32_t resid = 0; + uint32_t resid = 0u; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ssize_t new_block = - theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); - if (new_block >= 0) { - block = new_block; + ApkAssetsCookie new_cookie = + theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; } if (kDebugStyles) { @@ -352,14 +343,15 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s } } else if (value.data != Res_value::DATA_NULL_EMPTY) { // If we still don't have a value for this attribute, try to find it in the theme! - ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); - if (new_block >= 0) { + ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); + if (new_cookie != kInvalidCookie) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); - if (new_block >= 0) { - block = new_block; + new_cookie = + assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; } if (kDebugStyles) { @@ -375,7 +367,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = kXmlBlock; + cookie = kInvalidCookie; } if (kDebugStyles) { @@ -385,9 +377,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != kXmlBlock ? static_cast(res.getTableCookie(block)) - : static_cast(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -402,36 +392,28 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s out_values += STYLE_NUM_ENTRIES; } - res.unlock(); - // out_indices must NOT be nullptr. out_indices[0] = indices_idx; } -bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, - uint32_t* attrs, size_t attrs_length, - uint32_t* out_values, uint32_t* out_indices) { +bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { ResTable_config config; Res_value value; int indices_idx = 0; - // Now lock down the resource object and start pulling stuff from it. - res->lock(); - // Retrieve the XML attributes, if requested. const size_t xml_attr_count = xml_parser->getAttributeCount(); size_t ix = 0; uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix); - static const ssize_t kXmlBlock = 0x10000000; - // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - ssize_t block = kXmlBlock; - uint32_t type_set_flags = 0; + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0u; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -450,28 +432,27 @@ bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, cur_xml_attr = xml_parser->getAttributeNameResID(ix); } - uint32_t resid = 0; + uint32_t resid = 0u; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - // printf("Resolving attribute reference\n"); - ssize_t new_block = res->resolveReference(&value, block, &resid, - &type_set_flags, &config); - if (new_block >= 0) block = new_block; + ApkAssetsCookie new_cookie = + assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } } // Deal with the special @null value -- it turns back to TYPE_NULL. if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = kXmlBlock; + cookie = kInvalidCookie; } // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != kXmlBlock ? static_cast(res->getTableCookie(block)) - : static_cast(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -485,8 +466,6 @@ bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, out_values += STYLE_NUM_ENTRIES; } - res->unlock(); - if (out_indices != nullptr) { out_indices[0] = indices_idx; } diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 28548e27baf0..e08848f891f6 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -324,8 +324,6 @@ bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_i bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, FindEntryResult* out_entry) const { - ATRACE_CALL(); - // If the type IDs are offset in this package, we need to take that into account when searching // for a type. const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h index f281921824e7..03fad4947dfe 100644 --- a/libs/androidfw/include/androidfw/AttributeFinder.h +++ b/libs/androidfw/include/androidfw/AttributeFinder.h @@ -58,6 +58,7 @@ class BackTrackingAttributeFinder { BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end); Iterator Find(uint32_t attr); + inline Iterator end(); private: void JumpToClosestAttribute(uint32_t package_id); @@ -201,6 +202,11 @@ Iterator BackTrackingAttributeFinder::Find(uint32_t attr) { return end_; } +template +Iterator BackTrackingAttributeFinder::end() { + return end_; +} + } // namespace android #endif // ANDROIDFW_ATTRIBUTE_FINDER_H diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h index 69b760414846..35ef98d8c704 100644 --- a/libs/androidfw/include/androidfw/AttributeResolution.h +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -17,7 +17,8 @@ #ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H #define ANDROIDFW_ATTRIBUTERESOLUTION_H -#include +#include "androidfw/AssetManager2.h" +#include "androidfw/ResourceTypes.h" namespace android { @@ -42,19 +43,19 @@ enum { // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, +bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid, uint32_t* src_values, size_t src_values_length, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` is NOT optional and must NOT be nullptr. -void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs, +bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); } // namespace android diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 965e2dbd2fb2..1775f5070f4e 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -45,16 +45,17 @@ struct FindEntryResult { // A pointer to the resource table entry for this resource. // If the size of the entry is > sizeof(ResTable_entry), it can be cast to // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry = nullptr; + const ResTable_entry* entry; - // The configuration for which the resulting entry was defined. - const ResTable_config* config = nullptr; + // The configuration for which the resulting entry was defined. This points to a structure that + // is already swapped to host endianness. + const ResTable_config* config; - // Stores the resulting bitmask of configuration axis with which the resource value varies. - uint32_t type_flags = 0u; + // The bitmask of configuration axis with which the resource value varies. + uint32_t type_flags; // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table = nullptr; + const DynamicRefTable* dynamic_ref_table; // The string pool reference to the type's name. This uses a different string pool than // the global string pool, but this is hidden from the caller. diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h new file mode 100644 index 000000000000..64924f433245 --- /dev/null +++ b/libs/androidfw/include/androidfw/MutexGuard.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROIDFW_MUTEXGUARD_H +#define ANDROIDFW_MUTEXGUARD_H + +#include +#include + +#include "android-base/macros.h" + +namespace android { + +template +class ScopedLock; + +// Owns the guarded object and protects access to it via a mutex. +// The guarded object is inaccessible via this class. +// The mutex is locked and the object accessed via the ScopedLock class. +// +// NOTE: The template parameter T should not be a raw pointer, since ownership +// is ambiguous and error-prone. Instead use an std::unique_ptr<>. +// +// Example use: +// +// Guarded shared_string("hello"); +// { +// ScopedLock locked_string(shared_string); +// *locked_string += " world"; +// } +// +template +class Guarded { + static_assert(!std::is_pointer::value, "T must not be a raw pointer"); + + public: + explicit Guarded() : guarded_() { + } + + template + explicit Guarded(const T& guarded, + typename std::enable_if::value>::type = void()) + : guarded_(guarded) { + } + + template + explicit Guarded(T&& guarded, + typename std::enable_if::value>::type = void()) + : guarded_(std::move(guarded)) { + } + + private: + friend class ScopedLock; + + DISALLOW_COPY_AND_ASSIGN(Guarded); + + std::mutex lock_; + T guarded_; +}; + +template +class ScopedLock { + public: + explicit ScopedLock(Guarded& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) { + } + + T& operator*() { + return guarded_; + } + + T* operator->() { + return &guarded_; + } + + T* get() { + return &guarded_; + } + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedLock); + + std::lock_guard lock_; + T& guarded_; +}; + +} // namespace android + +#endif // ANDROIDFW_MUTEXGUARD_H diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index 2d73ce8f8ee3..cc3053798e7b 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -21,6 +21,7 @@ #include "android-base/file.h" #include "android-base/logging.h" #include "android-base/macros.h" +#include "androidfw/AssetManager2.h" #include "TestHelpers.h" #include "data/styles/R.h" @@ -32,15 +33,14 @@ namespace android { class AttributeResolutionTest : public ::testing::Test { public: virtual void SetUp() override { - std::string contents; - ASSERT_TRUE(ReadFileFromZipToString( - GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, table_.add(contents.data(), contents.size(), - 1 /*cookie*/, true /*copyData*/)); + styles_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + ASSERT_NE(nullptr, styles_assets_); + assetmanager_.SetApkAssets({styles_assets_.get()}); } protected: - ResTable table_; + std::unique_ptr styles_assets_; + AssetManager2 assetmanager_; }; class AttributeResolutionXmlTest : public AttributeResolutionTest { @@ -48,13 +48,12 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { virtual void SetUp() override { AttributeResolutionTest::SetUp(); - std::string contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", - "res/layout/layout.xml", &contents)); + std::unique_ptr asset = + assetmanager_.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); + ASSERT_NE(nullptr, asset); - ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(), - true /*copyData*/)); + ASSERT_EQ(NO_ERROR, + xml_parser_.setTo(asset->getBuffer(true), asset->getLength(), true /*copyData*/)); // Skip to the first tag. while (xml_parser_.next() != ResXMLParser::START_TAG) { @@ -66,14 +65,14 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { }; TEST_F(AttributeResolutionTest, Theme) { - ResTable::Theme theme(table_); - ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); + std::unique_ptr theme = assetmanager_.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_empty}}; std::array values; - ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/, + ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/, nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(), attrs.size(), values.data(), nullptr /*out_indices*/)); @@ -126,8 +125,8 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { R::attr::attr_four, R::attr::attr_empty}}; std::array values; - ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(), - nullptr /*out_indices*/)); + ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(), + values.data(), nullptr /*out_indices*/)); uint32_t* values_cursor = values.data(); EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]); @@ -171,15 +170,15 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { } TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) { - ResTable::Theme theme(table_); - ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); + std::unique_ptr theme = assetmanager_.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}}; std::array values; std::array indices; - ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(), + ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), attrs.size(), values.data(), indices.data()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index 7149beef797f..a8abcb5df86c 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -33,12 +33,12 @@ void GetResourceBenchmarkOld(const std::vector& paths, const ResTab } } + // Make sure to force creation of the ResTable first, or else the configuration doesn't get set. + const ResTable& table = assetmanager.getResources(true); if (config != nullptr) { assetmanager.setConfiguration(*config); } - const ResTable& table = assetmanager.getResources(true); - Res_value value; ResTable_config selected_config; uint32_t flags; diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp index 98e9a42d944d..e70d5ea0d566 100644 --- a/native/android/asset_manager.cpp +++ b/native/android/asset_manager.cpp @@ -18,9 +18,11 @@ #include #include +#include #include #include #include +#include #include #include "jni.h" @@ -35,21 +37,20 @@ using namespace android; // ----- struct AAssetDir { - AssetDir* mAssetDir; + std::unique_ptr mAssetDir; size_t mCurFileIndex; String8 mCachedFileName; - explicit AAssetDir(AssetDir* dir) : mAssetDir(dir), mCurFileIndex(0) { } - ~AAssetDir() { delete mAssetDir; } + explicit AAssetDir(std::unique_ptr dir) : + mAssetDir(std::move(dir)), mCurFileIndex(0) { } }; // ----- struct AAsset { - Asset* mAsset; + std::unique_ptr mAsset; - explicit AAsset(Asset* asset) : mAsset(asset) { } - ~AAsset() { delete mAsset; } + explicit AAsset(std::unique_ptr asset) : mAsset(std::move(asset)) { } }; // -------------------- Public native C API -------------------- @@ -104,19 +105,18 @@ AAsset* AAssetManager_open(AAssetManager* amgr, const char* filename, int mode) return NULL; } - AssetManager* mgr = static_cast(amgr); - Asset* asset = mgr->open(filename, amMode); - if (asset == NULL) { - return NULL; + ScopedLock locked_mgr(*AssetManagerForNdkAssetManager(amgr)); + std::unique_ptr asset = locked_mgr->Open(filename, amMode); + if (asset == nullptr) { + return nullptr; } - - return new AAsset(asset); + return new AAsset(std::move(asset)); } AAssetDir* AAssetManager_openDir(AAssetManager* amgr, const char* dirName) { - AssetManager* mgr = static_cast(amgr); - return new AAssetDir(mgr->openDir(dirName)); + ScopedLock locked_mgr(*AssetManagerForNdkAssetManager(amgr)); + return new AAssetDir(locked_mgr->OpenDir(dirName)); } /** diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index b32be736533b..52d0e08e4e7f 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -24,8 +24,9 @@ #include #include +#include #include -#include +#include #include #include @@ -1664,18 +1665,22 @@ nFileA3DCreateFromAssetStream(JNIEnv *_env, jobject _this, jlong con, jlong nati static jlong nFileA3DCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path) { - AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); + Guarded* mgr = AssetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); - if (asset == nullptr) { - return 0; + std::unique_ptr asset; + { + ScopedLock locked_mgr(*mgr); + asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; + } } - jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset); + jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset.release()); return id; } @@ -1752,22 +1757,25 @@ static jlong nFontCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path, jfloat fontSize, jint dpi) { - AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); + Guarded* mgr = AssetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); - if (asset == nullptr) { - return 0; + std::unique_ptr asset; + { + ScopedLock locked_mgr(*mgr); + asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; + } } jlong id = (jlong)(uintptr_t)rsFontCreateFromMemory((RsContext)con, str.c_str(), str.length(), fontSize, dpi, asset->getBuffer(false), asset->getLength()); - delete asset; return id; } -- cgit v1.2.3-59-g8ed1b From 64ee69d0f1f412edee2eb7a0c846deebbfa37ef9 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 8 Jan 2018 17:38:30 -0800 Subject: libandroidfw: Improve performance of AssetManager2 AssetManager2 relied on creating a list of configurations present in the resource table so as to avoid copying and converting ResTable_config's from the APK on every resource retrieval. ResTable, however, had a better optimization that pruned the configurations that didn't match the currently set configuration. This vastly reduced the number of ResTable_configs to test. In this CL, AssetManager2 follows suite with this optimization and only maintains the filtered ResTable_configs, falling back to the slow path when the configuration is overridden. Test: mma frameworks/base/libs/androidfw Test: adb sync system data Test: adb shell /data/benchmarktest64/libandroidfw_benchmarks/libandroidfw_benchmarks Change-Id: I5d46f8b005a37b72750d00bd75f090e7b5a36f60 --- libs/androidfw/Android.bp | 1 + libs/androidfw/AssetManager2.cpp | 273 +++++++++++++++++------ libs/androidfw/LoadedArsc.cpp | 261 +++++++--------------- libs/androidfw/include/androidfw/AssetManager2.h | 66 ++++-- libs/androidfw/include/androidfw/LoadedArsc.h | 111 +++++---- libs/androidfw/tests/ApkAssets_test.cpp | 78 +++---- libs/androidfw/tests/LoadedArsc_test.cpp | 169 +++++++------- libs/androidfw/tests/TestHelpers.h | 1 + 8 files changed, 538 insertions(+), 422 deletions(-) (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 7c9078b164a2..70d52164ff74 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -145,6 +145,7 @@ cc_test { "tests/TypeWrappers_test.cpp", "tests/ZipUtils_test.cpp", ], + static_libs: ["libgmock"], target: { android: { srcs: [ diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 2fc8e952707b..a558ff7ccfc1 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -36,6 +36,31 @@ namespace android { +struct FindEntryResult { + // A pointer to the resource table entry for this resource. + // If the size of the entry is > sizeof(ResTable_entry), it can be cast to + // a ResTable_map_entry and processed as a bag/map. + const ResTable_entry* entry; + + // The configuration for which the resulting entry was defined. This is already swapped to host + // endianness. + ResTable_config config; + + // The bitmask of configuration axis with which the resource value varies. + uint32_t type_flags; + + // The dynamic package ID map for the package from which this resource came from. + const DynamicRefTable* dynamic_ref_table; + + // The string pool reference to the type's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef type_string_ref; + + // The string pool reference to the entry's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef entry_string_ref; +}; + AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } @@ -44,6 +69,7 @@ bool AssetManager2::SetApkAssets(const std::vector& apk_assets bool invalidate_caches) { apk_assets_ = apk_assets; BuildDynamicRefTable(); + RebuildFilterList(); if (invalidate_caches) { InvalidateCaches(static_cast(-1)); } @@ -79,7 +105,7 @@ void AssetManager2::BuildDynamicRefTable() { PackageGroup* package_group = &package_groups_[idx]; // Add the package and to the set of packages with the same ID. - package_group->packages_.push_back(package.get()); + package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); package_group->cookies_.push_back(static_cast(i)); // Add the package name -> build time ID mappings. @@ -94,7 +120,7 @@ void AssetManager2::BuildDynamicRefTable() { // Now assign the runtime IDs so that we have a build-time to runtime ID map. const auto package_groups_end = package_groups_.end(); for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) { - const std::string& package_name = iter->packages_[0]->GetPackageName(); + const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName(); for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) { iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()), iter->dynamic_ref_table.mAssignedPackageId); @@ -108,17 +134,20 @@ void AssetManager2::DumpToLog() const { std::string list; for (size_t i = 0; i < package_ids_.size(); i++) { if (package_ids_[i] != 0xff) { - base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]); + base::StringAppendF(&list, "%02x -> %d, ", (int)i, package_ids_[i]); } } LOG(INFO) << "Package ID map: " << list; - for (const auto& package_group: package_groups_) { - list = ""; - for (const auto& package : package_group.packages_) { - base::StringAppendF(&list, "%s(%02x), ", package->GetPackageName().c_str(), package->GetPackageId()); - } - LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list; + for (const auto& package_group : package_groups_) { + list = ""; + for (const auto& package : package_group.packages_) { + base::StringAppendF(&list, "%s(%02x), ", package.loaded_package_->GetPackageName().c_str(), + package.loaded_package_->GetPackageId()); + } + LOG(INFO) << base::StringPrintf("PG (%02x): ", + package_group.dynamic_ref_table.mAssignedPackageId) + << list; } } @@ -157,52 +186,54 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { configuration_ = configuration; if (diff) { + RebuildFilterList(); InvalidateCaches(static_cast(diff)); } } std::set AssetManager2::GetResourceConfigurations(bool exclude_system, - bool exclude_mipmap) { + bool exclude_mipmap) const { ATRACE_CALL(); std::set configurations; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { - if (exclude_system && package->IsSystem()) { + for (const ConfiguredPackage& package : package_group.packages_) { + if (exclude_system && package.loaded_package_->IsSystem()) { continue; } - package->CollectConfigurations(exclude_mipmap, &configurations); + package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations); } } return configurations; } std::set AssetManager2::GetResourceLocales(bool exclude_system, - bool merge_equivalent_languages) { + bool merge_equivalent_languages) const { ATRACE_CALL(); std::set locales; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { - if (exclude_system && package->IsSystem()) { + for (const ConfiguredPackage& package : package_group.packages_) { + if (exclude_system && package.loaded_package_->IsSystem()) { continue; } - package->CollectLocales(merge_equivalent_languages, &locales); + package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales); } } return locales; } -std::unique_ptr AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) { +std::unique_ptr AssetManager2::Open(const std::string& filename, + Asset::AccessMode mode) const { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, mode); } std::unique_ptr AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) { + Asset::AccessMode mode) const { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, cookie, mode); } -std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) { +std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) const { ATRACE_CALL(); std::string full_path = "assets/" + dirname; @@ -236,7 +267,7 @@ std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) { // is inconsistent for split APKs. std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie) { + ApkAssetsCookie* out_cookie) const { ATRACE_CALL(); for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) { std::unique_ptr asset = apk_assets_[i]->Open(filename, mode); @@ -255,7 +286,8 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, - ApkAssetsCookie cookie, Asset::AccessMode mode) { + ApkAssetsCookie cookie, + Asset::AccessMode mode) const { ATRACE_CALL(); if (cookie < 0 || static_cast(cookie) >= apk_assets_.size()) { return {}; @@ -264,12 +296,13 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, - bool stop_at_first_match, FindEntryResult* out_entry) { + bool /*stop_at_first_match*/, + FindEntryResult* out_entry) const { // Might use this if density_override != 0. ResTable_config density_override_config; // Select our configuration or generate a density override configuration. - ResTable_config* desired_config = &configuration_; + const ResTable_config* desired_config = &configuration_; if (density_override != 0 && density_override != configuration_.density) { density_override_config = configuration_; density_override_config.density = density_override; @@ -283,53 +316,135 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri const uint32_t package_id = get_package_id(resid); const uint8_t type_idx = get_type_id(resid) - 1; - const uint16_t entry_id = get_entry_id(resid); + const uint16_t entry_idx = get_entry_id(resid); - const uint8_t idx = package_ids_[package_id]; - if (idx == 0xff) { + const uint8_t package_idx = package_ids_[package_id]; + if (package_idx == 0xff) { LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); return kInvalidCookie; } - FindEntryResult best_entry; - ApkAssetsCookie best_cookie = kInvalidCookie; - uint32_t cumulated_flags = 0u; - - const PackageGroup& package_group = package_groups_[idx]; + const PackageGroup& package_group = package_groups_[package_idx]; const size_t package_count = package_group.packages_.size(); - FindEntryResult current_entry; - for (size_t i = 0; i < package_count; i++) { - const LoadedPackage* loaded_package = package_group.packages_[i]; - if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry)) { + + ApkAssetsCookie best_cookie = kInvalidCookie; + const LoadedPackage* best_package = nullptr; + const ResTable_type* best_type = nullptr; + const ResTable_config* best_config = nullptr; + ResTable_config best_config_copy; + uint32_t best_offset = 0u; + uint32_t type_flags = 0u; + + // If desired_config is the same as the set configuration, then we can use our filtered list + // and we don't need to match the configurations, since they already matched. + const bool use_fast_path = desired_config == &configuration_; + + for (size_t pi = 0; pi < package_count; pi++) { + const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; + const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_; + ApkAssetsCookie cookie = package_group.cookies_[pi]; + + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx); + if (UNLIKELY(type_spec == nullptr)) { continue; } - cumulated_flags |= current_entry.type_flags; + uint16_t local_entry_idx = entry_idx; - const ResTable_config* current_config = current_entry.config; - const ResTable_config* best_config = best_entry.config; - if (best_cookie == kInvalidCookie || - current_config->isBetterThan(*best_config, desired_config) || - (loaded_package->IsOverlay() && current_config->compare(*best_config) == 0)) { - best_entry = current_entry; - best_cookie = package_group.cookies_[i]; - if (stop_at_first_match) { - break; + // If there is an IDMAP supplied with this package, translate the entry ID. + if (type_spec->idmap_entries != nullptr) { + if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) { + // There is no mapping, so the resource is not meant to be in this overlay package. + continue; + } + } + + type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx); + + // If the package is an overlay, then even configurations that are the same MUST be chosen. + const bool package_is_overlay = loaded_package->IsOverlay(); + + const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; + if (use_fast_path) { + const std::vector& candidate_configs = filtered_group.configurations; + const size_t type_count = candidate_configs.size(); + for (uint32_t i = 0; i < type_count; i++) { + const ResTable_config& this_config = candidate_configs[i]; + + // We can skip calling ResTable_config::match() because we know that all candidate + // configurations that do NOT match have been filtered-out. + if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || + (package_is_overlay && this_config.compare(*best_config) == 0)) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const ResTable_type* type_chunk = filtered_group.types[i]; + const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + best_cookie = cookie; + best_package = loaded_package; + best_type = type_chunk; + best_config = &this_config; + best_offset = offset; + } + } + } else { + // This is the slower path, which doesn't use the filtered list of configurations. + // Here we must read the ResTable_config from the mmapped APK, convert it to host endianness + // and fill in any new fields that did not exist when the APK was compiled. + // Furthermore when selecting configurations we can't just record the pointer to the + // ResTable_config, we must copy it. + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config this_config; + this_config.copyFromDtoH((*iter)->config); + + if (this_config.match(*desired_config)) { + if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || + (package_is_overlay && this_config.compare(*best_config) == 0)) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + best_cookie = cookie; + best_package = loaded_package; + best_type = *iter; + best_config_copy = this_config; + best_config = &best_config_copy; + best_offset = offset; + } + } } } } - if (best_cookie == kInvalidCookie) { + if (UNLIKELY(best_cookie == kInvalidCookie)) { return kInvalidCookie; } - *out_entry = best_entry; + const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset); + if (UNLIKELY(best_entry == nullptr)) { + return kInvalidCookie; + } + + out_entry->entry = best_entry; + out_entry->config = *best_config; + out_entry->type_flags = type_flags; + out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); + out_entry->entry_string_ref = + StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; - out_entry->type_flags = cumulated_flags; return best_cookie; } -bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { +bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const { ATRACE_CALL(); FindEntryResult entry; @@ -339,7 +454,8 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { return false; } - const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid); + const LoadedPackage* package = + apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); if (package == nullptr) { return false; } @@ -367,7 +483,7 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { return true; } -bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { +bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const { FindEntryResult entry; ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); @@ -381,7 +497,7 @@ bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) { + uint32_t* out_flags) const { ATRACE_CALL(); FindEntryResult entry; @@ -400,7 +516,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Create a reference since we can't represent this complex type as a Res_value. out_value->dataType = Res_value::TYPE_REFERENCE; out_value->data = resid; - *out_selected_config = *entry.config; + *out_selected_config = entry.config; *out_flags = entry.type_flags; return cookie; } @@ -412,7 +528,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Convert the package ID to the runtime assigned package ID. entry.dynamic_ref_table->lookupResourceValue(out_value); - *out_selected_config = *entry.config; + *out_selected_config = entry.config; *out_flags = entry.type_flags; return cookie; } @@ -420,7 +536,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) { + uint32_t* out_last_reference) const { ATRACE_CALL(); constexpr const int kMaxIterations = 20; @@ -488,7 +604,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { // Attributes, arrays, etc don't have a resource id as the name. They specify // other data, which would be wrong to change via a lookup. if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, + resid); return nullptr; } } @@ -520,7 +637,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { const ResolvedBag* parent_bag = GetBag(parent_resid); if (parent_bag == nullptr) { // Failed to get the parent that should exist. - LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid); + LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, + resid); return nullptr; } @@ -539,7 +657,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t child_key = dtohl(map_entry->name.ident); if (!is_internal_resid(child_key)) { if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, + resid); return nullptr; } } @@ -578,7 +697,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t new_key = dtohl(map_entry->name.ident); if (!is_internal_resid(new_key)) { if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, + resid); return nullptr; } } @@ -634,7 +754,7 @@ static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) { uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const std::string& fallback_type, - const std::string& fallback_package) { + const std::string& fallback_package) const { StringPiece package_name, type, entry; if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) { return 0u; @@ -666,7 +786,8 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const static std::u16string kAttrPrivate16 = u"^attr-private"; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { + for (const ConfiguredPackage& package_impl : package_group.packages_) { + const LoadedPackage* package = package_impl.loaded_package_; if (package_name != package->GetPackageName()) { // All packages in the same group are expected to have the same package name. break; @@ -688,6 +809,32 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, return 0u; } +void AssetManager2::RebuildFilterList() { + for (PackageGroup& group : package_groups_) { + for (ConfiguredPackage& impl : group.packages_) { + // Destroy it. + impl.filtered_configs_.~ByteBucketArray(); + + // Re-create it. + new (&impl.filtered_configs_) ByteBucketArray(); + + // Create the filters here. + impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec* spec, uint8_t type_index) { + FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_index); + const auto iter_end = spec->types + spec->type_count; + for (auto iter = spec->types; iter != iter_end; ++iter) { + ResTable_config this_config; + this_config.copyFromDtoH((*iter)->config); + if (this_config.match(configuration_)) { + group.configurations.push_back(this_config); + group.types.push_back(*iter); + } + } + }); + } + } +} + void AssetManager2::InvalidateCaches(uint32_t diff) { if (diff == 0xffffffffu) { // Everything must go. @@ -868,7 +1015,7 @@ ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_type_spec_flags, - uint32_t* out_last_ref) { + uint32_t* out_last_ref) const { if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) { uint32_t new_flags; cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags); diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index e08848f891f6..1d2c597c4c8c 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -44,44 +44,6 @@ namespace android { constexpr const static int kAppPackageId = 0x7f; -// Element of a TypeSpec array. See TypeSpec. -struct Type { - // The configuration for which this type defines entries. - // This is already converted to host endianness. - ResTable_config configuration; - - // Pointer to the mmapped data where entry definitions are kept. - const ResTable_type* type; -}; - -// TypeSpec is going to be immediately proceeded by -// an array of Type structs, all in the same block of memory. -struct TypeSpec { - // Pointer to the mmapped data where flags are kept. - // Flags denote whether the resource entry is public - // and under which configurations it varies. - const ResTable_typeSpec* type_spec; - - // Pointer to the mmapped data where the IDMAP mappings for this type - // exist. May be nullptr if no IDMAP exists. - const IdmapEntry_header* idmap_entries; - - // The number of types that follow this struct. - // There is a type for each configuration - // that entries are defined for. - size_t type_count; - - // Trick to easily access a variable number of Type structs - // proceeding this struct, and to ensure their alignment. - const Type types[0]; -}; - -// TypeSpecPtr points to the block of memory that holds -// a TypeSpec struct, followed by an array of Type structs. -// TypeSpecPtr is a managed pointer that knows how to delete -// itself. -using TypeSpecPtr = util::unique_cptr; - namespace { // Builder that helps accumulate Type structs and then create a single @@ -95,21 +57,22 @@ class TypeSpecPtrBuilder { } void AddType(const ResTable_type* type) { - ResTable_config config; - config.copyFromDtoH(type->config); - types_.push_back(Type{config, type}); + types_.push_back(type); } TypeSpecPtr Build() { // Check for overflow. - if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) { + using ElementType = const ResTable_type*; + if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(ElementType) < + types_.size()) { return {}; } - TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type))); + TypeSpec* type_spec = + (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType))); type_spec->type_spec = header_; type_spec->idmap_entries = idmap_header_; type_spec->type_count = types_.size(); - memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type)); + memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType)); return TypeSpecPtr(type_spec); } @@ -118,7 +81,7 @@ class TypeSpecPtrBuilder { const ResTable_typeSpec* header_; const IdmapEntry_header* idmap_header_; - std::vector types_; + std::vector types_; }; } // namespace @@ -162,18 +125,17 @@ static bool VerifyResTableType(const ResTable_type* header) { return true; } -static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset, - size_t entry_idx) { +static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) { // Check that the offset is aligned. if (entry_offset & 0x03) { - LOG(ERROR) << "Entry offset at index " << entry_idx << " is not 4-byte aligned."; + LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned."; return false; } // Check that the offset doesn't overflow. if (entry_offset > std::numeric_limits::max() - dtohl(type->entriesStart)) { // Overflow in offset. - LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large."; + LOG(ERROR) << "Entry at offset " << entry_offset << " is too large."; return false; } @@ -181,7 +143,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset entry_offset += dtohl(type->entriesStart); if (entry_offset > chunk_size - sizeof(ResTable_entry)) { - LOG(ERROR) << "Entry offset at index " << entry_idx + LOG(ERROR) << "Entry at offset " << entry_offset << " is too large. No room for ResTable_entry."; return false; } @@ -191,13 +153,13 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t entry_size = dtohs(entry->size); if (entry_size < sizeof(*entry)) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx + LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too small."; return false; } if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx + LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too large."; return false; } @@ -205,7 +167,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset if (entry_size < sizeof(ResTable_map_entry)) { // There needs to be room for one Res_value struct. if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) { - LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << entry_idx + LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset << " for type " << (int)type->id << "."; return false; } @@ -214,12 +176,12 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset reinterpret_cast(reinterpret_cast(entry) + entry_size); const size_t value_size = dtohs(value->size); if (value_size < sizeof(Res_value)) { - LOG(ERROR) << "Res_value at index " << entry_idx << " is too small."; + LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small."; return false; } if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) { - LOG(ERROR) << "Res_value size " << value_size << " at index " << entry_idx + LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset << " is too large."; return false; } @@ -228,117 +190,76 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t map_entry_count = dtohl(map->count); size_t map_entries_start = entry_offset + entry_size; if (map_entries_start & 0x03) { - LOG(ERROR) << "Map entries at index " << entry_idx << " start at unaligned offset."; + LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset."; return false; } // Each entry is sizeof(ResTable_map) big. if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) { - LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << entry_idx << "."; + LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << "."; return false; } } return true; } -bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_idx, - const ResTable_config& config, FindEntryResult* out_entry) const { - const ResTable_config* best_config = nullptr; - const ResTable_type* best_type = nullptr; - uint32_t best_offset = 0; - - for (uint32_t i = 0; i < type_spec_ptr->type_count; i++) { - const Type* type = &type_spec_ptr->types[i]; - const ResTable_type* type_chunk = type->type; - - if (type->configuration.match(config) && - (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const size_t entry_count = dtohl(type_chunk->entryCount); - const size_t offsets_offset = dtohs(type_chunk->header.headerSize); - - // Check if there is the desired entry in this type. - - if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { - // This is encoded as a sparse map, so perform a binary search. - const ResTable_sparseTypeEntry* sparse_indices = - reinterpret_cast( - reinterpret_cast(type_chunk) + offsets_offset); - const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; - const ResTable_sparseTypeEntry* result = - std::lower_bound(sparse_indices, sparse_indices_end, entry_idx, - [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { - return dtohs(entry.idx) < entry_idx; - }); - - if (result == sparse_indices_end || dtohs(result->idx) != entry_idx) { - // No entry found. - continue; - } - - // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as - // the real offset divided by 4. - best_offset = uint32_t{dtohs(result->offset)} * 4u; - } else { - if (entry_idx >= entry_count) { - // This entry cannot be here. - continue; - } +const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk, + uint16_t entry_index) { + uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index); + if (entry_offset == ResTable_type::NO_ENTRY) { + return nullptr; + } + return GetEntryFromOffset(type_chunk, entry_offset); +} - const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type_chunk) + offsets_offset); - const uint32_t offset = dtohl(entry_offsets[entry_idx]); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } +uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const size_t entry_count = dtohl(type_chunk->entryCount); + const size_t offsets_offset = dtohs(type_chunk->header.headerSize); - // There is an entry for this resource, record it. - best_offset = offset; - } + // Check if there is the desired entry in this type. - best_config = &type->configuration; - best_type = type_chunk; + if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { + // This is encoded as a sparse map, so perform a binary search. + const ResTable_sparseTypeEntry* sparse_indices = + reinterpret_cast( + reinterpret_cast(type_chunk) + offsets_offset); + const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; + const ResTable_sparseTypeEntry* result = + std::lower_bound(sparse_indices, sparse_indices_end, entry_index, + [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { + return dtohs(entry.idx) < entry_idx; + }); + + if (result == sparse_indices_end || dtohs(result->idx) != entry_index) { + // No entry found. + return ResTable_type::NO_ENTRY; } - } - if (best_type == nullptr) { - return false; + // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as + // the real offset divided by 4. + return uint32_t{dtohs(result->offset)} * 4u; } - if (UNLIKELY(!VerifyResTableEntry(best_type, best_offset, entry_idx))) { - return false; + // This type is encoded as a dense array. + if (entry_index >= entry_count) { + // This entry cannot be here. + return ResTable_type::NO_ENTRY; } - const ResTable_entry* best_entry = reinterpret_cast( - reinterpret_cast(best_type) + best_offset + dtohl(best_type->entriesStart)); - - const uint32_t* flags = reinterpret_cast(type_spec_ptr->type_spec + 1); - out_entry->type_flags = dtohl(flags[entry_idx]); - out_entry->entry = best_entry; - out_entry->config = best_config; - out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1); - out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index)); - return true; + const uint32_t* entry_offsets = reinterpret_cast( + reinterpret_cast(type_chunk) + offsets_offset); + return dtohl(entry_offsets[entry_index]); } -bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, - FindEntryResult* out_entry) const { - // If the type IDs are offset in this package, we need to take that into account when searching - // for a type. - const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; - if (UNLIKELY(ptr == nullptr)) { - return false; +const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk, + uint32_t offset) { + if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) { + return nullptr; } - - // If there is an IDMAP supplied with this package, translate the entry ID. - if (ptr->idmap_entries != nullptr) { - if (!LoadedIdmap::Lookup(ptr->idmap_entries, entry_idx, &entry_idx)) { - // There is no mapping, so the resource is not meant to be in this overlay package. - return false; - } - } - return FindEntry(ptr, entry_idx, config, out_entry); + return reinterpret_cast(reinterpret_cast(type_chunk) + + offset + dtohl(type_chunk->entriesStart)); } void LoadedPackage::CollectConfigurations(bool exclude_mipmap, @@ -346,7 +267,7 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, const static std::u16string kMipMap = u"mipmap"; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const util::unique_cptr& type_spec = type_specs_[i]; + const TypeSpecPtr& type_spec = type_specs_[i]; if (type_spec != nullptr) { if (exclude_mipmap) { const int type_idx = type_spec->type_spec->id - 1; @@ -367,8 +288,11 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, } } - for (size_t j = 0; j < type_spec->type_count; j++) { - out_configs->insert(type_spec->types[j].configuration); + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config config; + config.copyFromDtoH((*iter)->config); + out_configs->insert(config); } } } @@ -378,10 +302,12 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set* out char temp_locale[RESTABLE_MAX_LOCALE_LEN]; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const util::unique_cptr& type_spec = type_specs_[i]; + const TypeSpecPtr& type_spec = type_specs_[i]; if (type_spec != nullptr) { - for (size_t j = 0; j < type_spec->type_count; j++) { - const ResTable_config& configuration = type_spec->types[j].configuration; + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config configuration; + configuration.copyFromDtoH((*iter)->config); if (configuration.locale != 0) { configuration.getBcp47Locale(temp_locale, canonicalize); std::string locale(temp_locale); @@ -409,17 +335,17 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } - for (size_t ti = 0; ti < type_spec->type_count; ti++) { - const Type* type = &type_spec->types[ti]; - size_t entry_count = dtohl(type->type->entryCount); + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + const ResTable_type* type = *iter; + size_t entry_count = dtohl(type->entryCount); for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) { const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type->type) + dtohs(type->type->header.headerSize)); + reinterpret_cast(type) + dtohs(type->header.headerSize)); const uint32_t offset = dtohl(entry_offsets[entry_idx]); if (offset != ResTable_type::NO_ENTRY) { - const ResTable_entry* entry = - reinterpret_cast(reinterpret_cast(type->type) + - dtohl(type->type->entriesStart) + offset); + const ResTable_entry* entry = reinterpret_cast( + reinterpret_cast(type) + dtohl(type->entriesStart) + offset); if (dtohl(entry->key.index) == static_cast(key_idx)) { // The package ID will be overridden by the caller (due to runtime assignment of package // IDs for shared libraries). @@ -431,8 +357,7 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } -const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const { - const uint8_t package_id = get_package_id(resid); +const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { for (const auto& loaded_package : packages_) { if (loaded_package->GetPackageId() == package_id) { return loaded_package.get(); @@ -680,26 +605,6 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, return std::move(loaded_package); } -bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, - FindEntryResult* out_entry) const { - ATRACE_CALL(); - - const uint8_t package_id = get_package_id(resid); - const uint8_t type_id = get_type_id(resid); - const uint16_t entry_id = get_entry_id(resid); - - if (UNLIKELY(type_id == 0)) { - LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); - return false; - } - - for (const auto& loaded_package : packages_) { - if (loaded_package->GetPackageId() == package_id) { - return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry); - } - } - return false; -} bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library) { diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index b033137b4764..ef08897d997a 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -69,6 +69,8 @@ struct ResolvedBag { Entry entries[0]; }; +struct FindEntryResult; + // AssetManager2 is the main entry point for accessing assets and resources. // AssetManager2 provides caching of resources retrieved via the underlying ApkAssets. class AssetManager2 { @@ -127,7 +129,7 @@ class AssetManager2 { // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap' // will be excluded from the list. std::set GetResourceConfigurations(bool exclude_system = false, - bool exclude_mipmap = false); + bool exclude_mipmap = false) const; // Returns all the locales for which there are resources defined. This includes resource // locales in all the ApkAssets set for this AssetManager. @@ -136,24 +138,24 @@ class AssetManager2 { // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized // and de-duped in the resulting list. std::set GetResourceLocales(bool exclude_system = false, - bool merge_equivalent_languages = false); + bool merge_equivalent_languages = false) const; // Searches the set of APKs loaded by this AssetManager and opens the first one found located // in the assets/ directory. // `mode` controls how the file is opened. // // NOTE: The loaded APKs are searched in reverse order. - std::unique_ptr Open(const std::string& filename, Asset::AccessMode mode); + std::unique_ptr Open(const std::string& filename, Asset::AccessMode mode) const; // Opens a file within the assets/ directory of the APK specified by `cookie`. // `mode` controls how the file is opened. std::unique_ptr Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode); + Asset::AccessMode mode) const; // Opens the directory specified by `dirname`. The result is an AssetDir that is the combination // of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded. // The entries are sorted by their ASCII name. - std::unique_ptr OpenDir(const std::string& dirname); + std::unique_ptr OpenDir(const std::string& dirname) const; // Searches the set of APKs loaded by this AssetManager and opens the first one found. // `mode` controls how the file is opened. @@ -161,24 +163,24 @@ class AssetManager2 { // // NOTE: The loaded APKs are searched in reverse order. std::unique_ptr OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie = nullptr); + ApkAssetsCookie* out_cookie = nullptr) const; // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened. // This is typically used to open a specific AndroidManifest.xml, or a binary XML file // referenced by a resource lookup with GetResource(). std::unique_ptr OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode); + Asset::AccessMode mode) const; // Populates the `out_name` parameter with resource name information. // Utf8 strings are preferred, and only if they are unavailable are // the Utf16 variants populated. // Returns false if the resource was not found or the name was missing/corrupt. - bool GetResourceName(uint32_t resid, ResourceName* out_name); + bool GetResourceName(uint32_t resid, ResourceName* out_name) const; // Populates `out_flags` with the bitmask of configuration axis that this resource varies with. // See ResTable_config for the list of configuration axis. // Returns false if the resource was not found. - bool GetResourceFlags(uint32_t resid, uint32_t* out_flags); + bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const; // Finds the resource ID assigned to `resource_name`. // `resource_name` must be of the form '[package:][type/]entry'. @@ -186,7 +188,7 @@ class AssetManager2 { // If no type is specified in `resource_name`, then `fallback_type` is used as the type. // Returns 0x0 if no resource by that name was found. uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {}, - const std::string& fallback_package = {}); + const std::string& fallback_package = {}) const; // Retrieves the best matching resource with ID `resid`. The resource value is filled into // `out_value` and the configuration for the selected value is populated in `out_selected_config`. @@ -199,7 +201,7 @@ class AssetManager2 { // this function logs if the resource was a map/bag type before returning kInvalidCookie. ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags); + uint32_t* out_flags) const; // Resolves the resource reference in `in_out_value` if the data type is // Res_value::TYPE_REFERENCE. @@ -215,7 +217,7 @@ class AssetManager2 { // it was not found. ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference); + uint32_t* out_last_reference) const; // Retrieves the best matching bag/map resource with ID `resid`. // This method will resolve all parent references for this bag and merge keys with the child. @@ -233,9 +235,9 @@ class AssetManager2 { std::unique_ptr NewTheme(); template - void ForEachPackage(Func func) { + void ForEachPackage(Func func) const { for (const PackageGroup& package_group : package_groups_) { - func(package_group.packages_.front()->GetPackageName(), + func(package_group.packages_.front().loaded_package_->GetPackageName(), package_group.dynamic_ref_table.mAssignedPackageId); } } @@ -260,7 +262,7 @@ class AssetManager2 { // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds. ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, - FindEntryResult* out_entry); + FindEntryResult* out_entry) const; // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. @@ -270,13 +272,43 @@ class AssetManager2 { // bitmask `diff`. void InvalidateCaches(uint32_t diff); + // Triggers the re-construction of lists of types that match the set configuration. + // This should always be called when mutating the AssetManager's configuration or ApkAssets set. + void RebuildFilterList(); + // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector apk_assets_; + // A collection of configurations and their associated ResTable_type that match the current + // AssetManager configuration. + struct FilteredConfigGroup { + std::vector configurations; + std::vector types; + }; + + // Represents an single package. + struct ConfiguredPackage { + // A pointer to the immutable, loaded package info. + const LoadedPackage* loaded_package_; + + // A mutable AssetManager-specific list of configurations that match the AssetManager's + // current configuration. This is used as an optimization to avoid checking every single + // candidate configuration when looking up resources. + ByteBucketArray filtered_configs_; + }; + + // Represents a logical package, which can be made up of many individual packages. Each package + // in a PackageGroup shares the same package name and package ID. struct PackageGroup { - std::vector packages_; + // The set of packages that make-up this group. + std::vector packages_; + + // The cookies associated with each package in the group. They share the same order as + // packages_. std::vector cookies_; + + // A library reference table that contains build-package ID to runtime-package ID mappings. DynamicRefTable dynamic_ref_table; }; @@ -350,7 +382,7 @@ class Theme { ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config = nullptr, uint32_t* in_out_type_spec_flags = nullptr, - uint32_t* out_last_ref = nullptr); + uint32_t* out_last_ref = nullptr) const; private: DISALLOW_COPY_AND_ASSIGN(Theme); diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 1775f5070f4e..35ae5fcd9e7b 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -41,33 +41,40 @@ class DynamicPackageEntry { int package_id = 0; }; -struct FindEntryResult { - // A pointer to the resource table entry for this resource. - // If the size of the entry is > sizeof(ResTable_entry), it can be cast to - // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry; - - // The configuration for which the resulting entry was defined. This points to a structure that - // is already swapped to host endianness. - const ResTable_config* config; - - // The bitmask of configuration axis with which the resource value varies. - uint32_t type_flags; - - // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table; - - // The string pool reference to the type's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef type_string_ref; - - // The string pool reference to the entry's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef entry_string_ref; +// TypeSpec is going to be immediately proceeded by +// an array of Type structs, all in the same block of memory. +struct TypeSpec { + // Pointer to the mmapped data where flags are kept. + // Flags denote whether the resource entry is public + // and under which configurations it varies. + const ResTable_typeSpec* type_spec; + + // Pointer to the mmapped data where the IDMAP mappings for this type + // exist. May be nullptr if no IDMAP exists. + const IdmapEntry_header* idmap_entries; + + // The number of types that follow this struct. + // There is a type for each configuration that entries are defined for. + size_t type_count; + + // Trick to easily access a variable number of Type structs + // proceeding this struct, and to ensure their alignment. + const ResTable_type* types[0]; + + inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const { + if (entry_index >= dtohl(type_spec->entryCount)) { + return 0u; + } + + const uint32_t* flags = reinterpret_cast(type_spec + 1); + return flags[entry_index]; + } }; -struct TypeSpec; -class LoadedArsc; +// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of +// ResTable_type pointers. +// TypeSpecPtr is a managed pointer that knows how to delete itself. +using TypeSpecPtr = util::unique_cptr; class LoadedPackage { public: @@ -77,9 +84,6 @@ class LoadedPackage { ~LoadedPackage(); - bool FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, - FindEntryResult* out_entry) const; - // Finds the entry with the specified type name and entry name. The names are in UTF-16 because // the underlying ResStringPool API expects this. For now this is acceptable, but since // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. @@ -87,6 +91,12 @@ class LoadedPackage { // for patching the correct package ID to the resource ID. uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; + static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index); + + static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index); + + static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset); + // Returns the string pool where type names are stored. inline const ResStringPool* GetTypeStringPool() const { return &type_string_pool_; @@ -136,14 +146,32 @@ class LoadedPackage { // before being inserted into the set. This may cause some equivalent locales to de-dupe. void CollectLocales(bool canonicalize, std::set* out_locales) const; + // type_idx is TT - 1 from 0xPPTTEEEE. + inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const { + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + return type_specs_[type_index - type_id_offset_].get(); + } + + template + void ForEachTypeSpec(Func f) const { + for (size_t i = 0; i < type_specs_.size(); i++) { + const TypeSpecPtr& ptr = type_specs_[i]; + if (ptr != nullptr) { + uint8_t type_id = ptr->type_spec->id; + if (ptr->idmap_entries != nullptr) { + type_id = ptr->idmap_entries->target_type_id; + } + f(ptr.get(), type_id - 1); + } + } + } + private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); LoadedPackage(); - bool FindEntry(const util::unique_cptr& type_spec_ptr, uint16_t entry_idx, - const ResTable_config& config, FindEntryResult* out_entry) const; - ResStringPool type_string_pool_; ResStringPool key_string_pool_; std::string package_name_; @@ -153,7 +181,7 @@ class LoadedPackage { bool system_ = false; bool overlay_ = false; - ByteBucketArray> type_specs_; + ByteBucketArray type_specs_; std::vector dynamic_package_map_; }; @@ -181,25 +209,20 @@ class LoadedArsc { return &global_string_pool_; } - // Finds the resource with ID `resid` with the best value for configuration `config`. - // The parameter `out_entry` will be filled with the resulting resource entry. - // The resource entry can be a simple entry (ResTable_entry) or a complex bag - // (ResTable_entry_map). - bool FindEntry(uint32_t resid, const ResTable_config& config, FindEntryResult* out_entry) const; + // Gets a pointer to the package with the specified package ID, or nullptr if no such package + // exists. + const LoadedPackage* GetPackageById(uint8_t package_id) const; - // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist. - const LoadedPackage* GetPackageForId(uint32_t resid) const; + // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. + inline const std::vector>& GetPackages() const { + return packages_; + } // Returns true if this is a system provided resource. inline bool IsSystem() const { return system_; } - // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. - inline const std::vector>& GetPackages() const { - return packages_; - } - private: DISALLOW_COPY_AND_ASSIGN(LoadedArsc); diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index 6c43a67e602f..e2b9f0040989 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -26,58 +26,56 @@ using ::android::base::unique_fd; using ::com::android::basic::R; +using ::testing::Eq; +using ::testing::Ge; +using ::testing::NotNull; +using ::testing::SizeIs; +using ::testing::StrEq; namespace android { TEST(ApkAssetsTest, LoadApk) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - - const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); - ASSERT_NE(nullptr, loaded_package); - - std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); + ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkFromFd) { const std::string path = GetTestDataPath() + "/basic/basic.apk"; unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY)); - ASSERT_GE(fd.get(), 0); + ASSERT_THAT(fd.get(), Ge(0)); std::unique_ptr loaded_apk = ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - - const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); - ASSERT_NE(nullptr, loaded_package); - - std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); + ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); + const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic()); loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic()); } @@ -86,19 +84,22 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { ResTable target_table; const std::string target_path = GetTestDataPath() + "/basic/basic.apk"; ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, target_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); + ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/), + Eq(NO_ERROR)); ResTable overlay_table; const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk"; ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); + ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/), + Eq(NO_ERROR)); util::unique_cptr idmap_data; void* temp_data; size_t idmap_len; - ASSERT_EQ(NO_ERROR, target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), - overlay_path.c_str(), &temp_data, &idmap_len)); + ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), + overlay_path.c_str(), &temp_data, &idmap_len), + Eq(NO_ERROR)); idmap_data.reset(temp_data); TemporaryFile tf; @@ -108,37 +109,30 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { // Open something so that the destructor of TemporaryFile closes a valid fd. tf.fd = open("/dev/null", O_WRONLY); - std::unique_ptr loaded_overlay_apk = ApkAssets::LoadOverlay(tf.path); - ASSERT_NE(nullptr, loaded_overlay_apk); + ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull()); } TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); - { - std::unique_ptr assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, assets); - } + { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } - { - std::unique_ptr assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, assets); - } + { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } } TEST(ApkAssetsTest, OpenUncompressedAssetFd) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(asset, NotNull()); off64_t start, length; unique_fd fd(asset->openFileDescriptor(&start, &length)); - EXPECT_GE(fd.get(), 0); + ASSERT_THAT(fd.get(), Ge(0)); lseek64(fd.get(), start, SEEK_SET); @@ -146,7 +140,7 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) { buffer.resize(length); ASSERT_TRUE(base::ReadFully(fd.get(), &*buffer.begin(), length)); - EXPECT_EQ("This should be uncompressed.\n\n", buffer); + EXPECT_THAT(buffer, StrEq("This should be uncompressed.\n\n")); } } // namespace android diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 37ddafb14fd3..bedebd66cb2f 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -16,6 +16,8 @@ #include "androidfw/LoadedArsc.h" +#include "androidfw/ResourceUtils.h" + #include "TestHelpers.h" #include "data/basic/R.h" #include "data/libclient/R.h" @@ -27,6 +29,13 @@ namespace basic = com::android::basic; namespace libclient = com::android::libclient; namespace sparse = com::android::sparse; +using ::testing::Eq; +using ::testing::Ge; +using ::testing::IsNull; +using ::testing::NotNull; +using ::testing::SizeIs; +using ::testing::StrEq; + namespace android { TEST(LoadedArscTest, LoadSinglePackageArsc) { @@ -35,39 +44,24 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); - - const std::vector>& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - EXPECT_EQ(std::string("com.android.app"), packages[0]->GetPackageName()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); - - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.sdkVersion = 24; - - FindEntryResult entry; + ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_TRUE(loaded_arsc->FindEntry(app::R::string::string_one, config, &entry)); - ASSERT_NE(nullptr, entry.entry); -} + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(app::R::string::string_one)); + ASSERT_THAT(package, NotNull()); + EXPECT_THAT(package->GetPackageName(), StrEq("com.android.app")); + EXPECT_THAT(package->GetPackageId(), Eq(0x7f)); -TEST(LoadedArscTest, FindDefaultEntry) { - std::string contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); + const uint8_t type_index = get_type_id(app::R::string::string_one) - 1; + const uint16_t entry_index = get_entry_id(app::R::string::string_one); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); - desired_config.language[0] = 'd'; - desired_config.language[1] = 'e'; - - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry)); - ASSERT_NE(nullptr, entry.entry); + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); } TEST(LoadedArscTest, LoadSparseEntryApp) { @@ -76,15 +70,22 @@ TEST(LoadedArscTest, LoadSparseEntryApp) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); + + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9)); + ASSERT_THAT(package, NotNull()); - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.sdkVersion = 26; + const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1; + const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9); - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(sparse::R::integer::foo_9, config, &entry)); - ASSERT_NE(nullptr, entry.entry); + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); } TEST(LoadedArscTest, LoadSharedLibrary) { @@ -93,14 +94,13 @@ TEST(LoadedArscTest, LoadSharedLibrary) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_EQ(std::string("com.android.lib_one"), packages[0]->GetPackageName()); - EXPECT_EQ(0, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.lib_one")); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0)); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); @@ -114,25 +114,23 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) { "resources.arsc", &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_FALSE(packages[0]->IsDynamic()); - EXPECT_EQ(std::string("com.android.libclient"), packages[0]->GetPackageName()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.libclient")); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); // The library has two dependencies. - ASSERT_EQ(2u, dynamic_pkg_map.size()); + ASSERT_THAT(dynamic_pkg_map, SizeIs(2u)); + EXPECT_THAT(dynamic_pkg_map[0].package_name, StrEq("com.android.lib_one")); + EXPECT_THAT(dynamic_pkg_map[0].package_id, Eq(0x02)); - EXPECT_EQ(std::string("com.android.lib_one"), dynamic_pkg_map[0].package_name); - EXPECT_EQ(0x02, dynamic_pkg_map[0].package_id); - - EXPECT_EQ(std::string("com.android.lib_two"), dynamic_pkg_map[1].package_name); - EXPECT_EQ(0x03, dynamic_pkg_map[1].package_id); + EXPECT_THAT(dynamic_pkg_map[1].package_name, StrEq("com.android.lib_two")); + EXPECT_THAT(dynamic_pkg_map[1].package_id, Eq(0x03)); } TEST(LoadedArscTest, LoadAppAsSharedLibrary) { @@ -143,13 +141,12 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, true /*load_as_shared_library*/); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); } TEST(LoadedArscTest, LoadFeatureSplit) { @@ -157,21 +154,27 @@ TEST(LoadedArscTest, LoadFeatureSplit) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc", &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(basic::R::string::test3)); + ASSERT_THAT(package, NotNull()); - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry)); + uint8_t type_index = get_type_id(basic::R::string::test3) - 1; + uint8_t entry_index = get_entry_id(basic::R::string::test3); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); size_t len; - const char16_t* type_name16 = entry.type_string_ref.string16(&len); - ASSERT_NE(nullptr, type_name16); - ASSERT_NE(0u, len); + const char16_t* type_name16 = + package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len); + ASSERT_THAT(type_name16, NotNull()); + EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string")); - std::string type_name = util::Utf16ToUtf8(StringPiece16(type_name16, len)); - EXPECT_EQ(std::string("string"), type_name); + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull()); } class MockLoadedIdmap : public LoadedIdmap { @@ -199,23 +202,33 @@ class MockLoadedIdmap : public LoadedIdmap { }; TEST(LoadedArscTest, LoadOverlay) { - std::string contents, overlay_contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); + std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc", - &overlay_contents)); + &contents)); MockLoadedIdmap loaded_idmap; std::unique_ptr loaded_arsc = - LoadedArsc::Load(StringPiece(overlay_contents), &loaded_idmap); - ASSERT_NE(nullptr, loaded_arsc); - - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); - - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(0x08030001u, desired_config, &entry)); + LoadedArsc::Load(StringPiece(contents), &loaded_idmap); + ASSERT_THAT(loaded_arsc, NotNull()); + + const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u); + ASSERT_THAT(package, NotNull()); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); + + // The entry being overlaid doesn't exist at the original entry index. + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull()); + + // Since this is an overlay, the actual entry ID must be mapped. + ASSERT_THAT(type_spec->idmap_entries, NotNull()); + uint16_t target_entry_id = 0u; + ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id)); + ASSERT_THAT(target_entry_id, Eq(0x0u)); + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull()); } // structs with size fields (like Res_value, ResTable_entry) should be diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h index 43a995536d89..df0c642f4565 100644 --- a/libs/androidfw/tests/TestHelpers.h +++ b/libs/androidfw/tests/TestHelpers.h @@ -20,6 +20,7 @@ #include #include "androidfw/ResourceTypes.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "CommonHelpers.h" -- cgit v1.2.3-59-g8ed1b From 0dd369912a2b46c22d09ddc6ae117fe173e51cb8 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Thu, 25 Jan 2018 15:38:38 -0800 Subject: Revert "libandroidfw: Improve performance of AssetManager2" This reverts commit 64ee69d0f1f412edee2eb7a0c846deebbfa37ef9. Bug:72511998 Change-Id: Iab3ce449e60ec2451d391217543528d312089080 --- libs/androidfw/Android.bp | 1 - libs/androidfw/AssetManager2.cpp | 273 ++++++----------------- libs/androidfw/LoadedArsc.cpp | 261 +++++++++++++++------- libs/androidfw/include/androidfw/AssetManager2.h | 66 ++---- libs/androidfw/include/androidfw/LoadedArsc.h | 111 ++++----- libs/androidfw/tests/ApkAssets_test.cpp | 78 ++++--- libs/androidfw/tests/LoadedArsc_test.cpp | 169 +++++++------- libs/androidfw/tests/TestHelpers.h | 1 - 8 files changed, 422 insertions(+), 538 deletions(-) (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 70d52164ff74..7c9078b164a2 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -145,7 +145,6 @@ cc_test { "tests/TypeWrappers_test.cpp", "tests/ZipUtils_test.cpp", ], - static_libs: ["libgmock"], target: { android: { srcs: [ diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index a558ff7ccfc1..2fc8e952707b 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -36,31 +36,6 @@ namespace android { -struct FindEntryResult { - // A pointer to the resource table entry for this resource. - // If the size of the entry is > sizeof(ResTable_entry), it can be cast to - // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry; - - // The configuration for which the resulting entry was defined. This is already swapped to host - // endianness. - ResTable_config config; - - // The bitmask of configuration axis with which the resource value varies. - uint32_t type_flags; - - // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table; - - // The string pool reference to the type's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef type_string_ref; - - // The string pool reference to the entry's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef entry_string_ref; -}; - AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } @@ -69,7 +44,6 @@ bool AssetManager2::SetApkAssets(const std::vector& apk_assets bool invalidate_caches) { apk_assets_ = apk_assets; BuildDynamicRefTable(); - RebuildFilterList(); if (invalidate_caches) { InvalidateCaches(static_cast(-1)); } @@ -105,7 +79,7 @@ void AssetManager2::BuildDynamicRefTable() { PackageGroup* package_group = &package_groups_[idx]; // Add the package and to the set of packages with the same ID. - package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); + package_group->packages_.push_back(package.get()); package_group->cookies_.push_back(static_cast(i)); // Add the package name -> build time ID mappings. @@ -120,7 +94,7 @@ void AssetManager2::BuildDynamicRefTable() { // Now assign the runtime IDs so that we have a build-time to runtime ID map. const auto package_groups_end = package_groups_.end(); for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) { - const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName(); + const std::string& package_name = iter->packages_[0]->GetPackageName(); for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) { iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()), iter->dynamic_ref_table.mAssignedPackageId); @@ -134,20 +108,17 @@ void AssetManager2::DumpToLog() const { std::string list; for (size_t i = 0; i < package_ids_.size(); i++) { if (package_ids_[i] != 0xff) { - base::StringAppendF(&list, "%02x -> %d, ", (int)i, package_ids_[i]); + base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]); } } LOG(INFO) << "Package ID map: " << list; - for (const auto& package_group : package_groups_) { - list = ""; - for (const auto& package : package_group.packages_) { - base::StringAppendF(&list, "%s(%02x), ", package.loaded_package_->GetPackageName().c_str(), - package.loaded_package_->GetPackageId()); - } - LOG(INFO) << base::StringPrintf("PG (%02x): ", - package_group.dynamic_ref_table.mAssignedPackageId) - << list; + for (const auto& package_group: package_groups_) { + list = ""; + for (const auto& package : package_group.packages_) { + base::StringAppendF(&list, "%s(%02x), ", package->GetPackageName().c_str(), package->GetPackageId()); + } + LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list; } } @@ -186,54 +157,52 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { configuration_ = configuration; if (diff) { - RebuildFilterList(); InvalidateCaches(static_cast(diff)); } } std::set AssetManager2::GetResourceConfigurations(bool exclude_system, - bool exclude_mipmap) const { + bool exclude_mipmap) { ATRACE_CALL(); std::set configurations; for (const PackageGroup& package_group : package_groups_) { - for (const ConfiguredPackage& package : package_group.packages_) { - if (exclude_system && package.loaded_package_->IsSystem()) { + for (const LoadedPackage* package : package_group.packages_) { + if (exclude_system && package->IsSystem()) { continue; } - package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations); + package->CollectConfigurations(exclude_mipmap, &configurations); } } return configurations; } std::set AssetManager2::GetResourceLocales(bool exclude_system, - bool merge_equivalent_languages) const { + bool merge_equivalent_languages) { ATRACE_CALL(); std::set locales; for (const PackageGroup& package_group : package_groups_) { - for (const ConfiguredPackage& package : package_group.packages_) { - if (exclude_system && package.loaded_package_->IsSystem()) { + for (const LoadedPackage* package : package_group.packages_) { + if (exclude_system && package->IsSystem()) { continue; } - package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales); + package->CollectLocales(merge_equivalent_languages, &locales); } } return locales; } -std::unique_ptr AssetManager2::Open(const std::string& filename, - Asset::AccessMode mode) const { +std::unique_ptr AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, mode); } std::unique_ptr AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) const { + Asset::AccessMode mode) { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, cookie, mode); } -std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) const { +std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) { ATRACE_CALL(); std::string full_path = "assets/" + dirname; @@ -267,7 +236,7 @@ std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) con // is inconsistent for split APKs. std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie) const { + ApkAssetsCookie* out_cookie) { ATRACE_CALL(); for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) { std::unique_ptr asset = apk_assets_[i]->Open(filename, mode); @@ -286,8 +255,7 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, - ApkAssetsCookie cookie, - Asset::AccessMode mode) const { + ApkAssetsCookie cookie, Asset::AccessMode mode) { ATRACE_CALL(); if (cookie < 0 || static_cast(cookie) >= apk_assets_.size()) { return {}; @@ -296,13 +264,12 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, - bool /*stop_at_first_match*/, - FindEntryResult* out_entry) const { + bool stop_at_first_match, FindEntryResult* out_entry) { // Might use this if density_override != 0. ResTable_config density_override_config; // Select our configuration or generate a density override configuration. - const ResTable_config* desired_config = &configuration_; + ResTable_config* desired_config = &configuration_; if (density_override != 0 && density_override != configuration_.density) { density_override_config = configuration_; density_override_config.density = density_override; @@ -316,135 +283,53 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri const uint32_t package_id = get_package_id(resid); const uint8_t type_idx = get_type_id(resid) - 1; - const uint16_t entry_idx = get_entry_id(resid); + const uint16_t entry_id = get_entry_id(resid); - const uint8_t package_idx = package_ids_[package_id]; - if (package_idx == 0xff) { + const uint8_t idx = package_ids_[package_id]; + if (idx == 0xff) { LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); return kInvalidCookie; } - const PackageGroup& package_group = package_groups_[package_idx]; - const size_t package_count = package_group.packages_.size(); - + FindEntryResult best_entry; ApkAssetsCookie best_cookie = kInvalidCookie; - const LoadedPackage* best_package = nullptr; - const ResTable_type* best_type = nullptr; - const ResTable_config* best_config = nullptr; - ResTable_config best_config_copy; - uint32_t best_offset = 0u; - uint32_t type_flags = 0u; - - // If desired_config is the same as the set configuration, then we can use our filtered list - // and we don't need to match the configurations, since they already matched. - const bool use_fast_path = desired_config == &configuration_; - - for (size_t pi = 0; pi < package_count; pi++) { - const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; - const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_; - ApkAssetsCookie cookie = package_group.cookies_[pi]; - - // If the type IDs are offset in this package, we need to take that into account when searching - // for a type. - const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx); - if (UNLIKELY(type_spec == nullptr)) { - continue; - } - - uint16_t local_entry_idx = entry_idx; + uint32_t cumulated_flags = 0u; - // If there is an IDMAP supplied with this package, translate the entry ID. - if (type_spec->idmap_entries != nullptr) { - if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) { - // There is no mapping, so the resource is not meant to be in this overlay package. - continue; - } + const PackageGroup& package_group = package_groups_[idx]; + const size_t package_count = package_group.packages_.size(); + FindEntryResult current_entry; + for (size_t i = 0; i < package_count; i++) { + const LoadedPackage* loaded_package = package_group.packages_[i]; + if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry)) { + continue; } - type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx); - - // If the package is an overlay, then even configurations that are the same MUST be chosen. - const bool package_is_overlay = loaded_package->IsOverlay(); - - const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; - if (use_fast_path) { - const std::vector& candidate_configs = filtered_group.configurations; - const size_t type_count = candidate_configs.size(); - for (uint32_t i = 0; i < type_count; i++) { - const ResTable_config& this_config = candidate_configs[i]; - - // We can skip calling ResTable_config::match() because we know that all candidate - // configurations that do NOT match have been filtered-out. - if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || - (package_is_overlay && this_config.compare(*best_config) == 0)) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const ResTable_type* type_chunk = filtered_group.types[i]; - const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } - - best_cookie = cookie; - best_package = loaded_package; - best_type = type_chunk; - best_config = &this_config; - best_offset = offset; - } - } - } else { - // This is the slower path, which doesn't use the filtered list of configurations. - // Here we must read the ResTable_config from the mmapped APK, convert it to host endianness - // and fill in any new fields that did not exist when the APK was compiled. - // Furthermore when selecting configurations we can't just record the pointer to the - // ResTable_config, we must copy it. - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config this_config; - this_config.copyFromDtoH((*iter)->config); - - if (this_config.match(*desired_config)) { - if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || - (package_is_overlay && this_config.compare(*best_config) == 0)) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } + cumulated_flags |= current_entry.type_flags; - best_cookie = cookie; - best_package = loaded_package; - best_type = *iter; - best_config_copy = this_config; - best_config = &best_config_copy; - best_offset = offset; - } - } + const ResTable_config* current_config = current_entry.config; + const ResTable_config* best_config = best_entry.config; + if (best_cookie == kInvalidCookie || + current_config->isBetterThan(*best_config, desired_config) || + (loaded_package->IsOverlay() && current_config->compare(*best_config) == 0)) { + best_entry = current_entry; + best_cookie = package_group.cookies_[i]; + if (stop_at_first_match) { + break; } } } - if (UNLIKELY(best_cookie == kInvalidCookie)) { + if (best_cookie == kInvalidCookie) { return kInvalidCookie; } - const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset); - if (UNLIKELY(best_entry == nullptr)) { - return kInvalidCookie; - } - - out_entry->entry = best_entry; - out_entry->config = *best_config; - out_entry->type_flags = type_flags; - out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); - out_entry->entry_string_ref = - StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); + *out_entry = best_entry; out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; + out_entry->type_flags = cumulated_flags; return best_cookie; } -bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const { +bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { ATRACE_CALL(); FindEntryResult entry; @@ -454,8 +339,7 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons return false; } - const LoadedPackage* package = - apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); + const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid); if (package == nullptr) { return false; } @@ -483,7 +367,7 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons return true; } -bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const { +bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { FindEntryResult entry; ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); @@ -497,7 +381,7 @@ bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) const { + uint32_t* out_flags) { ATRACE_CALL(); FindEntryResult entry; @@ -516,7 +400,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Create a reference since we can't represent this complex type as a Res_value. out_value->dataType = Res_value::TYPE_REFERENCE; out_value->data = resid; - *out_selected_config = entry.config; + *out_selected_config = *entry.config; *out_flags = entry.type_flags; return cookie; } @@ -528,7 +412,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Convert the package ID to the runtime assigned package ID. entry.dynamic_ref_table->lookupResourceValue(out_value); - *out_selected_config = entry.config; + *out_selected_config = *entry.config; *out_flags = entry.type_flags; return cookie; } @@ -536,7 +420,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) const { + uint32_t* out_last_reference) { ATRACE_CALL(); constexpr const int kMaxIterations = 20; @@ -604,8 +488,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { // Attributes, arrays, etc don't have a resource id as the name. They specify // other data, which would be wrong to change via a lookup. if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, - resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); return nullptr; } } @@ -637,8 +520,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { const ResolvedBag* parent_bag = GetBag(parent_resid); if (parent_bag == nullptr) { // Failed to get the parent that should exist. - LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, - resid); + LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid); return nullptr; } @@ -657,8 +539,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t child_key = dtohl(map_entry->name.ident); if (!is_internal_resid(child_key)) { if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, - resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid); return nullptr; } } @@ -697,8 +578,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t new_key = dtohl(map_entry->name.ident); if (!is_internal_resid(new_key)) { if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, - resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); return nullptr; } } @@ -754,7 +634,7 @@ static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) { uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const std::string& fallback_type, - const std::string& fallback_package) const { + const std::string& fallback_package) { StringPiece package_name, type, entry; if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) { return 0u; @@ -786,8 +666,7 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const static std::u16string kAttrPrivate16 = u"^attr-private"; for (const PackageGroup& package_group : package_groups_) { - for (const ConfiguredPackage& package_impl : package_group.packages_) { - const LoadedPackage* package = package_impl.loaded_package_; + for (const LoadedPackage* package : package_group.packages_) { if (package_name != package->GetPackageName()) { // All packages in the same group are expected to have the same package name. break; @@ -809,32 +688,6 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, return 0u; } -void AssetManager2::RebuildFilterList() { - for (PackageGroup& group : package_groups_) { - for (ConfiguredPackage& impl : group.packages_) { - // Destroy it. - impl.filtered_configs_.~ByteBucketArray(); - - // Re-create it. - new (&impl.filtered_configs_) ByteBucketArray(); - - // Create the filters here. - impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec* spec, uint8_t type_index) { - FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_index); - const auto iter_end = spec->types + spec->type_count; - for (auto iter = spec->types; iter != iter_end; ++iter) { - ResTable_config this_config; - this_config.copyFromDtoH((*iter)->config); - if (this_config.match(configuration_)) { - group.configurations.push_back(this_config); - group.types.push_back(*iter); - } - } - }); - } - } -} - void AssetManager2::InvalidateCaches(uint32_t diff) { if (diff == 0xffffffffu) { // Everything must go. @@ -1015,7 +868,7 @@ ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_type_spec_flags, - uint32_t* out_last_ref) const { + uint32_t* out_last_ref) { if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) { uint32_t new_flags; cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags); diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 1d2c597c4c8c..e08848f891f6 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -44,6 +44,44 @@ namespace android { constexpr const static int kAppPackageId = 0x7f; +// Element of a TypeSpec array. See TypeSpec. +struct Type { + // The configuration for which this type defines entries. + // This is already converted to host endianness. + ResTable_config configuration; + + // Pointer to the mmapped data where entry definitions are kept. + const ResTable_type* type; +}; + +// TypeSpec is going to be immediately proceeded by +// an array of Type structs, all in the same block of memory. +struct TypeSpec { + // Pointer to the mmapped data where flags are kept. + // Flags denote whether the resource entry is public + // and under which configurations it varies. + const ResTable_typeSpec* type_spec; + + // Pointer to the mmapped data where the IDMAP mappings for this type + // exist. May be nullptr if no IDMAP exists. + const IdmapEntry_header* idmap_entries; + + // The number of types that follow this struct. + // There is a type for each configuration + // that entries are defined for. + size_t type_count; + + // Trick to easily access a variable number of Type structs + // proceeding this struct, and to ensure their alignment. + const Type types[0]; +}; + +// TypeSpecPtr points to the block of memory that holds +// a TypeSpec struct, followed by an array of Type structs. +// TypeSpecPtr is a managed pointer that knows how to delete +// itself. +using TypeSpecPtr = util::unique_cptr; + namespace { // Builder that helps accumulate Type structs and then create a single @@ -57,22 +95,21 @@ class TypeSpecPtrBuilder { } void AddType(const ResTable_type* type) { - types_.push_back(type); + ResTable_config config; + config.copyFromDtoH(type->config); + types_.push_back(Type{config, type}); } TypeSpecPtr Build() { // Check for overflow. - using ElementType = const ResTable_type*; - if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(ElementType) < - types_.size()) { + if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) { return {}; } - TypeSpec* type_spec = - (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType))); + TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type))); type_spec->type_spec = header_; type_spec->idmap_entries = idmap_header_; type_spec->type_count = types_.size(); - memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType)); + memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type)); return TypeSpecPtr(type_spec); } @@ -81,7 +118,7 @@ class TypeSpecPtrBuilder { const ResTable_typeSpec* header_; const IdmapEntry_header* idmap_header_; - std::vector types_; + std::vector types_; }; } // namespace @@ -125,17 +162,18 @@ static bool VerifyResTableType(const ResTable_type* header) { return true; } -static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) { +static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset, + size_t entry_idx) { // Check that the offset is aligned. if (entry_offset & 0x03) { - LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned."; + LOG(ERROR) << "Entry offset at index " << entry_idx << " is not 4-byte aligned."; return false; } // Check that the offset doesn't overflow. if (entry_offset > std::numeric_limits::max() - dtohl(type->entriesStart)) { // Overflow in offset. - LOG(ERROR) << "Entry at offset " << entry_offset << " is too large."; + LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large."; return false; } @@ -143,7 +181,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset entry_offset += dtohl(type->entriesStart); if (entry_offset > chunk_size - sizeof(ResTable_entry)) { - LOG(ERROR) << "Entry at offset " << entry_offset + LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large. No room for ResTable_entry."; return false; } @@ -153,13 +191,13 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t entry_size = dtohs(entry->size); if (entry_size < sizeof(*entry)) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset + LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx << " is too small."; return false; } if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset + LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx << " is too large."; return false; } @@ -167,7 +205,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset if (entry_size < sizeof(ResTable_map_entry)) { // There needs to be room for one Res_value struct. if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) { - LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset + LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << entry_idx << " for type " << (int)type->id << "."; return false; } @@ -176,12 +214,12 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset reinterpret_cast(reinterpret_cast(entry) + entry_size); const size_t value_size = dtohs(value->size); if (value_size < sizeof(Res_value)) { - LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small."; + LOG(ERROR) << "Res_value at index " << entry_idx << " is too small."; return false; } if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) { - LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset + LOG(ERROR) << "Res_value size " << value_size << " at index " << entry_idx << " is too large."; return false; } @@ -190,76 +228,117 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t map_entry_count = dtohl(map->count); size_t map_entries_start = entry_offset + entry_size; if (map_entries_start & 0x03) { - LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset."; + LOG(ERROR) << "Map entries at index " << entry_idx << " start at unaligned offset."; return false; } // Each entry is sizeof(ResTable_map) big. if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) { - LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << "."; + LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << entry_idx << "."; return false; } } return true; } -const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk, - uint16_t entry_index) { - uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index); - if (entry_offset == ResTable_type::NO_ENTRY) { - return nullptr; - } - return GetEntryFromOffset(type_chunk, entry_offset); -} - -uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const size_t entry_count = dtohl(type_chunk->entryCount); - const size_t offsets_offset = dtohs(type_chunk->header.headerSize); +bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_idx, + const ResTable_config& config, FindEntryResult* out_entry) const { + const ResTable_config* best_config = nullptr; + const ResTable_type* best_type = nullptr; + uint32_t best_offset = 0; + + for (uint32_t i = 0; i < type_spec_ptr->type_count; i++) { + const Type* type = &type_spec_ptr->types[i]; + const ResTable_type* type_chunk = type->type; + + if (type->configuration.match(config) && + (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const size_t entry_count = dtohl(type_chunk->entryCount); + const size_t offsets_offset = dtohs(type_chunk->header.headerSize); + + // Check if there is the desired entry in this type. + + if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { + // This is encoded as a sparse map, so perform a binary search. + const ResTable_sparseTypeEntry* sparse_indices = + reinterpret_cast( + reinterpret_cast(type_chunk) + offsets_offset); + const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; + const ResTable_sparseTypeEntry* result = + std::lower_bound(sparse_indices, sparse_indices_end, entry_idx, + [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { + return dtohs(entry.idx) < entry_idx; + }); + + if (result == sparse_indices_end || dtohs(result->idx) != entry_idx) { + // No entry found. + continue; + } - // Check if there is the desired entry in this type. + // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as + // the real offset divided by 4. + best_offset = uint32_t{dtohs(result->offset)} * 4u; + } else { + if (entry_idx >= entry_count) { + // This entry cannot be here. + continue; + } - if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { - // This is encoded as a sparse map, so perform a binary search. - const ResTable_sparseTypeEntry* sparse_indices = - reinterpret_cast( + const uint32_t* entry_offsets = reinterpret_cast( reinterpret_cast(type_chunk) + offsets_offset); - const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; - const ResTable_sparseTypeEntry* result = - std::lower_bound(sparse_indices, sparse_indices_end, entry_index, - [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { - return dtohs(entry.idx) < entry_idx; - }); - - if (result == sparse_indices_end || dtohs(result->idx) != entry_index) { - // No entry found. - return ResTable_type::NO_ENTRY; + const uint32_t offset = dtohl(entry_offsets[entry_idx]); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + // There is an entry for this resource, record it. + best_offset = offset; + } + + best_config = &type->configuration; + best_type = type_chunk; } + } - // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as - // the real offset divided by 4. - return uint32_t{dtohs(result->offset)} * 4u; + if (best_type == nullptr) { + return false; } - // This type is encoded as a dense array. - if (entry_index >= entry_count) { - // This entry cannot be here. - return ResTable_type::NO_ENTRY; + if (UNLIKELY(!VerifyResTableEntry(best_type, best_offset, entry_idx))) { + return false; } - const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type_chunk) + offsets_offset); - return dtohl(entry_offsets[entry_index]); + const ResTable_entry* best_entry = reinterpret_cast( + reinterpret_cast(best_type) + best_offset + dtohl(best_type->entriesStart)); + + const uint32_t* flags = reinterpret_cast(type_spec_ptr->type_spec + 1); + out_entry->type_flags = dtohl(flags[entry_idx]); + out_entry->entry = best_entry; + out_entry->config = best_config; + out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1); + out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index)); + return true; } -const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk, - uint32_t offset) { - if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) { - return nullptr; +bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, + FindEntryResult* out_entry) const { + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; + if (UNLIKELY(ptr == nullptr)) { + return false; } - return reinterpret_cast(reinterpret_cast(type_chunk) + - offset + dtohl(type_chunk->entriesStart)); + + // If there is an IDMAP supplied with this package, translate the entry ID. + if (ptr->idmap_entries != nullptr) { + if (!LoadedIdmap::Lookup(ptr->idmap_entries, entry_idx, &entry_idx)) { + // There is no mapping, so the resource is not meant to be in this overlay package. + return false; + } + } + return FindEntry(ptr, entry_idx, config, out_entry); } void LoadedPackage::CollectConfigurations(bool exclude_mipmap, @@ -267,7 +346,7 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, const static std::u16string kMipMap = u"mipmap"; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const TypeSpecPtr& type_spec = type_specs_[i]; + const util::unique_cptr& type_spec = type_specs_[i]; if (type_spec != nullptr) { if (exclude_mipmap) { const int type_idx = type_spec->type_spec->id - 1; @@ -288,11 +367,8 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, } } - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config config; - config.copyFromDtoH((*iter)->config); - out_configs->insert(config); + for (size_t j = 0; j < type_spec->type_count; j++) { + out_configs->insert(type_spec->types[j].configuration); } } } @@ -302,12 +378,10 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set* out char temp_locale[RESTABLE_MAX_LOCALE_LEN]; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const TypeSpecPtr& type_spec = type_specs_[i]; + const util::unique_cptr& type_spec = type_specs_[i]; if (type_spec != nullptr) { - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config configuration; - configuration.copyFromDtoH((*iter)->config); + for (size_t j = 0; j < type_spec->type_count; j++) { + const ResTable_config& configuration = type_spec->types[j].configuration; if (configuration.locale != 0) { configuration.getBcp47Locale(temp_locale, canonicalize); std::string locale(temp_locale); @@ -335,17 +409,17 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - const ResTable_type* type = *iter; - size_t entry_count = dtohl(type->entryCount); + for (size_t ti = 0; ti < type_spec->type_count; ti++) { + const Type* type = &type_spec->types[ti]; + size_t entry_count = dtohl(type->type->entryCount); for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) { const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type) + dtohs(type->header.headerSize)); + reinterpret_cast(type->type) + dtohs(type->type->header.headerSize)); const uint32_t offset = dtohl(entry_offsets[entry_idx]); if (offset != ResTable_type::NO_ENTRY) { - const ResTable_entry* entry = reinterpret_cast( - reinterpret_cast(type) + dtohl(type->entriesStart) + offset); + const ResTable_entry* entry = + reinterpret_cast(reinterpret_cast(type->type) + + dtohl(type->type->entriesStart) + offset); if (dtohl(entry->key.index) == static_cast(key_idx)) { // The package ID will be overridden by the caller (due to runtime assignment of package // IDs for shared libraries). @@ -357,7 +431,8 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } -const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { +const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const { + const uint8_t package_id = get_package_id(resid); for (const auto& loaded_package : packages_) { if (loaded_package->GetPackageId() == package_id) { return loaded_package.get(); @@ -605,6 +680,26 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, return std::move(loaded_package); } +bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, + FindEntryResult* out_entry) const { + ATRACE_CALL(); + + const uint8_t package_id = get_package_id(resid); + const uint8_t type_id = get_type_id(resid); + const uint16_t entry_id = get_entry_id(resid); + + if (UNLIKELY(type_id == 0)) { + LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); + return false; + } + + for (const auto& loaded_package : packages_) { + if (loaded_package->GetPackageId() == package_id) { + return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry); + } + } + return false; +} bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library) { diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index ef08897d997a..b033137b4764 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -69,8 +69,6 @@ struct ResolvedBag { Entry entries[0]; }; -struct FindEntryResult; - // AssetManager2 is the main entry point for accessing assets and resources. // AssetManager2 provides caching of resources retrieved via the underlying ApkAssets. class AssetManager2 { @@ -129,7 +127,7 @@ class AssetManager2 { // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap' // will be excluded from the list. std::set GetResourceConfigurations(bool exclude_system = false, - bool exclude_mipmap = false) const; + bool exclude_mipmap = false); // Returns all the locales for which there are resources defined. This includes resource // locales in all the ApkAssets set for this AssetManager. @@ -138,24 +136,24 @@ class AssetManager2 { // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized // and de-duped in the resulting list. std::set GetResourceLocales(bool exclude_system = false, - bool merge_equivalent_languages = false) const; + bool merge_equivalent_languages = false); // Searches the set of APKs loaded by this AssetManager and opens the first one found located // in the assets/ directory. // `mode` controls how the file is opened. // // NOTE: The loaded APKs are searched in reverse order. - std::unique_ptr Open(const std::string& filename, Asset::AccessMode mode) const; + std::unique_ptr Open(const std::string& filename, Asset::AccessMode mode); // Opens a file within the assets/ directory of the APK specified by `cookie`. // `mode` controls how the file is opened. std::unique_ptr Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) const; + Asset::AccessMode mode); // Opens the directory specified by `dirname`. The result is an AssetDir that is the combination // of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded. // The entries are sorted by their ASCII name. - std::unique_ptr OpenDir(const std::string& dirname) const; + std::unique_ptr OpenDir(const std::string& dirname); // Searches the set of APKs loaded by this AssetManager and opens the first one found. // `mode` controls how the file is opened. @@ -163,24 +161,24 @@ class AssetManager2 { // // NOTE: The loaded APKs are searched in reverse order. std::unique_ptr OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie = nullptr) const; + ApkAssetsCookie* out_cookie = nullptr); // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened. // This is typically used to open a specific AndroidManifest.xml, or a binary XML file // referenced by a resource lookup with GetResource(). std::unique_ptr OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) const; + Asset::AccessMode mode); // Populates the `out_name` parameter with resource name information. // Utf8 strings are preferred, and only if they are unavailable are // the Utf16 variants populated. // Returns false if the resource was not found or the name was missing/corrupt. - bool GetResourceName(uint32_t resid, ResourceName* out_name) const; + bool GetResourceName(uint32_t resid, ResourceName* out_name); // Populates `out_flags` with the bitmask of configuration axis that this resource varies with. // See ResTable_config for the list of configuration axis. // Returns false if the resource was not found. - bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const; + bool GetResourceFlags(uint32_t resid, uint32_t* out_flags); // Finds the resource ID assigned to `resource_name`. // `resource_name` must be of the form '[package:][type/]entry'. @@ -188,7 +186,7 @@ class AssetManager2 { // If no type is specified in `resource_name`, then `fallback_type` is used as the type. // Returns 0x0 if no resource by that name was found. uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {}, - const std::string& fallback_package = {}) const; + const std::string& fallback_package = {}); // Retrieves the best matching resource with ID `resid`. The resource value is filled into // `out_value` and the configuration for the selected value is populated in `out_selected_config`. @@ -201,7 +199,7 @@ class AssetManager2 { // this function logs if the resource was a map/bag type before returning kInvalidCookie. ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) const; + uint32_t* out_flags); // Resolves the resource reference in `in_out_value` if the data type is // Res_value::TYPE_REFERENCE. @@ -217,7 +215,7 @@ class AssetManager2 { // it was not found. ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) const; + uint32_t* out_last_reference); // Retrieves the best matching bag/map resource with ID `resid`. // This method will resolve all parent references for this bag and merge keys with the child. @@ -235,9 +233,9 @@ class AssetManager2 { std::unique_ptr NewTheme(); template - void ForEachPackage(Func func) const { + void ForEachPackage(Func func) { for (const PackageGroup& package_group : package_groups_) { - func(package_group.packages_.front().loaded_package_->GetPackageName(), + func(package_group.packages_.front()->GetPackageName(), package_group.dynamic_ref_table.mAssignedPackageId); } } @@ -262,7 +260,7 @@ class AssetManager2 { // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds. ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, - FindEntryResult* out_entry) const; + FindEntryResult* out_entry); // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. @@ -272,43 +270,13 @@ class AssetManager2 { // bitmask `diff`. void InvalidateCaches(uint32_t diff); - // Triggers the re-construction of lists of types that match the set configuration. - // This should always be called when mutating the AssetManager's configuration or ApkAssets set. - void RebuildFilterList(); - // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector apk_assets_; - // A collection of configurations and their associated ResTable_type that match the current - // AssetManager configuration. - struct FilteredConfigGroup { - std::vector configurations; - std::vector types; - }; - - // Represents an single package. - struct ConfiguredPackage { - // A pointer to the immutable, loaded package info. - const LoadedPackage* loaded_package_; - - // A mutable AssetManager-specific list of configurations that match the AssetManager's - // current configuration. This is used as an optimization to avoid checking every single - // candidate configuration when looking up resources. - ByteBucketArray filtered_configs_; - }; - - // Represents a logical package, which can be made up of many individual packages. Each package - // in a PackageGroup shares the same package name and package ID. struct PackageGroup { - // The set of packages that make-up this group. - std::vector packages_; - - // The cookies associated with each package in the group. They share the same order as - // packages_. + std::vector packages_; std::vector cookies_; - - // A library reference table that contains build-package ID to runtime-package ID mappings. DynamicRefTable dynamic_ref_table; }; @@ -382,7 +350,7 @@ class Theme { ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config = nullptr, uint32_t* in_out_type_spec_flags = nullptr, - uint32_t* out_last_ref = nullptr) const; + uint32_t* out_last_ref = nullptr); private: DISALLOW_COPY_AND_ASSIGN(Theme); diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 35ae5fcd9e7b..1775f5070f4e 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -41,40 +41,33 @@ class DynamicPackageEntry { int package_id = 0; }; -// TypeSpec is going to be immediately proceeded by -// an array of Type structs, all in the same block of memory. -struct TypeSpec { - // Pointer to the mmapped data where flags are kept. - // Flags denote whether the resource entry is public - // and under which configurations it varies. - const ResTable_typeSpec* type_spec; - - // Pointer to the mmapped data where the IDMAP mappings for this type - // exist. May be nullptr if no IDMAP exists. - const IdmapEntry_header* idmap_entries; - - // The number of types that follow this struct. - // There is a type for each configuration that entries are defined for. - size_t type_count; - - // Trick to easily access a variable number of Type structs - // proceeding this struct, and to ensure their alignment. - const ResTable_type* types[0]; - - inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const { - if (entry_index >= dtohl(type_spec->entryCount)) { - return 0u; - } - - const uint32_t* flags = reinterpret_cast(type_spec + 1); - return flags[entry_index]; - } +struct FindEntryResult { + // A pointer to the resource table entry for this resource. + // If the size of the entry is > sizeof(ResTable_entry), it can be cast to + // a ResTable_map_entry and processed as a bag/map. + const ResTable_entry* entry; + + // The configuration for which the resulting entry was defined. This points to a structure that + // is already swapped to host endianness. + const ResTable_config* config; + + // The bitmask of configuration axis with which the resource value varies. + uint32_t type_flags; + + // The dynamic package ID map for the package from which this resource came from. + const DynamicRefTable* dynamic_ref_table; + + // The string pool reference to the type's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef type_string_ref; + + // The string pool reference to the entry's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef entry_string_ref; }; -// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of -// ResTable_type pointers. -// TypeSpecPtr is a managed pointer that knows how to delete itself. -using TypeSpecPtr = util::unique_cptr; +struct TypeSpec; +class LoadedArsc; class LoadedPackage { public: @@ -84,6 +77,9 @@ class LoadedPackage { ~LoadedPackage(); + bool FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, + FindEntryResult* out_entry) const; + // Finds the entry with the specified type name and entry name. The names are in UTF-16 because // the underlying ResStringPool API expects this. For now this is acceptable, but since // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. @@ -91,12 +87,6 @@ class LoadedPackage { // for patching the correct package ID to the resource ID. uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; - static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index); - - static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index); - - static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset); - // Returns the string pool where type names are stored. inline const ResStringPool* GetTypeStringPool() const { return &type_string_pool_; @@ -146,32 +136,14 @@ class LoadedPackage { // before being inserted into the set. This may cause some equivalent locales to de-dupe. void CollectLocales(bool canonicalize, std::set* out_locales) const; - // type_idx is TT - 1 from 0xPPTTEEEE. - inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const { - // If the type IDs are offset in this package, we need to take that into account when searching - // for a type. - return type_specs_[type_index - type_id_offset_].get(); - } - - template - void ForEachTypeSpec(Func f) const { - for (size_t i = 0; i < type_specs_.size(); i++) { - const TypeSpecPtr& ptr = type_specs_[i]; - if (ptr != nullptr) { - uint8_t type_id = ptr->type_spec->id; - if (ptr->idmap_entries != nullptr) { - type_id = ptr->idmap_entries->target_type_id; - } - f(ptr.get(), type_id - 1); - } - } - } - private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); LoadedPackage(); + bool FindEntry(const util::unique_cptr& type_spec_ptr, uint16_t entry_idx, + const ResTable_config& config, FindEntryResult* out_entry) const; + ResStringPool type_string_pool_; ResStringPool key_string_pool_; std::string package_name_; @@ -181,7 +153,7 @@ class LoadedPackage { bool system_ = false; bool overlay_ = false; - ByteBucketArray type_specs_; + ByteBucketArray> type_specs_; std::vector dynamic_package_map_; }; @@ -209,20 +181,25 @@ class LoadedArsc { return &global_string_pool_; } - // Gets a pointer to the package with the specified package ID, or nullptr if no such package - // exists. - const LoadedPackage* GetPackageById(uint8_t package_id) const; + // Finds the resource with ID `resid` with the best value for configuration `config`. + // The parameter `out_entry` will be filled with the resulting resource entry. + // The resource entry can be a simple entry (ResTable_entry) or a complex bag + // (ResTable_entry_map). + bool FindEntry(uint32_t resid, const ResTable_config& config, FindEntryResult* out_entry) const; - // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. - inline const std::vector>& GetPackages() const { - return packages_; - } + // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist. + const LoadedPackage* GetPackageForId(uint32_t resid) const; // Returns true if this is a system provided resource. inline bool IsSystem() const { return system_; } + // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. + inline const std::vector>& GetPackages() const { + return packages_; + } + private: DISALLOW_COPY_AND_ASSIGN(LoadedArsc); diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index e2b9f0040989..6c43a67e602f 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -26,56 +26,58 @@ using ::android::base::unique_fd; using ::com::android::basic::R; -using ::testing::Eq; -using ::testing::Ge; -using ::testing::NotNull; -using ::testing::SizeIs; -using ::testing::StrEq; namespace android { TEST(ApkAssetsTest, LoadApk) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); - ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); + ASSERT_NE(nullptr, loaded_arsc); + + const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); + ASSERT_NE(nullptr, loaded_package); + + std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); + ASSERT_NE(nullptr, asset); } TEST(ApkAssetsTest, LoadApkFromFd) { const std::string path = GetTestDataPath() + "/basic/basic.apk"; unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY)); - ASSERT_THAT(fd.get(), Ge(0)); + ASSERT_GE(fd.get(), 0); std::unique_ptr loaded_apk = ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); - ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); + ASSERT_NE(nullptr, loaded_arsc); + + const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); + ASSERT_NE(nullptr, loaded_package); + + std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); + ASSERT_NE(nullptr, asset); } TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_THAT(loaded_apk, NotNull()); - + ASSERT_NE(nullptr, loaded_apk); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); + ASSERT_NE(nullptr, loaded_arsc); + ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic()); loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); + ASSERT_NE(nullptr, loaded_arsc); + ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic()); } @@ -84,22 +86,19 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { ResTable target_table; const std::string target_path = GetTestDataPath() + "/basic/basic.apk"; ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents)); - ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/), - Eq(NO_ERROR)); + ASSERT_EQ(NO_ERROR, target_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); ResTable overlay_table; const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk"; ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents)); - ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/), - Eq(NO_ERROR)); + ASSERT_EQ(NO_ERROR, overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); util::unique_cptr idmap_data; void* temp_data; size_t idmap_len; - ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), - overlay_path.c_str(), &temp_data, &idmap_len), - Eq(NO_ERROR)); + ASSERT_EQ(NO_ERROR, target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), + overlay_path.c_str(), &temp_data, &idmap_len)); idmap_data.reset(temp_data); TemporaryFile tf; @@ -109,30 +108,37 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { // Open something so that the destructor of TemporaryFile closes a valid fd. tf.fd = open("/dev/null", O_WRONLY); - ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull()); + std::unique_ptr loaded_overlay_apk = ApkAssets::LoadOverlay(tf.path); + ASSERT_NE(nullptr, loaded_overlay_apk); } TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); - { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } + { + std::unique_ptr assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); + ASSERT_NE(nullptr, assets); + } - { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } + { + std::unique_ptr assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); + ASSERT_NE(nullptr, assets); + } } TEST(ApkAssetsTest, OpenUncompressedAssetFd) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN); - ASSERT_THAT(asset, NotNull()); + ASSERT_NE(nullptr, asset); off64_t start, length; unique_fd fd(asset->openFileDescriptor(&start, &length)); - ASSERT_THAT(fd.get(), Ge(0)); + EXPECT_GE(fd.get(), 0); lseek64(fd.get(), start, SEEK_SET); @@ -140,7 +146,7 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) { buffer.resize(length); ASSERT_TRUE(base::ReadFully(fd.get(), &*buffer.begin(), length)); - EXPECT_THAT(buffer, StrEq("This should be uncompressed.\n\n")); + EXPECT_EQ("This should be uncompressed.\n\n", buffer); } } // namespace android diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index bedebd66cb2f..37ddafb14fd3 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -16,8 +16,6 @@ #include "androidfw/LoadedArsc.h" -#include "androidfw/ResourceUtils.h" - #include "TestHelpers.h" #include "data/basic/R.h" #include "data/libclient/R.h" @@ -29,13 +27,6 @@ namespace basic = com::android::basic; namespace libclient = com::android::libclient; namespace sparse = com::android::sparse; -using ::testing::Eq; -using ::testing::Ge; -using ::testing::IsNull; -using ::testing::NotNull; -using ::testing::SizeIs; -using ::testing::StrEq; - namespace android { TEST(LoadedArscTest, LoadSinglePackageArsc) { @@ -44,24 +35,39 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); + + const std::vector>& packages = loaded_arsc->GetPackages(); + ASSERT_EQ(1u, packages.size()); + EXPECT_EQ(std::string("com.android.app"), packages[0]->GetPackageName()); + EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.sdkVersion = 24; + + FindEntryResult entry; - const LoadedPackage* package = - loaded_arsc->GetPackageById(get_package_id(app::R::string::string_one)); - ASSERT_THAT(package, NotNull()); - EXPECT_THAT(package->GetPackageName(), StrEq("com.android.app")); - EXPECT_THAT(package->GetPackageId(), Eq(0x7f)); + ASSERT_TRUE(loaded_arsc->FindEntry(app::R::string::string_one, config, &entry)); + ASSERT_NE(nullptr, entry.entry); +} - const uint8_t type_index = get_type_id(app::R::string::string_one) - 1; - const uint16_t entry_index = get_entry_id(app::R::string::string_one); +TEST(LoadedArscTest, FindDefaultEntry) { + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + ASSERT_NE(nullptr, loaded_arsc); - const ResTable_type* type = type_spec->types[0]; - ASSERT_THAT(type, NotNull()); - ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + desired_config.language[0] = 'd'; + desired_config.language[1] = 'e'; + + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry)); + ASSERT_NE(nullptr, entry.entry); } TEST(LoadedArscTest, LoadSparseEntryApp) { @@ -70,22 +76,15 @@ TEST(LoadedArscTest, LoadSparseEntryApp) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); - - const LoadedPackage* package = - loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9)); - ASSERT_THAT(package, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); - const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1; - const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9); + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.sdkVersion = 26; - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - - const ResTable_type* type = type_spec->types[0]; - ASSERT_THAT(type, NotNull()); - ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(sparse::R::integer::foo_9, config, &entry)); + ASSERT_NE(nullptr, entry.entry); } TEST(LoadedArscTest, LoadSharedLibrary) { @@ -94,13 +93,14 @@ TEST(LoadedArscTest, LoadSharedLibrary) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_THAT(packages, SizeIs(1u)); + ASSERT_EQ(1u, packages.size()); + EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.lib_one")); - EXPECT_THAT(packages[0]->GetPackageId(), Eq(0)); + EXPECT_EQ(std::string("com.android.lib_one"), packages[0]->GetPackageName()); + EXPECT_EQ(0, packages[0]->GetPackageId()); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); @@ -114,23 +114,25 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) { "resources.arsc", &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_THAT(packages, SizeIs(1u)); + ASSERT_EQ(1u, packages.size()); + EXPECT_FALSE(packages[0]->IsDynamic()); - EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.libclient")); - EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); + EXPECT_EQ(std::string("com.android.libclient"), packages[0]->GetPackageName()); + EXPECT_EQ(0x7f, packages[0]->GetPackageId()); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); // The library has two dependencies. - ASSERT_THAT(dynamic_pkg_map, SizeIs(2u)); - EXPECT_THAT(dynamic_pkg_map[0].package_name, StrEq("com.android.lib_one")); - EXPECT_THAT(dynamic_pkg_map[0].package_id, Eq(0x02)); + ASSERT_EQ(2u, dynamic_pkg_map.size()); - EXPECT_THAT(dynamic_pkg_map[1].package_name, StrEq("com.android.lib_two")); - EXPECT_THAT(dynamic_pkg_map[1].package_id, Eq(0x03)); + EXPECT_EQ(std::string("com.android.lib_one"), dynamic_pkg_map[0].package_name); + EXPECT_EQ(0x02, dynamic_pkg_map[0].package_id); + + EXPECT_EQ(std::string("com.android.lib_two"), dynamic_pkg_map[1].package_name); + EXPECT_EQ(0x03, dynamic_pkg_map[1].package_id); } TEST(LoadedArscTest, LoadAppAsSharedLibrary) { @@ -141,12 +143,13 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, true /*load_as_shared_library*/); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_THAT(packages, SizeIs(1u)); + ASSERT_EQ(1u, packages.size()); + EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); + EXPECT_EQ(0x7f, packages[0]->GetPackageId()); } TEST(LoadedArscTest, LoadFeatureSplit) { @@ -154,27 +157,21 @@ TEST(LoadedArscTest, LoadFeatureSplit) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc", &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); - const LoadedPackage* package = - loaded_arsc->GetPackageById(get_package_id(basic::R::string::test3)); - ASSERT_THAT(package, NotNull()); + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); - uint8_t type_index = get_type_id(basic::R::string::test3) - 1; - uint8_t entry_index = get_entry_id(basic::R::string::test3); - - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry)); size_t len; - const char16_t* type_name16 = - package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len); - ASSERT_THAT(type_name16, NotNull()); - EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string")); + const char16_t* type_name16 = entry.type_string_ref.string16(&len); + ASSERT_NE(nullptr, type_name16); + ASSERT_NE(0u, len); - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull()); + std::string type_name = util::Utf16ToUtf8(StringPiece16(type_name16, len)); + EXPECT_EQ(std::string("string"), type_name); } class MockLoadedIdmap : public LoadedIdmap { @@ -202,33 +199,23 @@ class MockLoadedIdmap : public LoadedIdmap { }; TEST(LoadedArscTest, LoadOverlay) { - std::string contents; + std::string contents, overlay_contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc", - &contents)); + &overlay_contents)); MockLoadedIdmap loaded_idmap; std::unique_ptr loaded_arsc = - LoadedArsc::Load(StringPiece(contents), &loaded_idmap); - ASSERT_THAT(loaded_arsc, NotNull()); - - const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u); - ASSERT_THAT(package, NotNull()); - - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); - - // The entry being overlaid doesn't exist at the original entry index. - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull()); - - // Since this is an overlay, the actual entry ID must be mapped. - ASSERT_THAT(type_spec->idmap_entries, NotNull()); - uint16_t target_entry_id = 0u; - ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id)); - ASSERT_THAT(target_entry_id, Eq(0x0u)); - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull()); + LoadedArsc::Load(StringPiece(overlay_contents), &loaded_idmap); + ASSERT_NE(nullptr, loaded_arsc); + + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(0x08030001u, desired_config, &entry)); } // structs with size fields (like Res_value, ResTable_entry) should be diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h index df0c642f4565..43a995536d89 100644 --- a/libs/androidfw/tests/TestHelpers.h +++ b/libs/androidfw/tests/TestHelpers.h @@ -20,7 +20,6 @@ #include #include "androidfw/ResourceTypes.h" -#include "gmock/gmock.h" #include "gtest/gtest.h" #include "CommonHelpers.h" -- cgit v1.2.3-59-g8ed1b From f7d01dd7e14e01420c39e7fb8eca3cfa0f5f31b6 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Thu, 25 Jan 2018 15:38:58 -0800 Subject: Revert "Replace AssetManager with AssetManager2 implementation" This reverts commit dcb3c6559b09ec89771858ec27a787027da9af50. Bug:72511998 Change-Id: I665966ca109f66f85d8665db388c71ea2303c3b8 --- core/java/android/content/pm/PackageParser.java | 19 +- core/java/android/content/res/ApkAssets.java | 221 -- core/java/android/content/res/AssetManager.java | 1321 ++++----- core/java/android/content/res/Resources.java | 9 +- core/java/android/content/res/ResourcesImpl.java | 22 +- core/java/android/content/res/TypedArray.java | 185 +- core/java/android/content/res/XmlBlock.java | 8 +- core/jni/Android.bp | 3 +- core/jni/AndroidRuntime.cpp | 2 - core/jni/android/graphics/FontFamily.cpp | 29 +- core/jni/android_app_NativeActivity.cpp | 2 +- core/jni/android_content_res_ApkAssets.cpp | 150 - core/jni/android_util_AssetManager.cpp | 2892 +++++++++++--------- .../android_runtime/android_util_AssetManager.h | 15 +- libs/androidfw/AssetManager2.cpp | 6 +- libs/androidfw/AttributeResolution.cpp | 263 +- libs/androidfw/LoadedArsc.cpp | 2 + libs/androidfw/include/androidfw/AttributeFinder.h | 6 - .../include/androidfw/AttributeResolution.h | 11 +- libs/androidfw/include/androidfw/LoadedArsc.h | 13 +- libs/androidfw/include/androidfw/MutexGuard.h | 101 - libs/androidfw/tests/AttributeResolution_test.cpp | 39 +- libs/androidfw/tests/BenchmarkHelpers.cpp | 4 +- native/android/asset_manager.cpp | 28 +- rs/jni/android_renderscript_RenderScript.cpp | 30 +- 25 files changed, 2501 insertions(+), 2880 deletions(-) delete mode 100644 core/java/android/content/res/ApkAssets.java delete mode 100644 core/jni/android_content_res_ApkAssets.cpp delete mode 100644 libs/androidfw/include/androidfw/MutexGuard.h (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index d5c88aa1fbcb..3d26af1f15bb 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -54,7 +54,6 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper; import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.SplitAssetDependencyLoader; import android.content.pm.split.SplitAssetLoader; -import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -1595,19 +1594,21 @@ public class PackageParser { int flags) throws PackageParserException { final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); - ApkAssets apkAssets = null; + AssetManager assets = null; XmlResourceParser parser = null; try { - try { - apkAssets = fd != null - ? ApkAssets.loadFromFd(fd, debugPathName, false, false) - : ApkAssets.loadFromPath(apkPath); - } catch (IOException e) { + assets = newConfiguredAssetManager(); + int cookie = fd != null + ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath); + if (cookie == 0) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse " + apkPath); } - parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME); + final DisplayMetrics metrics = new DisplayMetrics(); + metrics.setToDefaults(); + + parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); final SigningDetails signingDetails; if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) { @@ -1634,7 +1635,7 @@ public class PackageParser { "Failed to parse " + apkPath, e); } finally { IoUtils.closeQuietly(parser); - IoUtils.closeQuietly(apkAssets); + IoUtils.closeQuietly(assets); } } diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java deleted file mode 100644 index b087c48d8d4c..000000000000 --- a/core/java/android/content/res/ApkAssets.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.content.res; - -import android.annotation.NonNull; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.Preconditions; - -import java.io.FileDescriptor; -import java.io.IOException; - -/** - * The loaded, immutable, in-memory representation of an APK. - * - * The main implementation is native C++ and there is very little API surface exposed here. The APK - * is mainly accessed via {@link AssetManager}. - * - * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers, - * making the creation of AssetManagers very cheap. - * @hide - */ -public final class ApkAssets implements AutoCloseable { - @GuardedBy("this") private long mNativePtr; - @GuardedBy("this") private StringBlock mStringBlock; - - /** - * Creates a new ApkAssets instance from the given path on disk. - * - * @param path The path to an APK on disk. - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException { - return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/); - } - - /** - * Creates a new ApkAssets instance from the given path on disk. - * - * @param path The path to an APK on disk. - * @param system When true, the APK is loaded as a system APK (framework). - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system) - throws IOException { - return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/); - } - - /** - * Creates a new ApkAssets instance from the given path on disk. - * - * @param path The path to an APK on disk. - * @param system When true, the APK is loaded as a system APK (framework). - * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are - * loaded as a shared library. - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system, - boolean forceSharedLibrary) throws IOException { - return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/); - } - - /** - * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications. - * - * Performs a dup of the underlying fd, so you must take care of still closing - * the FileDescriptor yourself (and can do that whenever you want). - * - * @param fd The FileDescriptor of an open, readable APK. - * @param friendlyName The friendly name used to identify this ApkAssets when logging. - * @param system When true, the APK is loaded as a system APK (framework). - * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are - * loaded as a shared library. - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd, - @NonNull String friendlyName, boolean system, boolean forceSharedLibrary) - throws IOException { - return new ApkAssets(fd, friendlyName, system, forceSharedLibrary); - } - - /** - * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path - * is encoded within the IDMAP. - * - * @param idmapPath Path to the IDMAP of an overlay APK. - * @param system When true, the APK is loaded as a system APK (framework). - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system) - throws IOException { - return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/); - } - - private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) - throws IOException { - Preconditions.checkNotNull(path, "path"); - mNativePtr = nativeLoad(path, system, forceSharedLib, overlay); - mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); - } - - private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system, - boolean forceSharedLib) throws IOException { - Preconditions.checkNotNull(fd, "fd"); - Preconditions.checkNotNull(friendlyName, "friendlyName"); - mNativePtr = nativeLoadFromFd(fd, friendlyName, system, forceSharedLib); - mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); - } - - @NonNull String getAssetPath() { - synchronized (this) { - ensureValidLocked(); - return nativeGetAssetPath(mNativePtr); - } - } - - CharSequence getStringFromPool(int idx) { - synchronized (this) { - ensureValidLocked(); - return mStringBlock.get(idx); - } - } - - /** - * Retrieve a parser for a compiled XML file. This is associated with a single APK and - * NOT a full AssetManager. This means that shared-library references will not be - * dynamically assigned runtime package IDs. - * - * @param fileName The path to the file within the APK. - * @return An XmlResourceParser. - * @throws IOException if the file was not found or an error occurred retrieving it. - */ - public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException { - Preconditions.checkNotNull(fileName, "fileName"); - synchronized (this) { - ensureValidLocked(); - long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName); - try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) { - XmlResourceParser parser = block.newParser(); - // If nativeOpenXml doesn't throw, it will always return a valid native pointer, - // which makes newParser always return non-null. But let's be paranoid. - if (parser == null) { - throw new AssertionError("block.newParser() returned a null parser"); - } - return parser; - } - } - } - - /** - * Returns false if the underlying APK was changed since this ApkAssets was loaded. - */ - public boolean isUpToDate() { - synchronized (this) { - ensureValidLocked(); - return nativeIsUpToDate(mNativePtr); - } - } - - /** - * Closes the ApkAssets and destroys the underlying native implementation. Further use of the - * ApkAssets object will cause exceptions to be thrown. - * - * Calling close on an already closed ApkAssets does nothing. - */ - @Override - public void close() { - synchronized (this) { - if (mNativePtr == 0) { - return; - } - - mStringBlock = null; - nativeDestroy(mNativePtr); - mNativePtr = 0; - } - } - - @Override - protected void finalize() throws Throwable { - if (mNativePtr != 0) { - nativeDestroy(mNativePtr); - } - } - - private void ensureValidLocked() { - if (mNativePtr == 0) { - throw new RuntimeException("ApkAssets is closed"); - } - } - - private static native long nativeLoad( - @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) - throws IOException; - private static native long nativeLoadFromFd(@NonNull FileDescriptor fd, - @NonNull String friendlyName, boolean system, boolean forceSharedLib) - throws IOException; - private static native void nativeDestroy(long ptr); - private static native @NonNull String nativeGetAssetPath(long ptr); - private static native long nativeGetStringBlock(long ptr); - private static native boolean nativeIsUpToDate(long ptr); - private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException; -} diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 78370f4d56a8..78665609bdd4 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -18,11 +18,9 @@ package android.content.res; import android.annotation.AnyRes; import android.annotation.ArrayRes; -import android.annotation.AttrRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; -import android.annotation.StyleRes; import android.content.pm.ActivityInfo; import android.content.res.Configuration.NativeConfig; import android.os.ParcelFileDescriptor; @@ -30,18 +28,10 @@ import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.Preconditions; - -import java.io.BufferedReader; -import java.io.FileInputStream; +import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.channels.FileLock; -import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; /** @@ -52,17 +42,7 @@ import java.util.HashMap; * bytes. */ public final class AssetManager implements AutoCloseable { - private static final String TAG = "AssetManager"; - private static final boolean DEBUG_REFS = false; - - private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk"; - - private static final Object sSync = new Object(); - - // Not private for LayoutLib's BridgeAssetManager. - @GuardedBy("sSync") static AssetManager sSystem = null; - - @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0]; + /* modes used when opening an asset */ /** * Mode for {@link #open(String, int)}: no specific information about how @@ -85,279 +65,87 @@ public final class AssetManager implements AutoCloseable { */ public static final int ACCESS_BUFFER = 3; - @GuardedBy("this") private final TypedValue mValue = new TypedValue(); - @GuardedBy("this") private final long[] mOffsets = new long[2]; - - // Pointer to native implementation, stuffed inside a long. - @GuardedBy("this") private long mObject; - - // The loaded asset paths. - @GuardedBy("this") private ApkAssets[] mApkAssets; + private static final String TAG = "AssetManager"; + private static final boolean localLOGV = false || false; + + private static final boolean DEBUG_REFS = false; + + private static final Object sSync = new Object(); + /*package*/ static AssetManager sSystem = null; - // Debug/reference counting implementation. - @GuardedBy("this") private boolean mOpen = true; - @GuardedBy("this") private int mNumRefs = 1; - @GuardedBy("this") private HashMap mRefStacks; + private final TypedValue mValue = new TypedValue(); + private final long[] mOffsets = new long[2]; + + // For communication with native code. + private long mObject; + private StringBlock mStringBlocks[] = null; + + private int mNumRefs = 1; + private boolean mOpen = true; + private HashMap mRefStacks; + /** * Create a new AssetManager containing only the basic system assets. * Applications will not generally use this method, instead retrieving the * appropriate asset manager with {@link Resources#getAssets}. Not for * use by applications. - * @hide + * {@hide} */ public AssetManager() { - final ApkAssets[] assets; - synchronized (sSync) { - createSystemAssetsInZygoteLocked(); - assets = sSystemApkAssets; - } - - mObject = nativeCreate(); - if (DEBUG_REFS) { - mNumRefs = 0; - incRefsLocked(hashCode()); + synchronized (this) { + if (DEBUG_REFS) { + mNumRefs = 0; + incRefsLocked(this.hashCode()); + } + init(false); + if (localLOGV) Log.v(TAG, "New asset manager: " + this); + ensureSystemAssets(); } - - // Always set the framework resources. - setApkAssets(assets, false /*invalidateCaches*/); } - /** - * Private constructor that doesn't call ensureSystemAssets. - * Used for the creation of system assets. - */ - @SuppressWarnings("unused") - private AssetManager(boolean sentinel) { - mObject = nativeCreate(); - if (DEBUG_REFS) { - mNumRefs = 0; - incRefsLocked(hashCode()); + private static void ensureSystemAssets() { + synchronized (sSync) { + if (sSystem == null) { + AssetManager system = new AssetManager(true); + system.makeStringBlocks(null); + sSystem = system; + } } } - - /** - * This must be called from Zygote so that system assets are shared by all applications. - * @hide - */ - private static void createSystemAssetsInZygoteLocked() { - if (sSystem != null) { - return; - } - - // Make sure that all IDMAPs are up to date. - nativeVerifySystemIdmaps(); - - try { - ArrayList apkAssets = new ArrayList<>(); - apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/)); - - // Load all static RROs. - try (FileInputStream fis = new FileInputStream( - "/data/resource-cache/overlays.list"); - BufferedReader br = new BufferedReader(new InputStreamReader(fis))) { - // Acquire a lock so that any idmap scanning doesn't impact the current set. - try (FileLock flock = fis.getChannel().lock(0, Long.MAX_VALUE, - true /*shared*/)) { - for (String line; (line = br.readLine()) != null; ) { - String idmapPath = line.split(" ")[1]; - apkAssets.add( - ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/)); - } - } + + private AssetManager(boolean isSystem) { + if (DEBUG_REFS) { + synchronized (this) { + mNumRefs = 0; + incRefsLocked(this.hashCode()); } - - sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]); - sSystem = new AssetManager(true /*sentinel*/); - sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/); - } catch (IOException e) { - throw new IllegalStateException("Failed to create system AssetManager", e); } + init(true); + if (localLOGV) Log.v(TAG, "New asset manager: " + this); } /** * Return a global shared asset manager that provides access to only * system assets (no application assets). - * @hide + * {@hide} */ public static AssetManager getSystem() { - synchronized (sSync) { - createSystemAssetsInZygoteLocked(); - return sSystem; - } + ensureSystemAssets(); + return sSystem; } /** * Close this asset manager. */ - @Override public void close() { - synchronized (this) { - if (!mOpen) { - return; - } - - mOpen = false; - decRefsLocked(hashCode()); - } - } - - /** - * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)} - * family of methods. - * - * @param apkAssets The new set of paths. - * @param invalidateCaches Whether to invalidate any caches. This should almost always be true. - * Set this to false if you are appending new resources - * (not new configurations). - * @hide - */ - public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) { - Preconditions.checkNotNull(apkAssets, "apkAssets"); - synchronized (this) { - ensureValidLocked(); - mApkAssets = apkAssets; - nativeSetApkAssets(mObject, apkAssets, invalidateCaches); - if (invalidateCaches) { - // Invalidate all caches. - invalidateCachesLocked(-1); - } - } - } - - /** - * Invalidates the caches in this AssetManager according to the bitmask `diff`. - * - * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}. - * @see ActivityInfo.Config - */ - private void invalidateCachesLocked(int diff) { - // TODO(adamlesinski): Currently there are no caches to invalidate in Java code. - } - - /** - * @hide - */ - public @NonNull ApkAssets[] getApkAssets() { - synchronized (this) { - ensureValidLocked(); - return mApkAssets; - } - } - - /** - * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} - * @hide - */ - @Deprecated - public int addAssetPath(String path) { - return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/); - } - - /** - * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} - * @hide - */ - @Deprecated - public int addAssetPathAsSharedLibrary(String path) { - return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/); - } - - /** - * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} - * @hide - */ - @Deprecated - public int addOverlayPath(String path) { - return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/); - } - - private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) { - Preconditions.checkNotNull(path, "path"); - synchronized (this) { - ensureOpenLocked(); - final int count = mApkAssets.length; - for (int i = 0; i < count; i++) { - if (mApkAssets[i].getAssetPath().equals(path)) { - return i + 1; - } - } - - final ApkAssets assets; - try { - if (overlay) { - // TODO(b/70343104): This hardcoded path will be removed once - // addAssetPathInternal is deleted. - final String idmapPath = "/data/resource-cache/" - + path.substring(1).replace('/', '@') - + "@idmap"; - assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/); - } else { - assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib); - } - } catch (IOException e) { - return 0; + synchronized(this) { + //System.out.println("Release: num=" + mNumRefs + // + ", released=" + mReleased); + if (mOpen) { + mOpen = false; + decRefsLocked(this.hashCode()); } - - final ApkAssets[] newApkAssets = Arrays.copyOf(mApkAssets, count + 1); - newApkAssets[count] = assets; - setApkAssets(newApkAssets, true); - return count + 1; - } - } - - /** - * Ensures that the native implementation has not been destroyed. - * The AssetManager may have been closed, but references to it still exist - * and therefore the native implementation is not destroyed. - */ - private void ensureValidLocked() { - if (mObject == 0) { - throw new RuntimeException("AssetManager has been destroyed"); - } - } - - /** - * Ensures that the AssetManager has not been explicitly closed. If this method passes, - * then this implies that ensureValidLocked() also passes. - */ - private void ensureOpenLocked() { - if (!mOpen) { - throw new RuntimeException("AssetManager has been closed"); - } - } - - /** - * Populates {@code outValue} with the data associated a particular - * resource identifier for the current configuration. - * - * @param resId the resource identifier to load - * @param densityDpi the density bucket for which to load the resource - * @param outValue the typed value in which to put the data - * @param resolveRefs {@code true} to resolve references, {@code false} - * to leave them unresolved - * @return {@code true} if the data was loaded into {@code outValue}, - * {@code false} otherwise - */ - boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, - boolean resolveRefs) { - Preconditions.checkNotNull(outValue, "outValue"); - synchronized (this) { - ensureValidLocked(); - final int cookie = nativeGetResourceValue( - mObject, resId, (short) densityDpi, outValue, resolveRefs); - if (cookie <= 0) { - return false; - } - - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); - - if (outValue.type == TypedValue.TYPE_STRING) { - outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); - } - return true; } } @@ -368,7 +156,8 @@ public final class AssetManager implements AutoCloseable { * @param resId the resource identifier to load * @return the string value, or {@code null} */ - @Nullable CharSequence getResourceText(@StringRes int resId) { + @Nullable + final CharSequence getResourceText(@StringRes int resId) { synchronized (this) { final TypedValue outValue = mValue; if (getResourceValue(resId, 0, outValue, true)) { @@ -383,15 +172,15 @@ public final class AssetManager implements AutoCloseable { * identifier for the current configuration. * * @param resId the resource identifier to load - * @param bagEntryId the index into the bag to load + * @param bagEntryId * @return the string value, or {@code null} */ - @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { + @Nullable + final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { synchronized (this) { - ensureValidLocked(); final TypedValue outValue = mValue; - final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue); - if (cookie <= 0) { + final int block = loadResourceBagValue(resId, bagEntryId, outValue, true); + if (block < 0) { return null; } @@ -400,60 +189,52 @@ public final class AssetManager implements AutoCloseable { outValue.changingConfigurations); if (outValue.type == TypedValue.TYPE_STRING) { - return mApkAssets[cookie - 1].getStringFromPool(outValue.data); + return mStringBlocks[block].get(outValue.data); } return outValue.coerceToString(); } } - int getResourceArraySize(@ArrayRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceArraySize(mObject, resId); - } - } - /** - * Populates `outData` with array elements of `resId`. `outData` is normally - * used with - * {@link TypedArray}. - * - * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES} - * long, - * with the indices of the data representing the type, value, asset cookie, - * resource ID, - * configuration change mask, and density of the element. - * - * @param resId The resource ID of an array resource. - * @param outData The array to populate with data. - * @return The length of the array. + * Retrieves the string array associated with a particular resource + * identifier for the current configuration. * - * @see TypedArray#STYLE_TYPE - * @see TypedArray#STYLE_DATA - * @see TypedArray#STYLE_ASSET_COOKIE - * @see TypedArray#STYLE_RESOURCE_ID - * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS - * @see TypedArray#STYLE_DENSITY + * @param resId the resource identifier of the string array + * @return the string array, or {@code null} */ - int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) { - Preconditions.checkNotNull(outData, "outData"); - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceArray(mObject, resId, outData); - } + @Nullable + final String[] getResourceStringArray(@ArrayRes int resId) { + return getArrayStringResource(resId); } /** - * Retrieves the string array associated with a particular resource - * identifier for the current configuration. + * Populates {@code outValue} with the data associated a particular + * resource identifier for the current configuration. * - * @param resId the resource identifier of the string array - * @return the string array, or {@code null} + * @param resId the resource identifier to load + * @param densityDpi the density bucket for which to load the resource + * @param outValue the typed value in which to put the data + * @param resolveRefs {@code true} to resolve references, {@code false} + * to leave them unresolved + * @return {@code true} if the data was loaded into {@code outValue}, + * {@code false} otherwise */ - @Nullable String[] getResourceStringArray(@ArrayRes int resId) { + final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, + boolean resolveRefs) { synchronized (this) { - ensureValidLocked(); - return nativeGetResourceStringArray(mObject, resId); + final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs); + if (block < 0) { + return false; + } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mStringBlocks[block].get(outValue.data); + } + return true; } } @@ -463,48 +244,26 @@ public final class AssetManager implements AutoCloseable { * * @param resId the resource id of the string array */ - @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { + final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { synchronized (this) { - ensureValidLocked(); - final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId); + final int[] rawInfoArray = getArrayStringInfo(resId); if (rawInfoArray == null) { return null; } - final int rawInfoArrayLen = rawInfoArray.length; final int infoArrayLen = rawInfoArrayLen / 2; + int block; + int index; final CharSequence[] retArray = new CharSequence[infoArrayLen]; for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) { - int cookie = rawInfoArray[i]; - int index = rawInfoArray[i + 1]; - retArray[j] = (index >= 0 && cookie > 0) - ? mApkAssets[cookie - 1].getStringFromPool(index) : null; + block = rawInfoArray[i]; + index = rawInfoArray[i + 1]; + retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null; } return retArray; } } - @Nullable int[] getResourceIntArray(@ArrayRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceIntArray(mObject, resId); - } - } - - /** - * Get the attributes for a style resource. These are the <item> - * elements in - * a <style> resource. - * @param resId The resource ID of the style - * @return An array of attribute IDs. - */ - @AttrRes int[] getStyleAttributes(@StyleRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetStyleAttributes(mObject, resId); - } - } - /** * Populates {@code outValue} with the data associated with a particular * resource identifier for the current configuration. Resolves theme @@ -518,88 +277,73 @@ public final class AssetManager implements AutoCloseable { * @return {@code true} if the data was loaded into {@code outValue}, * {@code false} otherwise */ - boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, + final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, boolean resolveRefs) { - Preconditions.checkNotNull(outValue, "outValue"); - synchronized (this) { - ensureValidLocked(); - final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue, - resolveRefs); - if (cookie <= 0) { - return false; - } - - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); - - if (outValue.type == TypedValue.TYPE_STRING) { - outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); - } - return true; - } - } - - void dumpTheme(long theme, int priority, String tag, String prefix) { - synchronized (this) { - ensureValidLocked(); - nativeThemeDump(mObject, theme, priority, tag, prefix); + final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs); + if (block < 0) { + return false; } - } - @Nullable String getResourceName(@AnyRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceName(mObject, resId); - } - } + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); - @Nullable String getResourcePackageName(@AnyRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourcePackageName(mObject, resId); + if (outValue.type == TypedValue.TYPE_STRING) { + final StringBlock[] blocks = ensureStringBlocks(); + outValue.string = blocks[block].get(outValue.data); } + return true; } - @Nullable String getResourceTypeName(@AnyRes int resId) { + /** + * Ensures the string blocks are loaded. + * + * @return the string blocks + */ + @NonNull + final StringBlock[] ensureStringBlocks() { synchronized (this) { - ensureValidLocked(); - return nativeGetResourceTypeName(mObject, resId); + if (mStringBlocks == null) { + makeStringBlocks(sSystem.mStringBlocks); + } + return mStringBlocks; } } - @Nullable String getResourceEntryName(@AnyRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceEntryName(mObject, resId); + /*package*/ final void makeStringBlocks(StringBlock[] seed) { + final int seedNum = (seed != null) ? seed.length : 0; + final int num = getStringBlockCount(); + mStringBlocks = new StringBlock[num]; + if (localLOGV) Log.v(TAG, "Making string blocks for " + this + + ": " + num); + for (int i=0; i Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len; + } + public final void close() throws IOException { + synchronized (AssetManager.this) { + if (mAsset != 0) { + destroyAsset(mAsset); + mAsset = 0; + decRefsLocked(hashCode()); + } + } + } + public final void mark(int readlimit) { + mMarkPos = seekAsset(mAsset, 0, 0); + } + public final void reset() throws IOException { + seekAsset(mAsset, mMarkPos, -1); + } + public final int read(byte[] b) throws IOException { + return readAsset(mAsset, b, 0, b.length); + } + public final int read(byte[] b, int off, int len) throws IOException { + return readAsset(mAsset, b, off, len); } - - @Override public final long skip(long n) throws IOException { - ensureOpen(); - long pos = nativeAssetSeek(mAssetNativePtr, 0, 0); - if ((pos + n) > mLength) { - n = mLength - pos; + long pos = seekAsset(mAsset, 0, 0); + if ((pos+n) > mLength) { + n = mLength-pos; } if (n > 0) { - nativeAssetSeek(mAssetNativePtr, n, 0); + seekAsset(mAsset, n, 0); } return n; } - @Override - public final int available() throws IOException { - ensureOpen(); - final long len = nativeAssetGetRemainingLength(mAssetNativePtr); - return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len; + protected void finalize() throws Throwable + { + close(); } - @Override - public final boolean markSupported() { - return true; - } + private long mAsset; + private long mLength; + private long mMarkPos; + } - @Override - public final void mark(int readlimit) { - ensureOpen(); - mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0); + /** + * Add an additional set of assets to the asset manager. This can be + * either a directory or ZIP file. Not for use by applications. Returns + * the cookie of the added asset, or 0 on failure. + * {@hide} + */ + public final int addAssetPath(String path) { + return addAssetPathInternal(path, false); + } + + /** + * Add an application assets to the asset manager and loading it as shared library. + * This can be either a directory or ZIP file. Not for use by applications. Returns + * the cookie of the added asset, or 0 on failure. + * {@hide} + */ + public final int addAssetPathAsSharedLibrary(String path) { + return addAssetPathInternal(path, true); + } + + private final int addAssetPathInternal(String path, boolean appAsLib) { + synchronized (this) { + int res = addAssetPathNative(path, appAsLib); + makeStringBlocks(mStringBlocks); + return res; } + } - @Override - public final void reset() throws IOException { - ensureOpen(); - nativeAssetSeek(mAssetNativePtr, mMarkPos, -1); + private native final int addAssetPathNative(String path, boolean appAsLib); + + /** + * Add an additional set of assets to the asset manager from an already open + * FileDescriptor. Not for use by applications. + * This does not give full AssetManager functionality for these assets, + * since the origin of the file is not known for purposes of sharing, + * overlay resolution, and other features. However it does allow you + * to do simple access to the contents of the given fd as an apk file. + * Performs a dup of the underlying fd, so you must take care of still closing + * the FileDescriptor yourself (and can do that whenever you want). + * Returns the cookie of the added asset, or 0 on failure. + * {@hide} + */ + public int addAssetFd(FileDescriptor fd, String debugPathName) { + return addAssetFdInternal(fd, debugPathName, false); + } + + private int addAssetFdInternal(FileDescriptor fd, String debugPathName, + boolean appAsLib) { + synchronized (this) { + int res = addAssetFdNative(fd, debugPathName, appAsLib); + makeStringBlocks(mStringBlocks); + return res; } + } - @Override - public final void close() throws IOException { - if (mAssetNativePtr != 0) { - nativeAssetDestroy(mAssetNativePtr); - mAssetNativePtr = 0; + private native int addAssetFdNative(FileDescriptor fd, String debugPathName, + boolean appAsLib); - synchronized (AssetManager.this) { - decRefsLocked(hashCode()); - } - } + /** + * Add a set of assets to overlay an already added set of assets. + * + * This is only intended for application resources. System wide resources + * are handled before any Java code is executed. + * + * {@hide} + */ + + public final int addOverlayPath(String idmapPath) { + synchronized (this) { + int res = addOverlayPathNative(idmapPath); + makeStringBlocks(mStringBlocks); + return res; } + } - @Override - protected void finalize() throws Throwable { - close(); + /** + * See addOverlayPath. + * + * {@hide} + */ + public native final int addOverlayPathNative(String idmapPath); + + /** + * Add multiple sets of assets to the asset manager at once. See + * {@link #addAssetPath(String)} for more information. Returns array of + * cookies for each added asset with 0 indicating failure, or null if + * the input array of paths is null. + * {@hide} + */ + public final int[] addAssetPaths(String[] paths) { + if (paths == null) { + return null; } - private void ensureOpen() { - if (mAssetNativePtr == 0) { - throw new IllegalStateException("AssetInputStream is closed"); - } + int[] cookies = new int[paths.length]; + for (int i = 0; i < paths.length; i++) { + cookies[i] = addAssetPath(paths[i]); } + + return cookies; } /** * Determine whether the state in this asset manager is up-to-date with * the files on the filesystem. If false is returned, you need to * instantiate a new AssetManager class to see the new data. - * @hide + * {@hide} */ - public boolean isUpToDate() { - for (ApkAssets apkAssets : getApkAssets()) { - if (!apkAssets.isUpToDate()) { - return false; - } - } - return true; - } + public native final boolean isUpToDate(); /** * Get the locales that this asset manager contains data for. @@ -1071,12 +784,7 @@ public final class AssetManager implements AutoCloseable { * are of the form {@code ll_CC} where {@code ll} is a two letter language code, * and {@code CC} is a two letter country code. */ - public String[] getLocales() { - synchronized (this) { - ensureValidLocked(); - return nativeGetLocales(mObject, false /*excludeSystem*/); - } - } + public native final String[] getLocales(); /** * Same as getLocales(), except that locales that are only provided by the system (i.e. those @@ -1086,57 +794,131 @@ public final class AssetManager implements AutoCloseable { * assets support Cherokee and French, getLocales() would return * [Cherokee, English, French, German], while getNonSystemLocales() would return * [Cherokee, French]. - * @hide + * {@hide} */ - public String[] getNonSystemLocales() { - synchronized (this) { - ensureValidLocked(); - return nativeGetLocales(mObject, true /*excludeSystem*/); - } - } + public native final String[] getNonSystemLocales(); - /** - * @hide - */ - Configuration[] getSizeConfigurations() { - synchronized (this) { - ensureValidLocked(); - return nativeGetSizeConfigurations(mObject); - } - } + /** {@hide} */ + public native final Configuration[] getSizeConfigurations(); /** - * Change the configuration used when retrieving resources. Not for use by + * Change the configuation used when retrieving resources. Not for use by * applications. - * @hide + * {@hide} */ - public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation, - int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, - int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, - int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) { - synchronized (this) { - ensureValidLocked(); - nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density, - keyboard, keyboardHidden, navigation, screenWidth, screenHeight, - smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode, - colorMode, majorVersion); - } - } + public native final void setConfiguration(int mcc, int mnc, String locale, + int orientation, int touchscreen, int density, int keyboard, + int keyboardHidden, int navigation, int screenWidth, int screenHeight, + int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, + int screenLayout, int uiMode, int colorMode, int majorVersion); /** - * @hide + * Retrieve the resource identifier for the given resource name. */ - public SparseArray getAssignedPackageIdentifiers() { - synchronized (this) { - ensureValidLocked(); - return nativeGetAssignedPackageIdentifiers(mObject); - } - } + /*package*/ native final int getResourceIdentifier(String name, + String defType, + String defPackage); - private void incRefsLocked(long id) { + /*package*/ native final String getResourceName(int resid); + /*package*/ native final String getResourcePackageName(int resid); + /*package*/ native final String getResourceTypeName(int resid); + /*package*/ native final String getResourceEntryName(int resid); + + private native final long openAsset(String fileName, int accessMode); + private final native ParcelFileDescriptor openAssetFd(String fileName, + long[] outOffsets) throws IOException; + private native final long openNonAssetNative(int cookie, String fileName, + int accessMode); + private native ParcelFileDescriptor openNonAssetFdNative(int cookie, + String fileName, long[] outOffsets) throws IOException; + private native final void destroyAsset(long asset); + private native final int readAssetChar(long asset); + private native final int readAsset(long asset, byte[] b, int off, int len); + private native final long seekAsset(long asset, long offset, int whence); + private native final long getAssetLength(long asset); + private native final long getAssetRemainingLength(long asset); + + /** Returns true if the resource was found, filling in mRetStringBlock and + * mRetData. */ + private native final int loadResourceValue(int ident, short density, TypedValue outValue, + boolean resolve); + /** Returns true if the resource was found, filling in mRetStringBlock and + * mRetData. */ + private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue, + boolean resolve); + /*package*/ static final int STYLE_NUM_ENTRIES = 6; + /*package*/ static final int STYLE_TYPE = 0; + /*package*/ static final int STYLE_DATA = 1; + /*package*/ static final int STYLE_ASSET_COOKIE = 2; + /*package*/ static final int STYLE_RESOURCE_ID = 3; + + /* Offset within typed data array for native changingConfigurations. */ + static final int STYLE_CHANGING_CONFIGURATIONS = 4; + + /*package*/ static final int STYLE_DENSITY = 5; + /*package*/ native static final void applyStyle(long theme, + int defStyleAttr, int defStyleRes, long xmlParser, + int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress); + /*package*/ native static final boolean resolveAttrs(long theme, + int defStyleAttr, int defStyleRes, int[] inValues, + int[] inAttrs, int[] outValues, int[] outIndices); + /*package*/ native final boolean retrieveAttributes( + long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); + /*package*/ native final int getArraySize(int resource); + /*package*/ native final int retrieveArray(int resource, int[] outValues); + private native final int getStringBlockCount(); + private native final long getNativeStringBlock(int block); + + /** + * {@hide} + */ + public native final String getCookieName(int cookie); + + /** + * {@hide} + */ + public native final SparseArray getAssignedPackageIdentifiers(); + + /** + * {@hide} + */ + public native static final int getGlobalAssetCount(); + + /** + * {@hide} + */ + public native static final String getAssetAllocations(); + + /** + * {@hide} + */ + public native static final int getGlobalAssetManagerCount(); + + private native final long newTheme(); + private native final void deleteTheme(long theme); + /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force); + /*package*/ native static final void copyTheme(long dest, long source); + /*package*/ native static final void clearTheme(long theme); + /*package*/ native static final int loadThemeAttributeValue(long theme, int ident, + TypedValue outValue, + boolean resolve); + /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix); + /*package*/ native static final @NativeConfig int getThemeChangingConfigurations(long theme); + + private native final long openXmlAssetNative(int cookie, String fileName); + + private native final String[] getArrayStringResource(int arrayRes); + private native final int[] getArrayStringInfo(int arrayRes); + /*package*/ native final int[] getArrayIntResource(int arrayRes); + /*package*/ native final int[] getStyleAttributes(int themeRes); + + private native final void init(boolean isSystem); + private native final void destroy(); + + private final void incRefsLocked(long id) { if (DEBUG_REFS) { if (mRefStacks == null) { - mRefStacks = new HashMap<>(); + mRefStacks = new HashMap(); } RuntimeException ex = new RuntimeException(); ex.fillInStackTrace(); @@ -1144,117 +926,16 @@ public final class AssetManager implements AutoCloseable { } mNumRefs++; } - - private void decRefsLocked(long id) { + + private final void decRefsLocked(long id) { if (DEBUG_REFS && mRefStacks != null) { mRefStacks.remove(id); } mNumRefs--; - if (mNumRefs == 0 && mObject != 0) { - nativeDestroy(mObject); - mObject = 0; + //System.out.println("Dec streams: mNumRefs=" + mNumRefs + // + " mReleased=" + mReleased); + if (mNumRefs == 0) { + destroy(); } } - - // AssetManager setup native methods. - private static native long nativeCreate(); - private static native void nativeDestroy(long ptr); - private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, - boolean invalidateCaches); - private static native void nativeSetConfiguration(long ptr, int mcc, int mnc, - @Nullable String locale, int orientation, int touchscreen, int density, int keyboard, - int keyboardHidden, int navigation, int screenWidth, int screenHeight, - int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, - int uiMode, int colorMode, int majorVersion); - private static native @NonNull SparseArray nativeGetAssignedPackageIdentifiers( - long ptr); - - // File native methods. - private static native @Nullable String[] nativeList(long ptr, @NonNull String path) - throws IOException; - private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode); - private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr, - @NonNull String fileName, long[] outOffsets) throws IOException; - private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName, - int accessMode); - private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie, - @NonNull String fileName, @NonNull long[] outOffsets) throws IOException; - private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName); - - // Primitive resource native methods. - private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density, - @NonNull TypedValue outValue, boolean resolveReferences); - private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId, - @NonNull TypedValue outValue); - - private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr, - @StyleRes int resId); - private static native @Nullable String[] nativeGetResourceStringArray(long ptr, - @ArrayRes int resId); - private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr, - @ArrayRes int resId); - private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId); - private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId); - private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId, - @NonNull int[] outValues); - - // Resource name/ID native methods. - private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name, - @Nullable String defType, @Nullable String defPackage); - private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid); - private static native @Nullable String nativeGetResourcePackageName(long ptr, - @AnyRes int resid); - private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid); - private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid); - private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem); - private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr); - - // Style attribute retrieval native methods. - private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, - @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs, - long outValuesAddress, long outIndicesAddress); - private static native boolean nativeResolveAttrs(long ptr, long themePtr, - @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, - @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); - private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr, - @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); - - // Theme related native methods - private static native long nativeThemeCreate(long ptr); - private static native void nativeThemeDestroy(long themePtr); - private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, - boolean force); - static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr); - static native void nativeThemeClear(long themePtr); - private static native int nativeThemeGetAttributeValue(long ptr, long themePtr, - @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve); - private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag, - String prefix); - static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr); - - // AssetInputStream related native methods. - private static native void nativeAssetDestroy(long assetPtr); - private static native int nativeAssetReadChar(long assetPtr); - private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len); - private static native long nativeAssetSeek(long assetPtr, long offset, int whence); - private static native long nativeAssetGetLength(long assetPtr); - private static native long nativeAssetGetRemainingLength(long assetPtr); - - private static native void nativeVerifySystemIdmaps(); - - // Global debug native methods. - /** - * @hide - */ - public static native int getGlobalAssetCount(); - - /** - * @hide - */ - public static native String getAssetAllocations(); - - /** - * @hide - */ - public static native int getGlobalAssetManagerCount(); } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 8f58891ed556..e173653cd961 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -590,7 +590,7 @@ public class Resources { */ @NonNull public int[] getIntArray(@ArrayRes int id) throws NotFoundException { - int[] res = mResourcesImpl.getAssets().getResourceIntArray(id); + int[] res = mResourcesImpl.getAssets().getArrayIntResource(id); if (res != null) { return res; } @@ -613,13 +613,13 @@ public class Resources { @NonNull public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException { final ResourcesImpl impl = mResourcesImpl; - int len = impl.getAssets().getResourceArraySize(id); + int len = impl.getAssets().getArraySize(id); if (len < 0) { throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id)); } TypedArray array = TypedArray.obtain(this, len); - array.mLength = impl.getAssets().getResourceArray(id, array.mData); + array.mLength = impl.getAssets().retrieveArray(id, array.mData); array.mIndices[0] = 0; return array; @@ -1789,7 +1789,8 @@ public class Resources { // out the attributes from the XML file (applying type information // contained in the resources and such). XmlBlock.Parser parser = (XmlBlock.Parser)set; - mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices); + mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, attrs, + array.mData, array.mIndices); array.mXml = parser; diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 2a4b278bdca8..97cb78bc4243 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -168,6 +168,7 @@ public class ResourcesImpl { mDisplayAdjustments = displayAdjustments; mConfiguration.setToDefaults(); updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo()); + mAssets.ensureStringBlocks(); } public DisplayAdjustments getDisplayAdjustments() { @@ -1273,7 +1274,8 @@ public class ResourcesImpl { void applyStyle(int resId, boolean force) { synchronized (mKey) { - mAssets.applyStyleToTheme(mTheme, resId, force); + AssetManager.applyThemeStyle(mTheme, resId, force); + mThemeResId = resId; mKey.append(resId, force); } @@ -1282,7 +1284,7 @@ public class ResourcesImpl { void setTo(ThemeImpl other) { synchronized (mKey) { synchronized (other.mKey) { - AssetManager.nativeThemeCopy(mTheme, other.mTheme); + AssetManager.copyTheme(mTheme, other.mTheme); mThemeResId = other.mThemeResId; mKey.setTo(other.getKey()); @@ -1305,10 +1307,12 @@ public class ResourcesImpl { // out the attributes from the XML file (applying type information // contained in the resources and such). final XmlBlock.Parser parser = (XmlBlock.Parser) set; - mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs, - array.mDataAddress, array.mIndicesAddress); + AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes, + parser != null ? parser.mParseState : 0, + attrs, attrs.length, array.mDataAddress, array.mIndicesAddress); array.mTheme = wrapper; array.mXml = parser; + return array; } } @@ -1325,7 +1329,7 @@ public class ResourcesImpl { } final TypedArray array = TypedArray.obtain(wrapper.getResources(), len); - mAssets.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); + AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); array.mTheme = wrapper; array.mXml = null; return array; @@ -1345,14 +1349,14 @@ public class ResourcesImpl { @Config int getChangingConfigurations() { synchronized (mKey) { final @NativeConfig int nativeChangingConfig = - AssetManager.nativeThemeGetChangingConfigurations(mTheme); + AssetManager.getThemeChangingConfigurations(mTheme); return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig); } } public void dump(int priority, String tag, String prefix) { synchronized (mKey) { - mAssets.dumpTheme(mTheme, priority, tag, prefix); + AssetManager.dumpTheme(mTheme, priority, tag, prefix); } } @@ -1381,13 +1385,13 @@ public class ResourcesImpl { */ void rebase() { synchronized (mKey) { - AssetManager.nativeThemeClear(mTheme); + AssetManager.clearTheme(mTheme); // Reapply the same styles in the same order. for (int i = 0; i < mKey.mCount; i++) { final int resId = mKey.mResId[i]; final boolean force = mKey.mForce[i]; - mAssets.applyStyleToTheme(mTheme, resId, force); + AssetManager.applyThemeStyle(mTheme, resId, force); } } } diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index cbb3c6df0558..f33c75168a5f 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -61,15 +61,6 @@ public class TypedArray { return attrs; } - // STYLE_ prefixed constants are offsets within the typed data array. - static final int STYLE_NUM_ENTRIES = 6; - static final int STYLE_TYPE = 0; - static final int STYLE_DATA = 1; - static final int STYLE_ASSET_COOKIE = 2; - static final int STYLE_RESOURCE_ID = 3; - static final int STYLE_CHANGING_CONFIGURATIONS = 4; - static final int STYLE_DENSITY = 5; - private final Resources mResources; private DisplayMetrics mMetrics; private AssetManager mAssets; @@ -87,7 +78,7 @@ public class TypedArray { private void resize(int len) { mLength = len; - final int dataLen = len * STYLE_NUM_ENTRIES; + final int dataLen = len * AssetManager.STYLE_NUM_ENTRIES; final int indicesLen = len + 1; final VMRuntime runtime = VMRuntime.getRuntime(); if (mDataAddress == 0 || mData.length < dataLen) { @@ -175,9 +166,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -212,9 +203,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -251,13 +242,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_STRING) { - final int cookie = data[index + STYLE_ASSET_COOKIE]; + final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; if (cookie < 0) { - return mXml.getPooledString(data[index + STYLE_DATA]).toString(); + return mXml.getPooledString( + data[index+AssetManager.STYLE_DATA]).toString(); } } return null; @@ -282,11 +274,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava( - data[index + STYLE_CHANGING_CONFIGURATIONS]); + data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); if ((changingConfigs & ~allowedChangingConfigs) != 0) { return null; } @@ -328,14 +320,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA] != 0; + return data[index+AssetManager.STYLE_DATA] != 0; } final TypedValue v = mValue; @@ -367,14 +359,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } final TypedValue v = mValue; @@ -404,16 +396,16 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FLOAT) { - return Float.intBitsToFloat(data[index + STYLE_DATA]); + return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]); } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } final TypedValue v = mValue; @@ -454,15 +446,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_STRING) { final TypedValue value = mValue; if (getValueAt(index, value)) { @@ -506,7 +498,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -541,7 +533,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -572,15 +564,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -620,14 +612,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimension( + data[index + AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -668,14 +661,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelOffset( + data[index + AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -717,14 +711,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize( + data[index+AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -760,15 +755,16 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize( + data[index+AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -799,14 +795,15 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize( + data[index + AssetManager.STYLE_DATA], mMetrics); } return defValue; @@ -836,14 +833,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FRACTION) { - return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase); + return TypedValue.complexToFraction( + data[index+AssetManager.STYLE_DATA], base, pbase); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -876,10 +874,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) { - final int resid = data[index + STYLE_RESOURCE_ID]; + if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) { + final int resid = data[index+AssetManager.STYLE_RESOURCE_ID]; if (resid != 0) { return resid; } @@ -904,10 +902,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { - return data[index + STYLE_DATA]; + if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { + return data[index + AssetManager.STYLE_DATA]; } return defValue; } @@ -941,7 +939,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -977,7 +975,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -1008,7 +1006,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { return mResources.getTextArray(value.resourceId); } return null; @@ -1029,7 +1027,7 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - return getValueAt(index * STYLE_NUM_ENTRIES, outValue); + return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue); } /** @@ -1045,8 +1043,8 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; - return mData[index + STYLE_TYPE]; + index *= AssetManager.STYLE_NUM_ENTRIES; + return mData[index + AssetManager.STYLE_TYPE]; } /** @@ -1065,9 +1063,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; return type != TypedValue.TYPE_NULL; } @@ -1086,11 +1084,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; return type != TypedValue.TYPE_NULL - || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; + || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; } /** @@ -1111,7 +1109,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { return value; } return null; @@ -1183,16 +1181,16 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * STYLE_NUM_ENTRIES; - if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { + final int index = i * AssetManager.STYLE_NUM_ENTRIES; + if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { // Not an attribute, ignore. continue; } // Null the entry so that we can safely call getZzz(). - data[index + STYLE_TYPE] = TypedValue.TYPE_NULL; + data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL; - final int attr = data[index + STYLE_DATA]; + final int attr = data[index + AssetManager.STYLE_DATA]; if (attr == 0) { // Useless data, ignore. continue; @@ -1233,44 +1231,45 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * STYLE_NUM_ENTRIES; - final int type = data[index + STYLE_TYPE]; + final int index = i * AssetManager.STYLE_NUM_ENTRIES; + final int type = data[index + AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { continue; } changingConfig |= ActivityInfo.activityInfoConfigNativeToJava( - data[index + STYLE_CHANGING_CONFIGURATIONS]); + data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); } return changingConfig; } private boolean getValueAt(int index, TypedValue outValue) { final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return false; } outValue.type = type; - outValue.data = data[index + STYLE_DATA]; - outValue.assetCookie = data[index + STYLE_ASSET_COOKIE]; - outValue.resourceId = data[index + STYLE_RESOURCE_ID]; + outValue.data = data[index+AssetManager.STYLE_DATA]; + outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; + outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID]; outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - data[index + STYLE_CHANGING_CONFIGURATIONS]); - outValue.density = data[index + STYLE_DENSITY]; + data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); + outValue.density = data[index+AssetManager.STYLE_DENSITY]; outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null; return true; } private CharSequence loadStringValueAt(int index) { final int[] data = mData; - final int cookie = data[index + STYLE_ASSET_COOKIE]; + final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; if (cookie < 0) { if (mXml != null) { - return mXml.getPooledString(data[index + STYLE_DATA]); + return mXml.getPooledString( + data[index+AssetManager.STYLE_DATA]); } return null; } - return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]); + return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]); } /** @hide */ diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java index d4ccffb83ca5..e6b957414ea8 100644 --- a/core/java/android/content/res/XmlBlock.java +++ b/core/java/android/content/res/XmlBlock.java @@ -16,7 +16,6 @@ package android.content.res; -import android.annotation.Nullable; import android.util.TypedValue; import com.android.internal.util.XmlUtils; @@ -34,7 +33,7 @@ import java.io.Reader; * * {@hide} */ -final class XmlBlock implements AutoCloseable { +final class XmlBlock { private static final boolean DEBUG=false; public XmlBlock(byte[] data) { @@ -49,7 +48,6 @@ final class XmlBlock implements AutoCloseable { mStrings = new StringBlock(nativeGetStringBlock(mNative), false); } - @Override public void close() { synchronized (this) { if (mOpen) { @@ -480,13 +478,13 @@ final class XmlBlock implements AutoCloseable { * are doing! The given native object must exist for the entire lifetime * of this newly creating XmlBlock. */ - XmlBlock(@Nullable AssetManager assets, long xmlBlock) { + XmlBlock(AssetManager assets, long xmlBlock) { mAssets = assets; mNative = xmlBlock; mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false); } - private @Nullable final AssetManager mAssets; + private final AssetManager mAssets; private final long mNative; /*package*/ final StringBlock mStrings; private boolean mOpen = true; diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 5751fc99a484..93f95c634ae3 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -110,8 +110,8 @@ cc_library_shared { "android_util_AssetManager.cpp", "android_util_Binder.cpp", "android_util_EventLog.cpp", - "android_util_Log.cpp", "android_util_MemoryIntArray.cpp", + "android_util_Log.cpp", "android_util_PathParser.cpp", "android_util_Process.cpp", "android_util_StringBlock.cpp", @@ -190,7 +190,6 @@ cc_library_shared { "android_backup_FileBackupHelperBase.cpp", "android_backup_BackupHelperDispatcher.cpp", "android_app_backup_FullBackup.cpp", - "android_content_res_ApkAssets.cpp", "android_content_res_ObbScanner.cpp", "android_content_res_Configuration.cpp", "android_animation_PropertyValuesHolder.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index e3b5c8f3136a..aa9a82415f97 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -122,7 +122,6 @@ extern int register_android_util_MemoryIntArray(JNIEnv* env); extern int register_android_util_PathParser(JNIEnv* env); extern int register_android_content_StringBlock(JNIEnv* env); extern int register_android_content_XmlBlock(JNIEnv* env); -extern int register_android_content_res_ApkAssets(JNIEnv* env); extern int register_android_graphics_Canvas(JNIEnv* env); extern int register_android_graphics_CanvasProperty(JNIEnv* env); extern int register_android_graphics_ColorFilter(JNIEnv* env); @@ -1341,7 +1340,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_AssetManager), REG_JNI(register_android_content_StringBlock), REG_JNI(register_android_content_XmlBlock), - REG_JNI(register_android_content_res_ApkAssets), REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_Hyphenator), REG_JNI(register_android_text_MeasuredParagraph), diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index c50026ea570e..dd3e6f02e9fe 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include "Utils.h" #include "FontUtils.h" @@ -224,8 +224,7 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b NPE_CHECK_RETURN_ZERO(env, jpath); NativeFamilyBuilder* builder = reinterpret_cast(builderPtr); - - Guarded* mgr = AssetManagerForJavaObject(env, jassetMgr); + AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr); if (NULL == mgr) { builder->axes.clear(); return false; @@ -237,33 +236,27 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b return false; } - std::unique_ptr asset; - { - ScopedLock locked_mgr(*mgr); - if (isAsset) { - asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); - } else if (cookie > 0) { - // Valid java cookies are 1-based, but AssetManager cookies are 0-based. - asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast(cookie - 1), - Asset::ACCESS_BUFFER); - } else { - asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER); - } + Asset* asset; + if (isAsset) { + asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); + } else { + asset = cookie ? mgr->openNonAsset(static_cast(cookie), str.c_str(), + Asset::ACCESS_BUFFER) : mgr->openNonAsset(str.c_str(), Asset::ACCESS_BUFFER); } - if (nullptr == asset) { + if (NULL == asset) { builder->axes.clear(); return false; } const void* buf = asset->getBuffer(false); if (NULL == buf) { + delete asset; builder->axes.clear(); return false; } - sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, - asset.release())); + sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset)); return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); } diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 49a24a30f77e..09e37e1a3de6 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -361,7 +361,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName code->sdkVersion = sdkVersion; code->javaAssetManager = env->NewGlobalRef(jAssetMgr); - code->assetManager = NdkAssetManagerForJavaObject(env, jAssetMgr); + code->assetManager = assetManagerForJavaObject(env, jAssetMgr); if (obbDir != NULL) { dirStr = env->GetStringUTFChars(obbDir, NULL); diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp deleted file mode 100644 index c0f151b71c93..000000000000 --- a/core/jni/android_content_res_ApkAssets.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "android-base/macros.h" -#include "android-base/stringprintf.h" -#include "android-base/unique_fd.h" -#include "androidfw/ApkAssets.h" -#include "utils/misc.h" - -#include "core_jni_helpers.h" -#include "jni.h" -#include "nativehelper/ScopedUtfChars.h" - -using ::android::base::unique_fd; - -namespace android { - -static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboolean system, - jboolean force_shared_lib, jboolean overlay) { - ScopedUtfChars path(env, java_path); - if (path.c_str() == nullptr) { - return 0; - } - - std::unique_ptr apk_assets; - if (overlay) { - apk_assets = ApkAssets::LoadOverlay(path.c_str(), system); - } else if (force_shared_lib) { - apk_assets = ApkAssets::LoadAsSharedLibrary(path.c_str(), system); - } else { - apk_assets = ApkAssets::Load(path.c_str(), system); - } - - if (apk_assets == nullptr) { - std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str()); - jniThrowException(env, "java/io/IOException", error_msg.c_str()); - return 0; - } - return reinterpret_cast(apk_assets.release()); -} - -static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor, - jstring friendly_name, jboolean system, jboolean force_shared_lib) { - ScopedUtfChars friendly_name_utf8(env, friendly_name); - if (friendly_name_utf8.c_str() == nullptr) { - return 0; - } - - int fd = jniGetFDFromFileDescriptor(env, file_descriptor); - if (fd < 0) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); - return 0; - } - - unique_fd dup_fd(::dup(fd)); - if (dup_fd < 0) { - jniThrowIOException(env, errno); - return 0; - } - - std::unique_ptr apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), - friendly_name_utf8.c_str(), - system, force_shared_lib); - if (apk_assets == nullptr) { - std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d", - friendly_name_utf8.c_str(), dup_fd.get()); - jniThrowException(env, "java/io/IOException", error_msg.c_str()); - return 0; - } - return reinterpret_cast(apk_assets.release()); -} - -static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - delete reinterpret_cast(ptr); -} - -static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) { - const ApkAssets* apk_assets = reinterpret_cast(ptr); - return env->NewStringUTF(apk_assets->GetPath().c_str()); -} - -static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - const ApkAssets* apk_assets = reinterpret_cast(ptr); - return reinterpret_cast(apk_assets->GetLoadedArsc()->GetStringPool()); -} - -static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - const ApkAssets* apk_assets = reinterpret_cast(ptr); - (void)apk_assets; - return JNI_TRUE; -} - -static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) { - ScopedUtfChars path_utf8(env, file_name); - if (path_utf8.c_str() == nullptr) { - return 0; - } - - const ApkAssets* apk_assets = reinterpret_cast(ptr); - std::unique_ptr asset = apk_assets->Open(path_utf8.c_str(), - Asset::AccessMode::ACCESS_RANDOM); - if (asset == nullptr) { - jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str()); - return 0; - } - - // DynamicRefTable is only needed when looking up resource references. Opening an XML file - // directly from an ApkAssets has no notion of proper resource references. - std::unique_ptr xml_tree = util::make_unique(nullptr /*dynamicRefTable*/); - status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); - asset.reset(); - - if (err != NO_ERROR) { - jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); - return 0; - } - return reinterpret_cast(xml_tree.release()); -} - -// JNI registration. -static const JNINativeMethod gApkAssetsMethods[] = { - {"nativeLoad", "(Ljava/lang/String;ZZZ)J", (void*)NativeLoad}, - {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZ)J", - (void*)NativeLoadFromFd}, - {"nativeDestroy", "(J)V", (void*)NativeDestroy}, - {"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath}, - {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock}, - {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate}, - {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml}, -}; - -int register_android_content_res_ApkAssets(JNIEnv* env) { - return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods, - arraysize(gApkAssetsMethods)); -} - -} // namespace android diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 7e17a4909cee..683b4c490ec3 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -1,1441 +1,1851 @@ -/* - * Copyright 2006, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* //device/libs/android_runtime/android_util_AssetManager.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ #define LOG_TAG "asset" +#include + #include #include #include -#include -#include #include #include +#include +#include #include // for AID_SYSTEM -#include "android-base/logging.h" -#include "android-base/properties.h" -#include "android-base/stringprintf.h" -#include "android_runtime/android_util_AssetManager.h" -#include "android_runtime/AndroidRuntime.h" -#include "android_util_Binder.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" -#include "androidfw/AssetManager2.h" #include "androidfw/AttributeResolution.h" -#include "androidfw/MutexGuard.h" #include "androidfw/ResourceTypes.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_util_Binder.h" #include "core_jni_helpers.h" #include "jni.h" -#include "nativehelper/JNIHelp.h" -#include "nativehelper/ScopedPrimitiveArray.h" -#include "nativehelper/ScopedStringChars.h" -#include "nativehelper/ScopedUtfChars.h" +#include +#include +#include #include "utils/Log.h" -#include "utils/String8.h" #include "utils/misc.h" +#include "utils/String8.h" extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap); extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap); -using ::android::base::StringPrintf; namespace android { +static const bool kThrowOnBadId = false; + // ---------------------------------------------------------------------------- -static struct typedvalue_offsets_t { - jfieldID mType; - jfieldID mData; - jfieldID mString; - jfieldID mAssetCookie; - jfieldID mResourceId; - jfieldID mChangingConfigurations; - jfieldID mDensity; +static struct typedvalue_offsets_t +{ + jfieldID mType; + jfieldID mData; + jfieldID mString; + jfieldID mAssetCookie; + jfieldID mResourceId; + jfieldID mChangingConfigurations; + jfieldID mDensity; } gTypedValueOffsets; -static struct assetfiledescriptor_offsets_t { - jfieldID mFd; - jfieldID mStartOffset; - jfieldID mLength; +static struct assetfiledescriptor_offsets_t +{ + jfieldID mFd; + jfieldID mStartOffset; + jfieldID mLength; } gAssetFileDescriptorOffsets; -static struct assetmanager_offsets_t { - jfieldID mObject; +static struct assetmanager_offsets_t +{ + jfieldID mObject; } gAssetManagerOffsets; -static struct { - jfieldID native_ptr; -} gApkAssetsFields; - -static struct sparsearray_offsets_t { - jclass classObject; - jmethodID constructor; - jmethodID put; +static struct sparsearray_offsets_t +{ + jclass classObject; + jmethodID constructor; + jmethodID put; } gSparseArrayOffsets; -static struct configuration_offsets_t { - jclass classObject; - jmethodID constructor; - jfieldID mSmallestScreenWidthDpOffset; - jfieldID mScreenWidthDpOffset; - jfieldID mScreenHeightDpOffset; +static struct configuration_offsets_t +{ + jclass classObject; + jmethodID constructor; + jfieldID mSmallestScreenWidthDpOffset; + jfieldID mScreenWidthDpOffset; + jfieldID mScreenHeightDpOffset; } gConfigurationOffsets; -jclass g_stringClass = nullptr; +jclass g_stringClass = NULL; // ---------------------------------------------------------------------------- -// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. -constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { - return cookie != kInvalidCookie ? static_cast(cookie + 1) : -1; -} - -constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) { - return cookie > 0 ? static_cast(cookie - 1) : kInvalidCookie; +static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, + const Res_value& value, uint32_t ref, ssize_t block, + uint32_t typeSpecFlags, ResTable_config* config = NULL); + +jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, + const Res_value& value, uint32_t ref, ssize_t block, + uint32_t typeSpecFlags, ResTable_config* config) +{ + env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType); + env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie, + static_cast(table->getTableCookie(block))); + env->SetIntField(outValue, gTypedValueOffsets.mData, value.data); + env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL); + env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref); + env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations, + typeSpecFlags); + if (config != NULL) { + env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density); + } + return block; } // This is called by zygote (running as user root) as part of preloadResources. -static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { - switch (pid_t pid = fork()) { - case -1: - PLOG(ERROR) << "failed to fork for idmap"; - break; - - // child - case 0: { - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata; - - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - - capheader.version = _LINUX_CAPABILITY_VERSION; - capheader.pid = 0; - - if (capget(&capheader, &capdata) != 0) { - PLOG(ERROR) << "capget"; - exit(1); - } - - capdata.effective = capdata.permitted; - if (capset(&capheader, &capdata) != 0) { - PLOG(ERROR) << "capset"; - exit(1); - } - - if (setgid(AID_SYSTEM) != 0) { - PLOG(ERROR) << "setgid"; - exit(1); - } - - if (setuid(AID_SYSTEM) != 0) { - PLOG(ERROR) << "setuid"; - exit(1); - } - - // Generic idmap parameters - const char* argv[8]; - int argc = 0; - struct stat st; - - memset(argv, 0, sizeof(argv)); - argv[argc++] = AssetManager::IDMAP_BIN; - argv[argc++] = "--scan"; - argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; - argv[argc++] = AssetManager::TARGET_APK_PATH; - argv[argc++] = AssetManager::IDMAP_DIR; - - // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, - // use OVERLAY_DIR/ in addition to OVERLAY_DIR. - std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY, - ""); - if (!overlay_theme_path.empty()) { - overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path; - if (stat(overlay_theme_path.c_str(), &st) == 0) { - argv[argc++] = overlay_theme_path.c_str(); - } - } - - if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::OVERLAY_DIR; - } - - if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; - } - - // Finally, invoke idmap (if any overlay directory exists) - if (argc > 5) { - execv(AssetManager::IDMAP_BIN, (char* const*)argv); - PLOG(ERROR) << "failed to execv for idmap"; - exit(1); // should never get here - } else { - exit(0); - } - } break; - - // parent - default: - waitpid(pid, nullptr, 0); - break; - } +static void verifySystemIdmaps() +{ + pid_t pid; + char system_id[10]; + + snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM); + + switch (pid = fork()) { + case -1: + ALOGE("failed to fork for idmap: %s", strerror(errno)); + break; + case 0: // child + { + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata; + + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + + capheader.version = _LINUX_CAPABILITY_VERSION; + capheader.pid = 0; + + if (capget(&capheader, &capdata) != 0) { + ALOGE("capget: %s\n", strerror(errno)); + exit(1); + } + + capdata.effective = capdata.permitted; + if (capset(&capheader, &capdata) != 0) { + ALOGE("capset: %s\n", strerror(errno)); + exit(1); + } + + if (setgid(AID_SYSTEM) != 0) { + ALOGE("setgid: %s\n", strerror(errno)); + exit(1); + } + + if (setuid(AID_SYSTEM) != 0) { + ALOGE("setuid: %s\n", strerror(errno)); + exit(1); + } + + // Generic idmap parameters + const char* argv[8]; + int argc = 0; + struct stat st; + + memset(argv, NULL, sizeof(argv)); + argv[argc++] = AssetManager::IDMAP_BIN; + argv[argc++] = "--scan"; + argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; + argv[argc++] = AssetManager::TARGET_APK_PATH; + argv[argc++] = AssetManager::IDMAP_DIR; + + // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, + // use OVERLAY_DIR/ in addition to OVERLAY_DIR. + char subdir[PROP_VALUE_MAX]; + int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir); + if (len > 0) { + String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir; + if (stat(overlayPath.string(), &st) == 0) { + argv[argc++] = overlayPath.string(); + } + } + if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::OVERLAY_DIR; + } + + if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; + } + + // Finally, invoke idmap (if any overlay directory exists) + if (argc > 5) { + execv(AssetManager::IDMAP_BIN, (char* const*)argv); + ALOGE("failed to execv for idmap: %s", strerror(errno)); + exit(1); // should never get here + } else { + exit(0); + } + } + break; + default: // parent + waitpid(pid, NULL, 0); + break; + } } -static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref, - uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) { - env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType); - env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie, - ApkAssetsCookieToJavaCookie(cookie)); - env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data); - env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr); - env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref); - env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags); - if (config != nullptr) { - env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density); - } - return static_cast(ApkAssetsCookieToJavaCookie(cookie)); -} // ---------------------------------------------------------------------------- -// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance. -struct GuardedAssetManager : public ::AAssetManager { - Guarded guarded_assetmanager; -}; - -::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { - jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject); - ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle); - if (am == nullptr) { +// this guy is exported to other jni routines +AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj) +{ + jlong amHandle = env->GetLongField(obj, gAssetManagerOffsets.mObject); + AssetManager* am = reinterpret_cast(amHandle); + if (am != NULL) { + return am; + } jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); - return nullptr; - } - return am; + return NULL; } -Guarded* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) { - if (assetmanager == nullptr) { - return nullptr; - } - return &reinterpret_cast(assetmanager)->guarded_assetmanager; -} +static jlong android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz, + jstring fileName, jint mode) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + ALOGV("openAsset in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Empty file name"); + return -1; + } + + if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM + && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return -1; + } + + Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return -1; + } + + //printf("Created Asset Stream: %p\n", a); -Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { - return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager)); + return reinterpret_cast(a); } -static Guarded& AssetManagerFromLong(jlong ptr) { - return *AssetManagerForNdkAssetManager(reinterpret_cast(ptr)); +static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets) +{ + off64_t startOffset, length; + int fd = a->openFileDescriptor(&startOffset, &length); + delete a; + + if (fd < 0) { + jniThrowException(env, "java/io/FileNotFoundException", + "This file can not be opened as a file descriptor; it is probably compressed"); + return NULL; + } + + jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0); + if (offsets == NULL) { + close(fd); + return NULL; + } + + offsets[0] = startOffset; + offsets[1] = length; + + env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0); + + jobject fileDesc = jniCreateFileDescriptor(env, fd); + if (fileDesc == NULL) { + close(fd); + return NULL; + } + + return newParcelFileDescriptor(env, fileDesc); } -static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr asset, - jlongArray out_offsets) { - off64_t start_offset, length; - int fd = asset->openFileDescriptor(&start_offset, &length); - asset.reset(); - - if (fd < 0) { - jniThrowException(env, "java/io/FileNotFoundException", - "This file can not be opened as a file descriptor; it is probably " - "compressed"); - return nullptr; - } - - jlong* offsets = reinterpret_cast(env->GetPrimitiveArrayCritical(out_offsets, 0)); - if (offsets == nullptr) { - close(fd); - return nullptr; - } - - offsets[0] = start_offset; - offsets[1] = length; - - env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0); - - jobject file_desc = jniCreateFileDescriptor(env, fd); - if (file_desc == nullptr) { - close(fd); - return nullptr; - } - return newParcelFileDescriptor(env, file_desc); +static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz, + jstring fileName, jlongArray outOffsets) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ALOGV("openAssetFd in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return NULL; + } + + Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return NULL; + } + + //printf("Created Asset Stream: %p\n", a); + + return returnParcelFileDescriptor(env, a, outOffsets); } -static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) { - return Asset::getGlobalCount(); +static jlong android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz, + jint cookie, + jstring fileName, + jint mode) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return -1; + } + + if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM + && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return -1; + } + + Asset* a = cookie + ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), + (Asset::AccessMode)mode) + : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return -1; + } + + //printf("Created Asset Stream: %p\n", a); + + return reinterpret_cast(a); } -static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) { - String8 alloc = Asset::getAssetAllocations(); - if (alloc.length() <= 0) { - return nullptr; - } - return env->NewStringUTF(alloc.string()); +static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz, + jint cookie, + jstring fileName, + jlongArray outOffsets) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ALOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return NULL; + } + + Asset* a = cookie + ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), Asset::ACCESS_RANDOM) + : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return NULL; + } + + //printf("Created Asset Stream: %p\n", a); + + return returnParcelFileDescriptor(env, a, outOffsets); } -static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) { - // TODO(adamlesinski): Switch to AssetManager2. - return AssetManager::getGlobalCount(); +static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz, + jstring fileName) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return NULL; + } + + AssetDir* dir = am->openDir(fileName8.c_str()); + + if (dir == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return NULL; + } + + size_t N = dir->getFileCount(); + + jobjectArray array = env->NewObjectArray(dir->getFileCount(), + g_stringClass, NULL); + if (array == NULL) { + delete dir; + return NULL; + } + + for (size_t i=0; igetFileName(i); + jstring str = env->NewStringUTF(name.string()); + if (str == NULL) { + delete dir; + return NULL; + } + env->SetObjectArrayElement(array, i, str); + env->DeleteLocalRef(str); + } + + delete dir; + + return array; } -static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) { - // AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and - // AssetManager2 in a contiguous block (GuardedAssetManager). - return reinterpret_cast(new GuardedAssetManager()); +static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz, + jlong assetHandle) +{ + Asset* a = reinterpret_cast(assetHandle); + + //printf("Destroying Asset Stream: %p\n", a); + + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return; + } + + delete a; } -static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - delete reinterpret_cast(ptr); +static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz, + jlong assetHandle) +{ + Asset* a = reinterpret_cast(assetHandle); + + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; + } + + uint8_t b; + ssize_t res = a->read(&b, 1); + return res == 1 ? b : -1; } -static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr, - jobjectArray apk_assets_array, jboolean invalidate_caches) { - const jsize apk_assets_len = env->GetArrayLength(apk_assets_array); - std::vector apk_assets; - apk_assets.reserve(apk_assets_len); - for (jsize i = 0; i < apk_assets_len; i++) { - jobject obj = env->GetObjectArrayElement(apk_assets_array, i); - if (obj == nullptr) { - std::string msg = StringPrintf("ApkAssets at index %d is null", i); - jniThrowNullPointerException(env, msg.c_str()); - return; +static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz, + jlong assetHandle, jbyteArray bArray, + jint off, jint len) +{ + Asset* a = reinterpret_cast(assetHandle); + + if (a == NULL || bArray == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; } - jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr); - if (env->ExceptionCheck()) { - return; + if (len == 0) { + return 0; + } + + jsize bLen = env->GetArrayLength(bArray); + if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); + return -1; } - apk_assets.push_back(reinterpret_cast(apk_assets_native_ptr)); - } - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - assetmanager->SetApkAssets(apk_assets, invalidate_caches); + jbyte* b = env->GetByteArrayElements(bArray, NULL); + ssize_t res = a->read(b+off, len); + env->ReleaseByteArrayElements(bArray, b, 0); + + if (res > 0) return static_cast(res); + + if (res < 0) { + jniThrowException(env, "java/io/IOException", ""); + } + return -1; } -static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc, - jstring locale, jint orientation, jint touchscreen, jint density, - jint keyboard, jint keyboard_hidden, jint navigation, - jint screen_width, jint screen_height, - jint smallest_screen_width_dp, jint screen_width_dp, - jint screen_height_dp, jint screen_layout, jint ui_mode, - jint color_mode, jint major_version) { - ResTable_config configuration; - memset(&configuration, 0, sizeof(configuration)); - configuration.mcc = static_cast(mcc); - configuration.mnc = static_cast(mnc); - configuration.orientation = static_cast(orientation); - configuration.touchscreen = static_cast(touchscreen); - configuration.density = static_cast(density); - configuration.keyboard = static_cast(keyboard); - configuration.inputFlags = static_cast(keyboard_hidden); - configuration.navigation = static_cast(navigation); - configuration.screenWidth = static_cast(screen_width); - configuration.screenHeight = static_cast(screen_height); - configuration.smallestScreenWidthDp = static_cast(smallest_screen_width_dp); - configuration.screenWidthDp = static_cast(screen_width_dp); - configuration.screenHeightDp = static_cast(screen_height_dp); - configuration.screenLayout = static_cast(screen_layout); - configuration.uiMode = static_cast(ui_mode); - configuration.colorMode = static_cast(color_mode); - configuration.sdkVersion = static_cast(major_version); - - if (locale != nullptr) { - ScopedUtfChars locale_utf8(env, locale); - CHECK(locale_utf8.c_str() != nullptr); - configuration.setBcp47Locale(locale_utf8.c_str()); - } - - // Constants duplicated from Java class android.content.res.Configuration. - static const jint kScreenLayoutRoundMask = 0x300; - static const jint kScreenLayoutRoundShift = 8; - - // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer - // in C++. We must extract the round qualifier out of the Java screenLayout and put it - // into screenLayout2. - configuration.screenLayout2 = - static_cast((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - assetmanager->SetConfiguration(configuration); +static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz, + jlong assetHandle, + jlong offset, jint whence) +{ + Asset* a = reinterpret_cast(assetHandle); + + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; + } + + return a->seek( + offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)); } -static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); +static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz, + jlong assetHandle) +{ + Asset* a = reinterpret_cast(assetHandle); - jobject sparse_array = - env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor); + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; + } - if (sparse_array == nullptr) { - // An exception is pending. - return nullptr; - } + return a->getLength(); +} - assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) { - jstring jpackage_name = env->NewStringUTF(package_name.c_str()); - if (jpackage_name == nullptr) { - // An exception is pending. - return; +static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz, + jlong assetHandle) +{ + Asset* a = reinterpret_cast(assetHandle); + + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; } - env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast(package_id), - jpackage_name); - }); - return sparse_array; + return a->getRemainingLength(); } -static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) { - ScopedUtfChars path_utf8(env, path); - if (path_utf8.c_str() == nullptr) { - // This will throw NPE. - return nullptr; - } - - std::vector all_file_paths; - { - StringPiece normalized_path = path_utf8.c_str(); - if (normalized_path.data()[0] == '/') { - normalized_path = normalized_path.substr(1); - } - std::string root_path = StringPrintf("assets/%s", normalized_path.data()); - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - for (const ApkAssets* assets : assetmanager->GetApkAssets()) { - assets->ForEachFile(root_path, [&](const StringPiece& file_path, FileType type) { - if (type == FileType::kFileTypeRegular) { - all_file_paths.push_back(file_path.to_string()); - } - }); +static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz, + jstring path, jboolean appAsLib) +{ + ScopedUtfChars path8(env, path); + if (path8.c_str() == NULL) { + return 0; } - } - jobjectArray array = env->NewObjectArray(all_file_paths.size(), g_stringClass, nullptr); - if (array == nullptr) { - return nullptr; - } + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } - jsize index = 0; - for (const std::string& file_path : all_file_paths) { - jstring java_string = env->NewStringUTF(file_path.c_str()); + int32_t cookie; + bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib); - // Check for errors creating the strings (if malformed or no memory). - if (env->ExceptionCheck()) { - return nullptr; + return (res) ? static_cast(cookie) : 0; +} + +static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz, + jstring idmapPath) +{ + ScopedUtfChars idmapPath8(env, idmapPath); + if (idmapPath8.c_str() == NULL) { + return 0; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; } - env->SetObjectArrayElement(array, index++, java_string); + int32_t cookie; + bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie); - // If we have a large amount of string in our array, we might overflow the - // local reference table of the VM. - env->DeleteLocalRef(java_string); - } - return array; + return (res) ? (jint)cookie : 0; } -static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, - jint access_mode) { - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return 0; - } - - if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && - access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return 0; - } - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr asset = - assetmanager->Open(asset_path_utf8.c_str(), static_cast(access_mode)); - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return 0; - } - return reinterpret_cast(asset.release()); +static jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz, + jobject fileDescriptor, jstring debugPathName, + jboolean appAsLib) +{ + ScopedUtfChars debugPathName8(env, debugPathName); + + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (fd < 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); + return 0; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + int dupfd = ::dup(fd); + if (dupfd < 0) { + jniThrowIOException(env, errno); + return 0; + } + + int32_t cookie; + bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib); + + return (res) ? static_cast(cookie) : 0; } -static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, - jlongArray out_offsets) { - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return nullptr; - } - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return nullptr; - } - return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); +static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_TRUE; + } + return am->isUpToDate() ? JNI_TRUE : JNI_FALSE; } -static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, - jstring asset_path, jint access_mode) { - ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return 0; - } - - if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && - access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return 0; - } - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr asset; - if (cookie != kInvalidCookie) { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, - static_cast(access_mode)); - } else { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), - static_cast(access_mode)); - } - - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return 0; - } - return reinterpret_cast(asset.release()); +static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales) +{ + Vector locales; + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + am->getLocales(&locales, includeSystemLocales); + + const int N = locales.size(); + + jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL); + if (result == NULL) { + return NULL; + } + + for (int i=0; iNewStringUTF(locales[i].string()); + if (str == NULL) { + return NULL; + } + env->SetObjectArrayElement(result, i, str); + env->DeleteLocalRef(str); + } + + return result; } -static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, - jstring asset_path, jlongArray out_offsets) { - ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return nullptr; - } - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr asset; - if (cookie != kInvalidCookie) { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); - } else { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); - } - - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return nullptr; - } - return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); +static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz) +{ + return getLocales(env, clazz, true /* include system locales */); } -static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie, - jstring asset_path) { - ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return 0; - } - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr asset; - if (cookie != kInvalidCookie) { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); - } else { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie); - } - - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return 0; - } - - // May be nullptr. - const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie); - - std::unique_ptr xml_tree = util::make_unique(dynamic_ref_table); - status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); - asset.reset(); - - if (err != NO_ERROR) { - jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); - return 0; - } - return reinterpret_cast(xml_tree.release()); +static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz) +{ + return getLocales(env, clazz, false /* don't include system locales */); } -static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, - jshort density, jobject typed_value, - jboolean resolve_references) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - ApkAssetsCookie cookie = - assetmanager->GetResource(static_cast(resid), false /*may_be_bag*/, - static_cast(density), &value, &selected_config, &flags); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - - uint32_t ref = static_cast(resid); - if (resolve_references) { - cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - } - return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value); +static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) { + jobject result = env->NewObject(gConfigurationOffsets.classObject, + gConfigurationOffsets.constructor); + if (result == NULL) { + return NULL; + } + + env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, + config.smallestScreenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); + + return result; } -static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, - jint bag_entry_id, jobject typed_value) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - - uint32_t type_spec_flags = bag->type_spec_flags; - ApkAssetsCookie cookie = kInvalidCookie; - const Res_value* bag_value = nullptr; - for (const ResolvedBag::Entry& entry : bag) { - if (entry.key == static_cast(bag_entry_id)) { - cookie = entry.cookie; - bag_value = &entry.value; - - // Keep searching (the old implementation did that). - } - } - - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - - Res_value value = *bag_value; - uint32_t ref = static_cast(resid); - ResTable_config selected_config; - cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value); +static jobjectArray getSizeConfigurationsInternal(JNIEnv* env, + const Vector& configs) { + const int N = configs.size(); + jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL); + if (result == NULL) { + return NULL; + } + + for (int i=0; iDeleteLocalRef(result); + return NULL; + } + + env->SetObjectArrayElement(result, i, config); + env->DeleteLocalRef(config); + } + + return result; } -static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return nullptr; - } - - jintArray array = env->NewIntArray(bag->entry_count); - if (env->ExceptionCheck()) { - return nullptr; - } - - for (uint32_t i = 0; i < bag->entry_count; i++) { - jint attr_resid = bag->entries[i].key; - env->SetIntArrayRegion(array, i, 1, &attr_resid); - } - return array; +static jobjectArray android_content_AssetManager_getSizeConfigurations(JNIEnv* env, jobject clazz) { + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + const ResTable& res(am->getResources()); + Vector configs; + res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */); + + return getSizeConfigurationsInternal(env, configs); } -static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, - jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return nullptr; - } - - jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr); - if (array == nullptr) { - return nullptr; - } - - for (uint32_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - - // Resolve any references to their final value. - Res_value value = entry.value; - ResTable_config selected_config; - uint32_t flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - return nullptr; - } - - if (value.dataType == Res_value::TYPE_STRING) { - const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie]; - const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool(); - - jstring java_string = nullptr; - size_t str_len; - const char* str_utf8 = pool->string8At(value.data, &str_len); - if (str_utf8 != nullptr) { - java_string = env->NewStringUTF(str_utf8); - } else { - const char16_t* str_utf16 = pool->stringAt(value.data, &str_len); - java_string = env->NewString(reinterpret_cast(str_utf16), str_len); - } - - // Check for errors creating the strings (if malformed or no memory). - if (env->ExceptionCheck()) { - return nullptr; - } - - env->SetObjectArrayElement(array, i, java_string); - - // If we have a large amount of string in our array, we might overflow the - // local reference table of the VM. - env->DeleteLocalRef(java_string); - } - } - return array; +static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz, + jint mcc, jint mnc, + jstring locale, jint orientation, + jint touchscreen, jint density, + jint keyboard, jint keyboardHidden, + jint navigation, + jint screenWidth, jint screenHeight, + jint smallestScreenWidthDp, + jint screenWidthDp, jint screenHeightDp, + jint screenLayout, jint uiMode, + jint colorMode, jint sdkVersion) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return; + } + + ResTable_config config; + memset(&config, 0, sizeof(config)); + + const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL; + + // Constants duplicated from Java class android.content.res.Configuration. + static const jint kScreenLayoutRoundMask = 0x300; + static const jint kScreenLayoutRoundShift = 8; + + config.mcc = (uint16_t)mcc; + config.mnc = (uint16_t)mnc; + config.orientation = (uint8_t)orientation; + config.touchscreen = (uint8_t)touchscreen; + config.density = (uint16_t)density; + config.keyboard = (uint8_t)keyboard; + config.inputFlags = (uint8_t)keyboardHidden; + config.navigation = (uint8_t)navigation; + config.screenWidth = (uint16_t)screenWidth; + config.screenHeight = (uint16_t)screenHeight; + config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp; + config.screenWidthDp = (uint16_t)screenWidthDp; + config.screenHeightDp = (uint16_t)screenHeightDp; + config.screenLayout = (uint8_t)screenLayout; + config.uiMode = (uint8_t)uiMode; + config.colorMode = (uint8_t)colorMode; + config.sdkVersion = (uint16_t)sdkVersion; + config.minorVersion = 0; + + // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer + // in C++. We must extract the round qualifier out of the Java screenLayout and put it + // into screenLayout2. + config.screenLayout2 = + (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); + + am->setConfiguration(config, locale8); + + if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8); } -static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr, - jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return nullptr; - } - - jintArray array = env->NewIntArray(bag->entry_count * 2); - if (array == nullptr) { - return nullptr; - } - - jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); - if (buffer == nullptr) { - return nullptr; - } - - for (size_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - Res_value value = entry.value; - ResTable_config selected_config; - uint32_t flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); - return nullptr; - } - - jint string_index = -1; - if (value.dataType == Res_value::TYPE_STRING) { - string_index = static_cast(value.data); - } - - buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie); - buffer[(i * 2) + 1] = string_index; - } - env->ReleasePrimitiveArrayCritical(array, buffer, 0); - return array; +static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz, + jstring name, + jstring defType, + jstring defPackage) +{ + ScopedStringChars name16(env, name); + if (name16.get() == NULL) { + return 0; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + const char16_t* defType16 = reinterpret_cast(defType) + ? reinterpret_cast(env->GetStringChars(defType, NULL)) + : NULL; + jsize defTypeLen = defType + ? env->GetStringLength(defType) : 0; + const char16_t* defPackage16 = reinterpret_cast(defPackage) + ? reinterpret_cast(env->GetStringChars(defPackage, + NULL)) + : NULL; + jsize defPackageLen = defPackage + ? env->GetStringLength(defPackage) : 0; + + jint ident = am->getResources().identifierForName( + reinterpret_cast(name16.get()), name16.size(), + defType16, defTypeLen, defPackage16, defPackageLen); + + if (defPackage16) { + env->ReleaseStringChars(defPackage, + reinterpret_cast(defPackage16)); + } + if (defType16) { + env->ReleaseStringChars(defType, + reinterpret_cast(defType16)); + } + + return ident; } -static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return nullptr; - } - - jintArray array = env->NewIntArray(bag->entry_count); - if (array == nullptr) { - return nullptr; - } - - jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); - if (buffer == nullptr) { - return nullptr; - } - - for (size_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - Res_value value = entry.value; - ResTable_config selected_config; - uint32_t flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); - return nullptr; - } - - if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) { - buffer[i] = static_cast(value.data); - } - } - env->ReleasePrimitiveArrayCritical(array, buffer, 0); - return array; +static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, true, &name)) { + return NULL; + } + + String16 str; + if (name.package != NULL) { + str.setTo(name.package, name.packageLen); + } + if (name.type8 != NULL || name.type != NULL) { + if (str.size() > 0) { + char16_t div = ':'; + str.append(&div, 1); + } + if (name.type8 != NULL) { + str.append(String16(name.type8, name.typeLen)); + } else { + str.append(name.type, name.typeLen); + } + } + if (name.name8 != NULL || name.name != NULL) { + if (str.size() > 0) { + char16_t div = '/'; + str.append(&div, 1); + } + if (name.name8 != NULL) { + str.append(String16(name.name8, name.nameLen)); + } else { + str.append(name.name, name.nameLen); + } + } + + return env->NewString((const jchar*)str.string(), str.size()); } -static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return -1; - } - return static_cast(bag->entry_count); +static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, true, &name)) { + return NULL; + } + + if (name.package != NULL) { + return env->NewString((const jchar*)name.package, name.packageLen); + } + + return NULL; } -static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, - jintArray out_data) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return -1; - } +static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } - const jsize out_data_length = env->GetArrayLength(out_data); - if (env->ExceptionCheck()) { - return -1; - } + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, true, &name)) { + return NULL; + } - if (static_cast(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough"); - return -1; - } + if (name.type8 != NULL) { + return env->NewStringUTF(name.type8); + } - jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(out_data, nullptr)); - if (buffer == nullptr) { - return -1; - } - - jint* cursor = buffer; - for (size_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - Res_value value = entry.value; - ResTable_config selected_config; - selected_config.density = 0; - uint32_t flags = bag->type_spec_flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT); - return -1; - } - - // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; - } - - cursor[STYLE_TYPE] = static_cast(value.dataType); - cursor[STYLE_DATA] = static_cast(value.data); - cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); - cursor[STYLE_RESOURCE_ID] = static_cast(ref); - cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast(flags); - cursor[STYLE_DENSITY] = static_cast(selected_config.density); - cursor += STYLE_NUM_ENTRIES; - } - env->ReleasePrimitiveArrayCritical(out_data, buffer, 0); - return static_cast(bag->entry_count); + if (name.type != NULL) { + return env->NewString((const jchar*)name.type, name.typeLen); + } + + return NULL; } -static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name, - jstring def_type, jstring def_package) { - ScopedUtfChars name_utf8(env, name); - if (name_utf8.c_str() == nullptr) { - // This will throw NPE. - return 0; - } - - std::string type; - if (def_type != nullptr) { - ScopedUtfChars type_utf8(env, def_type); - CHECK(type_utf8.c_str() != nullptr); - type = type_utf8.c_str(); - } - - std::string package; - if (def_package != nullptr) { - ScopedUtfChars package_utf8(env, def_package); - CHECK(package_utf8.c_str() != nullptr); - package = package_utf8.c_str(); - } - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - return static_cast(assetmanager->GetResourceId(name_utf8.c_str(), type, package)); +static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, true, &name)) { + return NULL; + } + + if (name.name8 != NULL) { + return env->NewStringUTF(name.name8); + } + + if (name.name != NULL) { + return env->NewString((const jchar*)name.name, name.nameLen); + } + + return NULL; } -static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast(resid), &name)) { - return nullptr; - } +static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz, + jint ident, + jshort density, + jobject outValue, + jboolean resolve) +{ + if (outValue == NULL) { + jniThrowNullPointerException(env, "outValue"); + return 0; + } + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + const ResTable& res(am->getResources()); + + Res_value value; + ResTable_config config; + uint32_t typeSpecFlags; + ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return 0; + } + } + uint32_t ref = ident; + if (resolve) { + block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return 0; + } + } + } + if (block >= 0) { + return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config); + } - std::string result; - if (name.package != nullptr) { - result.append(name.package, name.package_len); - } + return static_cast(block); +} - if (name.type != nullptr || name.type16 != nullptr) { - if (!result.empty()) { - result += ":"; +static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz, + jint ident, jint bagEntryId, + jobject outValue, jboolean resolve) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; } + const ResTable& res(am->getResources()); + + // Now lock down the resource object and start pulling stuff from it. + res.lock(); + + ssize_t block = -1; + Res_value value; - if (name.type != nullptr) { - result.append(name.type, name.type_len); - } else { - result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len)); + const ResTable::bag_entry* entry = NULL; + uint32_t typeSpecFlags; + ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags); + + for (ssize_t i=0; imap.name.ident) { + block = entry->stringBlock; + value = entry->map.value; + } + entry++; } - } - if (name.entry != nullptr || name.entry16 != nullptr) { - if (!result.empty()) { - result += "/"; + res.unlock(); + + if (block < 0) { + return static_cast(block); } - if (name.entry != nullptr) { - result.append(name.entry, name.entry_len); - } else { - result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); + uint32_t ref = ident; + if (resolve) { + block = res.resolveReference(&value, block, &ref, &typeSpecFlags); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return 0; + } + } + } + if (block >= 0) { + return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags); } - } - return env->NewStringUTF(result.c_str()); + + return static_cast(block); } -static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast(resid), &name)) { - return nullptr; - } - - if (name.package != nullptr) { - return env->NewStringUTF(name.package); - } - return nullptr; +static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + return am->getResources().getTableCount(); } -static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast(resid), &name)) { - return nullptr; - } - - if (name.type != nullptr) { - return env->NewStringUTF(name.type); - } else if (name.type16 != nullptr) { - return env->NewString(reinterpret_cast(name.type16), name.type_len); - } - return nullptr; +static jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz, + jint block) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + return reinterpret_cast(am->getResources().getTableStringBlock(block)); } -static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast(resid), &name)) { - return nullptr; - } - - if (name.entry != nullptr) { - return env->NewStringUTF(name.entry); - } else if (name.entry16 != nullptr) { - return env->NewString(reinterpret_cast(name.entry16), name.entry_len); - } - return nullptr; +static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz, + jint cookie) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + String8 name(am->getAssetPath(static_cast(cookie))); + if (name.length() == 0) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name"); + return NULL; + } + jstring str = env->NewStringUTF(name.string()); + return str; } -static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr, - jboolean exclude_system) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::set locales = - assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/); - - jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr); - if (array == nullptr) { - return nullptr; - } - - size_t idx = 0; - for (const std::string& locale : locales) { - jstring java_string = env->NewStringUTF(locale.c_str()); - if (java_string == nullptr) { - return nullptr; - } - env->SetObjectArrayElement(array, idx++, java_string); - env->DeleteLocalRef(java_string); - } - return array; +static jobject android_content_AssetManager_getAssignedPackageIdentifiers(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + const ResTable& res = am->getResources(); + + jobject sparseArray = env->NewObject(gSparseArrayOffsets.classObject, + gSparseArrayOffsets.constructor); + const size_t N = res.getBasePackageCount(); + for (size_t i = 0; i < N; i++) { + const String16 name = res.getBasePackageName(i); + env->CallVoidMethod( + sparseArray, gSparseArrayOffsets.put, + static_cast(res.getBasePackageId(i)), + env->NewString(reinterpret_cast(name.string()), + name.size())); + } + return sparseArray; } -static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) { - jobject result = - env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor); - if (result == nullptr) { - return nullptr; - } - - env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, - config.smallestScreenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); - return result; +static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + return reinterpret_cast(new ResTable::Theme(am->getResources())); } -static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::set configurations = - assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/); +static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz, + jlong themeHandle) +{ + ResTable::Theme* theme = reinterpret_cast(themeHandle); + delete theme; +} - jobjectArray array = - env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr); - if (array == nullptr) { - return nullptr; - } +static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz, + jlong themeHandle, + jint styleRes, + jboolean force) +{ + ResTable::Theme* theme = reinterpret_cast(themeHandle); + theme->applyStyle(styleRes, force ? true : false); +} - size_t idx = 0; - for (const ResTable_config& configuration : configurations) { - jobject java_configuration = ConstructConfigurationObject(env, configuration); - if (java_configuration == nullptr) { - return nullptr; +static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz, + jlong destHandle, jlong srcHandle) +{ + ResTable::Theme* dest = reinterpret_cast(destHandle); + ResTable::Theme* src = reinterpret_cast(srcHandle); + dest->setTo(*src); +} + +static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle) +{ + ResTable::Theme* theme = reinterpret_cast(themeHandle); + theme->clear(); +} + +static jint android_content_AssetManager_loadThemeAttributeValue( + JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve) +{ + ResTable::Theme* theme = reinterpret_cast(themeHandle); + const ResTable& res(theme->getResTable()); + + Res_value value; + // XXX value could be different in different configs! + uint32_t typeSpecFlags = 0; + ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags); + uint32_t ref = 0; + if (resolve) { + block = res.resolveReference(&value, block, &ref, &typeSpecFlags); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return 0; + } + } } + return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block; +} - env->SetObjectArrayElement(array, idx++, java_configuration); - env->DeleteLocalRef(java_configuration); - } - return array; +static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz, + jlong themeHandle) +{ + ResTable::Theme* theme = reinterpret_cast(themeHandle); + return theme->getChangingConfigurations(); } -static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr, - jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - - ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); - uint32_t* out_values = reinterpret_cast(out_values_ptr); - uint32_t* out_indices = reinterpret_cast(out_indices_ptr); - - jsize attrs_len = env->GetArrayLength(java_attrs); - jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); - if (attrs == nullptr) { - return; - } - - ApplyStyle(theme, xml_parser, static_cast(def_style_attr), - static_cast(def_style_resid), reinterpret_cast(attrs), attrs_len, - out_values, out_indices); - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); +static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz, + jlong themeHandle, jint pri, + jstring tag, jstring prefix) +{ + ResTable::Theme* theme = reinterpret_cast(themeHandle); + const ResTable& res(theme->getResTable()); + (void)res; + + // XXX Need to use params. + theme->dumpToLog(); } -static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint def_style_attr, jint def_style_resid, jintArray java_values, - jintArray java_attrs, jintArray out_java_values, - jintArray out_java_indices) { - const jsize attrs_len = env->GetArrayLength(java_attrs); - const jsize out_values_len = env->GetArrayLength(out_java_values); - if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); - return JNI_FALSE; - } - - jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); - if (attrs == nullptr) { - return JNI_FALSE; - } - - jint* values = nullptr; - jsize values_len = 0; - if (java_values != nullptr) { - values_len = env->GetArrayLength(java_values); - values = reinterpret_cast(env->GetPrimitiveArrayCritical(java_values, nullptr)); - if (values == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - return JNI_FALSE; - } - } - - jint* out_values = - reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); - if (out_values == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - if (values != nullptr) { - env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); - } - return JNI_FALSE; - } - - jint* out_indices = nullptr; - if (out_java_indices != nullptr) { - jsize out_indices_len = env->GetArrayLength(out_java_indices); - if (out_indices_len > attrs_len) { - out_indices = - reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); - if (out_indices == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - if (values != nullptr) { - env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); - } - env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); +static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz, + jlong themeToken, + jint defStyleAttr, + jint defStyleRes, + jintArray inValues, + jintArray attrs, + jintArray outValues, + jintArray outIndices) +{ + if (themeToken == 0) { + jniThrowNullPointerException(env, "theme token"); return JNI_FALSE; - } - } - } - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - - bool result = ResolveAttrs( - theme, static_cast(def_style_attr), static_cast(def_style_resid), - reinterpret_cast(values), values_len, reinterpret_cast(attrs), - attrs_len, reinterpret_cast(out_values), reinterpret_cast(out_indices)); - if (out_indices != nullptr) { - env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); - } - - env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); - if (values != nullptr) { - env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); - } - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - return result ? JNI_TRUE : JNI_FALSE; -} + } + if (attrs == NULL) { + jniThrowNullPointerException(env, "attrs"); + return JNI_FALSE; + } + if (outValues == NULL) { + jniThrowNullPointerException(env, "out values"); + return JNI_FALSE; + } -static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, - jlong xml_parser_ptr, jintArray java_attrs, - jintArray out_java_values, jintArray out_java_indices) { - const jsize attrs_len = env->GetArrayLength(java_attrs); - const jsize out_values_len = env->GetArrayLength(out_java_values); - if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); - return JNI_FALSE; - } - - jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); - if (attrs == nullptr) { - return JNI_FALSE; - } - - jint* out_values = - reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); - if (out_values == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - return JNI_FALSE; - } - - jint* out_indices = nullptr; - if (out_java_indices != nullptr) { - jsize out_indices_len = env->GetArrayLength(out_java_indices); - if (out_indices_len > attrs_len) { - out_indices = - reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); - if (out_indices == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); + const jsize NI = env->GetArrayLength(attrs); + const jsize NV = env->GetArrayLength(outValues); + if (NV < (NI*STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); return JNI_FALSE; - } } - } - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); + jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); + if (src == NULL) { + return JNI_FALSE; + } - bool result = RetrieveAttributes(assetmanager.get(), xml_parser, - reinterpret_cast(attrs), attrs_len, - reinterpret_cast(out_values), - reinterpret_cast(out_indices)); + jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0); + const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues); - if (out_indices != nullptr) { - env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); - } - env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - return result ? JNI_TRUE : JNI_FALSE; -} + jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + if (baseDest == NULL) { + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + return JNI_FALSE; + } + + jint* indices = NULL; + if (outIndices != NULL) { + if (env->GetArrayLength(outIndices) > NI) { + indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); + } + } -static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - return reinterpret_cast(assetmanager->NewTheme().release()); + ResTable::Theme* theme = reinterpret_cast(themeToken); + bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes, + (uint32_t*) srcValues, NSV, + (uint32_t*) src, NI, + (uint32_t*) baseDest, + (uint32_t*) indices); + + if (indices != NULL) { + env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); + } + env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); + env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0); + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + return result ? JNI_TRUE : JNI_FALSE; } -static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { - delete reinterpret_cast(theme_ptr); +static void android_content_AssetManager_applyStyle(JNIEnv* env, jobject, jlong themeToken, + jint defStyleAttr, jint defStyleRes, jlong xmlParserToken, jintArray attrsObj, jint length, + jlong outValuesAddress, jlong outIndicesAddress) { + jint* attrs = env->GetIntArrayElements(attrsObj, 0); + ResTable::Theme* theme = reinterpret_cast(themeToken); + ResXMLParser* xmlParser = reinterpret_cast(xmlParserToken); + uint32_t* outValues = reinterpret_cast(static_cast(outValuesAddress)); + uint32_t* outIndices = reinterpret_cast(static_cast(outIndicesAddress)); + ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes, + reinterpret_cast(attrs), length, outValues, outIndices); + env->ReleaseIntArrayElements(attrsObj, attrs, JNI_ABORT); } -static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint resid, jboolean force) { - // AssetManager is accessed via the theme, so grab an explicit lock here. - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - theme->ApplyStyle(static_cast(resid), force); - - // TODO(adamlesinski): Consider surfacing exception when result is failure. - // CTS currently expects no exceptions from this method. - // std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid); - // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str()); +static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz, + jlong xmlParserToken, + jintArray attrs, + jintArray outValues, + jintArray outIndices) +{ + if (xmlParserToken == 0) { + jniThrowNullPointerException(env, "xmlParserToken"); + return JNI_FALSE; + } + if (attrs == NULL) { + jniThrowNullPointerException(env, "attrs"); + return JNI_FALSE; + } + if (outValues == NULL) { + jniThrowNullPointerException(env, "out values"); + return JNI_FALSE; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_FALSE; + } + const ResTable& res(am->getResources()); + ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken; + + const jsize NI = env->GetArrayLength(attrs); + const jsize NV = env->GetArrayLength(outValues); + if (NV < (NI*STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); + return JNI_FALSE; + } + + jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); + if (src == NULL) { + return JNI_FALSE; + } + + jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + if (baseDest == NULL) { + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + return JNI_FALSE; + } + + jint* indices = NULL; + if (outIndices != NULL) { + if (env->GetArrayLength(outIndices) > NI) { + indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); + } + } + + bool result = RetrieveAttributes(&res, xmlParser, + (uint32_t*) src, NI, + (uint32_t*) baseDest, + (uint32_t*) indices); + + if (indices != NULL) { + env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); + } + env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + return result ? JNI_TRUE : JNI_FALSE; } -static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr, - jlong src_theme_ptr) { - Theme* dst_theme = reinterpret_cast(dst_theme_ptr); - Theme* src_theme = reinterpret_cast(src_theme_ptr); - if (!dst_theme->SetTo(*src_theme)) { - jniThrowException(env, "java/lang/IllegalArgumentException", - "Themes are from different AssetManagers"); - } +static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz, + jint id) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + const ResTable& res(am->getResources()); + + res.lock(); + const ResTable::bag_entry* defStyleEnt = NULL; + ssize_t bagOff = res.getBagLocked(id, &defStyleEnt); + res.unlock(); + + return static_cast(bagOff); } -static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { - reinterpret_cast(theme_ptr)->Clear(); +static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz, + jint id, + jintArray outValues) +{ + if (outValues == NULL) { + jniThrowNullPointerException(env, "out values"); + return JNI_FALSE; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_FALSE; + } + const ResTable& res(am->getResources()); + ResTable_config config; + Res_value value; + ssize_t block; + + const jsize NV = env->GetArrayLength(outValues); + + jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + jint* dest = baseDest; + if (dest == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", ""); + return JNI_FALSE; + } + + // Now lock down the resource object and start pulling stuff from it. + res.lock(); + + const ResTable::bag_entry* arrayEnt = NULL; + uint32_t arrayTypeSetFlags = 0; + ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags); + const ResTable::bag_entry* endArrayEnt = arrayEnt + + (bagOff >= 0 ? bagOff : 0); + + int i = 0; + uint32_t typeSetFlags; + while (i < NV && arrayEnt < endArrayEnt) { + block = arrayEnt->stringBlock; + typeSetFlags = arrayTypeSetFlags; + config.density = 0; + value = arrayEnt->map.value; + + uint32_t resid = 0; + if (value.dataType != Res_value::TYPE_NULL) { + // Take care of resolving the found resource to its final value. + //printf("Resolving attribute reference\n"); + ssize_t newBlock = res.resolveReference(&value, block, &resid, + &typeSetFlags, &config); + if (kThrowOnBadId) { + if (newBlock == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return JNI_FALSE; + } + } + if (newBlock >= 0) block = newBlock; + } + + // Deal with the special @null value -- it turns back to TYPE_NULL. + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + } + + //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data); + + // Write the final value back to Java. + dest[STYLE_TYPE] = value.dataType; + dest[STYLE_DATA] = value.data; + dest[STYLE_ASSET_COOKIE] = reinterpret_cast(res.getTableCookie(block)); + dest[STYLE_RESOURCE_ID] = resid; + dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags; + dest[STYLE_DENSITY] = config.density; + dest += STYLE_NUM_ENTRIES; + i+= STYLE_NUM_ENTRIES; + arrayEnt++; + } + + i /= STYLE_NUM_ENTRIES; + + res.unlock(); + + env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); + + return i; } -static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint resid, jobject typed_value, - jboolean resolve_references) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - - Res_value value; - uint32_t flags; - ApkAssetsCookie cookie = theme->GetAttribute(static_cast(resid), &value, &flags); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - - uint32_t ref = 0u; - if (resolve_references) { - ResTable_config selected_config; - cookie = - theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - } - return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value); +static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz, + jint cookie, + jstring fileName) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return 0; + } + + int32_t assetCookie = static_cast(cookie); + Asset* a = assetCookie + ? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER) + : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return 0; + } + + const DynamicRefTable* dynamicRefTable = + am->getResources().getDynamicRefTableForCookie(assetCookie); + ResXMLTree* block = new ResXMLTree(dynamicRefTable); + status_t err = block->setTo(a->getBuffer(true), a->getLength(), true); + a->close(); + delete a; + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + + return reinterpret_cast(block); } -static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint priority, jstring tag, jstring prefix) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - (void) theme; - (void) priority; - (void) tag; - (void) prefix; +static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz, + jint arrayResId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(arrayResId, &startOfBag); + if (N < 0) { + return NULL; + } + + jintArray array = env->NewIntArray(N * 2); + if (array == NULL) { + res.unlockBag(startOfBag); + return NULL; + } + + Res_value value; + const ResTable::bag_entry* bag = startOfBag; + for (size_t i = 0, j = 0; ((ssize_t)i)map.value; + + // Take care of resolving the found resource to its final value. + stringBlock = res.resolveReference(&value, bag->stringBlock, NULL); + if (value.dataType == Res_value::TYPE_STRING) { + stringIndex = value.data; + } + + if (kThrowOnBadId) { + if (stringBlock == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return array; + } + } + + //todo: It might be faster to allocate a C array to contain + // the blocknums and indices, put them in there and then + // do just one SetIntArrayRegion() + env->SetIntArrayRegion(array, j, 1, &stringBlock); + env->SetIntArrayRegion(array, j + 1, 1, &stringIndex); + j = j + 2; + } + res.unlockBag(startOfBag); + return array; } -static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/, - jlong theme_ptr) { - Theme* theme = reinterpret_cast(theme_ptr); - return static_cast(theme->GetChangingConfigurations()); +static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz, + jint arrayResId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(arrayResId, &startOfBag); + if (N < 0) { + return NULL; + } + + jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL); + if (env->ExceptionCheck()) { + res.unlockBag(startOfBag); + return NULL; + } + + Res_value value; + const ResTable::bag_entry* bag = startOfBag; + size_t strLen = 0; + for (size_t i=0; ((ssize_t)i)map.value; + jstring str = NULL; + + // Take care of resolving the found resource to its final value. + ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return array; + } + } + if (value.dataType == Res_value::TYPE_STRING) { + const ResStringPool* pool = res.getTableStringBlock(block); + const char* str8 = pool->string8At(value.data, &strLen); + if (str8 != NULL) { + str = env->NewStringUTF(str8); + } else { + const char16_t* str16 = pool->stringAt(value.data, &strLen); + str = env->NewString(reinterpret_cast(str16), + strLen); + } + + // If one of our NewString{UTF} calls failed due to memory, an + // exception will be pending. + if (env->ExceptionCheck()) { + res.unlockBag(startOfBag); + return NULL; + } + + env->SetObjectArrayElement(array, i, str); + + // str is not NULL at that point, otherwise ExceptionCheck would have been true. + // If we have a large amount of strings in our array, we might + // overflow the local reference table of the VM. + env->DeleteLocalRef(str); + } + } + res.unlockBag(startOfBag); + return array; } -static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - delete reinterpret_cast(asset_ptr); +static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz, + jint arrayResId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(arrayResId, &startOfBag); + if (N < 0) { + return NULL; + } + + jintArray array = env->NewIntArray(N); + if (array == NULL) { + res.unlockBag(startOfBag); + return NULL; + } + + Res_value value; + const ResTable::bag_entry* bag = startOfBag; + for (size_t i=0; ((ssize_t)i)map.value; + + // Take care of resolving the found resource to its final value. + ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return array; + } + } + if (value.dataType >= Res_value::TYPE_FIRST_INT + && value.dataType <= Res_value::TYPE_LAST_INT) { + int intVal = value.data; + env->SetIntArrayRegion(array, i, 1, &intVal); + } + } + res.unlockBag(startOfBag); + return array; } -static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - Asset* asset = reinterpret_cast(asset_ptr); - uint8_t b; - ssize_t res = asset->read(&b, sizeof(b)); - return res == sizeof(b) ? static_cast(b) : -1; +static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz, + jint styleId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(styleId, &startOfBag); + if (N < 0) { + return NULL; + } + + jintArray array = env->NewIntArray(N); + if (array == NULL) { + res.unlockBag(startOfBag); + return NULL; + } + + const ResTable::bag_entry* bag = startOfBag; + for (size_t i=0; ((ssize_t)i)map.name.ident; + env->SetIntArrayRegion(array, i, 1, &resourceId); + } + res.unlockBag(startOfBag); + return array; } -static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer, - jint offset, jint len) { - if (len == 0) { - return 0; - } +static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem) +{ + if (isSystem) { + verifySystemIdmaps(); + } + AssetManager* am = new AssetManager(); + if (am == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", ""); + return; + } - jsize buffer_len = env->GetArrayLength(java_buffer); - if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len || - offset > buffer_len - len) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); - return -1; - } + am->addDefaultAssets(); - ScopedByteArrayRW byte_array(env, java_buffer); - if (byte_array.get() == nullptr) { - return -1; - } + ALOGV("Created AssetManager %p for Java object %p\n", am, clazz); + env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast(am)); +} - Asset* asset = reinterpret_cast(asset_ptr); - ssize_t res = asset->read(byte_array.get() + offset, len); - if (res < 0) { - jniThrowException(env, "java/io/IOException", ""); - return -1; - } - return res > 0 ? static_cast(res) : -1; +static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz) +{ + AssetManager* am = (AssetManager*) + (env->GetLongField(clazz, gAssetManagerOffsets.mObject)); + ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz); + if (am != NULL) { + delete am; + env->SetLongField(clazz, gAssetManagerOffsets.mObject, 0); + } } -static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset, - jint whence) { - Asset* asset = reinterpret_cast(asset_ptr); - return static_cast(asset->seek( - static_cast(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)))); +static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz) +{ + return Asset::getGlobalCount(); } -static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - Asset* asset = reinterpret_cast(asset_ptr); - return static_cast(asset->getLength()); +static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz) +{ + String8 alloc = Asset::getAssetAllocations(); + if (alloc.length() <= 0) { + return NULL; + } + + jstring str = env->NewStringUTF(alloc.string()); + return str; } -static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - Asset* asset = reinterpret_cast(asset_ptr); - return static_cast(asset->getRemainingLength()); +static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz) +{ + return AssetManager::getGlobalCount(); } // ---------------------------------------------------------------------------- -// JNI registration. +/* + * JNI registration. + */ static const JNINativeMethod gAssetManagerMethods[] = { - // AssetManager setup methods. - {"nativeCreate", "()J", (void*)NativeCreate}, - {"nativeDestroy", "(J)V", (void*)NativeDestroy}, - {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets}, - {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V", - (void*)NativeSetConfiguration}, - {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;", - (void*)NativeGetAssignedPackageIdentifiers}, - - // AssetManager file methods. - {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList}, - {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset}, - {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*)NativeOpenAssetFd}, - {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset}, - {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*)NativeOpenNonAssetFd}, - {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset}, - - // AssetManager resource methods. - {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue}, - {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I", - (void*)NativeGetResourceBagValue}, - {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes}, - {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;", - (void*)NativeGetResourceStringArray}, - {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo}, - {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray}, - {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize}, - {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray}, - - // AssetManager resource name/ID methods. - {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", - (void*)NativeGetResourceIdentifier}, - {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName}, - {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName}, - {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName}, - {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName}, - {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales}, - {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;", - (void*)NativeGetSizeConfigurations}, - - // Style attribute related methods. - {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle}, - {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs}, - {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes}, - - // Theme related methods. - {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate}, - {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy}, - {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle}, - {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy}, - {"nativeThemeClear", "(J)V", (void*)NativeThemeClear}, - {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I", - (void*)NativeThemeGetAttributeValue}, - {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump}, - {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations}, - - // AssetInputStream methods. - {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy}, - {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar}, - {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead}, - {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek}, - {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength}, - {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength}, - - // System/idmap related methods. - {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps}, - - // Global management/debug methods. - {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount}, - {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations}, - {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount}, + /* name, signature, funcPtr */ + + // Basic asset stuff. + { "openAsset", "(Ljava/lang/String;I)J", + (void*) android_content_AssetManager_openAsset }, + { "openAssetFd", "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*) android_content_AssetManager_openAssetFd }, + { "openNonAssetNative", "(ILjava/lang/String;I)J", + (void*) android_content_AssetManager_openNonAssetNative }, + { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*) android_content_AssetManager_openNonAssetFdNative }, + { "list", "(Ljava/lang/String;)[Ljava/lang/String;", + (void*) android_content_AssetManager_list }, + { "destroyAsset", "(J)V", + (void*) android_content_AssetManager_destroyAsset }, + { "readAssetChar", "(J)I", + (void*) android_content_AssetManager_readAssetChar }, + { "readAsset", "(J[BII)I", + (void*) android_content_AssetManager_readAsset }, + { "seekAsset", "(JJI)J", + (void*) android_content_AssetManager_seekAsset }, + { "getAssetLength", "(J)J", + (void*) android_content_AssetManager_getAssetLength }, + { "getAssetRemainingLength", "(J)J", + (void*) android_content_AssetManager_getAssetRemainingLength }, + { "addAssetPathNative", "(Ljava/lang/String;Z)I", + (void*) android_content_AssetManager_addAssetPath }, + { "addAssetFdNative", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)I", + (void*) android_content_AssetManager_addAssetFd }, + { "addOverlayPathNative", "(Ljava/lang/String;)I", + (void*) android_content_AssetManager_addOverlayPath }, + { "isUpToDate", "()Z", + (void*) android_content_AssetManager_isUpToDate }, + + // Resources. + { "getLocales", "()[Ljava/lang/String;", + (void*) android_content_AssetManager_getLocales }, + { "getNonSystemLocales", "()[Ljava/lang/String;", + (void*) android_content_AssetManager_getNonSystemLocales }, + { "getSizeConfigurations", "()[Landroid/content/res/Configuration;", + (void*) android_content_AssetManager_getSizeConfigurations }, + { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIIII)V", + (void*) android_content_AssetManager_setConfiguration }, + { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + (void*) android_content_AssetManager_getResourceIdentifier }, + { "getResourceName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourceName }, + { "getResourcePackageName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourcePackageName }, + { "getResourceTypeName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourceTypeName }, + { "getResourceEntryName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourceEntryName }, + { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I", + (void*) android_content_AssetManager_loadResourceValue }, + { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I", + (void*) android_content_AssetManager_loadResourceBagValue }, + { "getStringBlockCount","()I", + (void*) android_content_AssetManager_getStringBlockCount }, + { "getNativeStringBlock","(I)J", + (void*) android_content_AssetManager_getNativeStringBlock }, + { "getCookieName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getCookieName }, + { "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;", + (void*) android_content_AssetManager_getAssignedPackageIdentifiers }, + + // Themes. + { "newTheme", "()J", + (void*) android_content_AssetManager_newTheme }, + { "deleteTheme", "(J)V", + (void*) android_content_AssetManager_deleteTheme }, + { "applyThemeStyle", "(JIZ)V", + (void*) android_content_AssetManager_applyThemeStyle }, + { "copyTheme", "(JJ)V", + (void*) android_content_AssetManager_copyTheme }, + { "clearTheme", "(J)V", + (void*) android_content_AssetManager_clearTheme }, + { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I", + (void*) android_content_AssetManager_loadThemeAttributeValue }, + { "getThemeChangingConfigurations", "(J)I", + (void*) android_content_AssetManager_getThemeChangingConfigurations }, + { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V", + (void*) android_content_AssetManager_dumpTheme }, + { "applyStyle","(JIIJ[IIJJ)V", + (void*) android_content_AssetManager_applyStyle }, + { "resolveAttrs","(JII[I[I[I[I)Z", + (void*) android_content_AssetManager_resolveAttrs }, + { "retrieveAttributes","(J[I[I[I)Z", + (void*) android_content_AssetManager_retrieveAttributes }, + { "getArraySize","(I)I", + (void*) android_content_AssetManager_getArraySize }, + { "retrieveArray","(I[I)I", + (void*) android_content_AssetManager_retrieveArray }, + + // XML files. + { "openXmlAssetNative", "(ILjava/lang/String;)J", + (void*) android_content_AssetManager_openXmlAssetNative }, + + // Arrays. + { "getArrayStringResource","(I)[Ljava/lang/String;", + (void*) android_content_AssetManager_getArrayStringResource }, + { "getArrayStringInfo","(I)[I", + (void*) android_content_AssetManager_getArrayStringInfo }, + { "getArrayIntResource","(I)[I", + (void*) android_content_AssetManager_getArrayIntResource }, + { "getStyleAttributes","(I)[I", + (void*) android_content_AssetManager_getStyleAttributes }, + + // Bookkeeping. + { "init", "(Z)V", + (void*) android_content_AssetManager_init }, + { "destroy", "()V", + (void*) android_content_AssetManager_destroy }, + { "getGlobalAssetCount", "()I", + (void*) android_content_AssetManager_getGlobalAssetCount }, + { "getAssetAllocations", "()Ljava/lang/String;", + (void*) android_content_AssetManager_getAssetAllocations }, + { "getGlobalAssetManagerCount", "()I", + (void*) android_content_AssetManager_getGlobalAssetManagerCount }, }; -int register_android_content_AssetManager(JNIEnv* env) { - jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets"); - gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J"); - - jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); - gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); - gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); - gTypedValueOffsets.mString = - GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;"); - gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); - gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); - gTypedValueOffsets.mChangingConfigurations = - GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I"); - gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); - - jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); - gAssetFileDescriptorOffsets.mFd = - GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;"); - gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); - gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); - - jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); - gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); - - jclass stringClass = FindClassOrDie(env, "java/lang/String"); - g_stringClass = MakeGlobalRefOrDie(env, stringClass); - - jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); - gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); - gSparseArrayOffsets.constructor = - GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "", "()V"); - gSparseArrayOffsets.put = - GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V"); - - jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); - gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); - gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "", "()V"); - gConfigurationOffsets.mSmallestScreenWidthDpOffset = - GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I"); - gConfigurationOffsets.mScreenWidthDpOffset = - GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I"); - gConfigurationOffsets.mScreenHeightDpOffset = - GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I"); - - return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, - NELEM(gAssetManagerMethods)); +int register_android_content_AssetManager(JNIEnv* env) +{ + jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); + gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); + gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); + gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string", + "Ljava/lang/CharSequence;"); + gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); + gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); + gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue, + "changingConfigurations", "I"); + gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); + + jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); + gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd", + "Landroid/os/ParcelFileDescriptor;"); + gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); + gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); + + jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); + gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); + + jclass stringClass = FindClassOrDie(env, "java/lang/String"); + g_stringClass = MakeGlobalRefOrDie(env, stringClass); + + jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); + gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); + gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, + "", "()V"); + gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", + "(ILjava/lang/Object;)V"); + + jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); + gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); + gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, + "", "()V"); + gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, + "smallestScreenWidthDp", "I"); + gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, + "screenWidthDp", "I"); + gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass, + "screenHeightDp", "I"); + + return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, + NELEM(gAssetManagerMethods)); } }; // namespace android diff --git a/core/jni/include/android_runtime/android_util_AssetManager.h b/core/jni/include/android_runtime/android_util_AssetManager.h index 2c1e3579eb92..8dd933707a6a 100644 --- a/core/jni/include/android_runtime/android_util_AssetManager.h +++ b/core/jni/include/android_runtime/android_util_AssetManager.h @@ -14,20 +14,17 @@ * limitations under the License. */ -#ifndef ANDROID_RUNTIME_ASSETMANAGER_H -#define ANDROID_RUNTIME_ASSETMANAGER_H +#ifndef android_util_AssetManager_H +#define android_util_AssetManager_H -#include "androidfw/AssetManager2.h" -#include "androidfw/MutexGuard.h" +#include #include "jni.h" namespace android { -extern AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); -extern Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); -extern Guarded* AssetManagerForNdkAssetManager(AAssetManager* assetmanager); +extern AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject assetMgr); -} // namespace android +} -#endif // ANDROID_RUNTIME_ASSETMANAGER_H +#endif diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 2fc8e952707b..415d3e36adf9 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -265,6 +265,8 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, FindEntryResult* out_entry) { + ATRACE_CALL(); + // Might use this if density_override != 0. ResTable_config density_override_config; @@ -427,7 +429,9 @@ ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_valu for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && in_out_value->data != 0u && iteration < kMaxIterations; iteration++) { - *out_last_reference = in_out_value->data; + if (out_last_reference != nullptr) { + *out_last_reference = in_out_value->data; + } uint32_t new_flags = 0u; cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/, in_out_value, in_out_selected_config, &new_flags); diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index f912af4f7190..60e3845d98a9 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -20,18 +20,13 @@ #include -#include "androidfw/AssetManager2.h" #include "androidfw/AttributeFinder.h" +#include "androidfw/ResourceTypes.h" constexpr bool kDebugStyles = false; namespace android { -// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. -static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { - return cookie != kInvalidCookie ? static_cast(cookie + 1) : static_cast(-1); -} - class XmlAttributeFinder : public BackTrackingAttributeFinder { public: @@ -49,53 +44,58 @@ class XmlAttributeFinder }; class BagAttributeFinder - : public BackTrackingAttributeFinder { + : public BackTrackingAttributeFinder { public: - BagAttributeFinder(const ResolvedBag* bag) - : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr, - bag != nullptr ? bag->entries + bag->entry_count : nullptr) { - } + BagAttributeFinder(const ResTable::bag_entry* start, + const ResTable::bag_entry* end) + : BackTrackingAttributeFinder(start, end) {} - inline uint32_t GetAttribute(const ResolvedBag::Entry* entry) const { - return entry->key; + inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const { + return entry->map.name.ident; } }; -bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, - uint32_t* src_values, size_t src_values_length, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { +bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, + uint32_t def_style_res, uint32_t* src_values, + size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, + uint32_t* out_indices) { if (kDebugStyles) { ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr, def_style_res); } - AssetManager2* assetmanager = theme->GetAssetManager(); + const ResTable& res = theme->getResTable(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_flags = 0u; + uint32_t def_style_bag_type_set_flags = 0; if (def_style_attr != 0) { Res_value value; - if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { + if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) { if (value.dataType == Res_value::TYPE_REFERENCE) { def_style_res = value.data; } } } - // Retrieve the default style bag, if requested. - const ResolvedBag* default_style_bag = nullptr; - if (def_style_res != 0) { - default_style_bag = assetmanager->GetBag(def_style_res); - if (default_style_bag != nullptr) { - def_style_flags |= default_style_bag->type_spec_flags; - } - } + // Now lock down the resource object and start pulling stuff from it. + res.lock(); - BagAttributeFinder def_style_attr_finder(default_style_bag); + // Retrieve the default style bag, if requested. + const ResTable::bag_entry* def_style_start = nullptr; + uint32_t def_style_type_set_flags = 0; + ssize_t bag_off = def_style_res != 0 + ? res.getBagLocked(def_style_res, &def_style_start, + &def_style_type_set_flags) + : -1; + def_style_type_set_flags |= def_style_bag_type_set_flags; + const ResTable::bag_entry* const def_style_end = + def_style_start + (bag_off >= 0 ? bag_off : 0); + BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -106,7 +106,7 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ApkAssetsCookie cookie = kInvalidCookie; + ssize_t block = -1; uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; @@ -122,14 +122,15 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, value.dataType = Res_value::TYPE_ATTRIBUTE; value.data = src_values[ii]; if (kDebugStyles) { - ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data); + ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, + value.data); } } else { - const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident); - if (entry != def_style_attr_finder.end()) { - cookie = entry->cookie; - type_set_flags = def_style_flags; - value = entry->value; + const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident); + if (def_style_entry != def_style_end) { + block = def_style_entry->stringBlock; + type_set_flags = def_style_type_set_flags; + value = def_style_entry->map.value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -139,26 +140,22 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } + ssize_t new_block = + theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); + if (new_block >= 0) block = new_block; if (kDebugStyles) { ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data); } } else if (value.data != Res_value::DATA_NULL_EMPTY) { - // If we still don't have a value for this attribute, try to find it in the theme! - ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); - if (new_cookie != kInvalidCookie) { + // If we still don't have a value for this attribute, try to find + // it in the theme! + ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); + if (new_block >= 0) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_cookie = - assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } + new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); + if (new_block >= 0) block = new_block; if (kDebugStyles) { ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -172,7 +169,7 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + block = -1; } if (kDebugStyles) { @@ -182,7 +179,9 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + out_values[STYLE_ASSET_COOKIE] = + block != -1 ? static_cast(res.getTableCookie(block)) + : static_cast(-1); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -196,80 +195,90 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, out_values += STYLE_NUM_ENTRIES; } + res.unlock(); + if (out_indices != nullptr) { out_indices[0] = indices_idx; } return true; } -void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { - ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, - def_style_attr, def_style_resid, xml_parser); + ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", + theme, def_style_attr, def_style_res, xml_parser); } - AssetManager2* assetmanager = theme->GetAssetManager(); + const ResTable& res = theme->getResTable(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_flags = 0u; + uint32_t def_style_bag_type_set_flags = 0; if (def_style_attr != 0) { Res_value value; - if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { + if (theme->getAttribute(def_style_attr, &value, + &def_style_bag_type_set_flags) >= 0) { if (value.dataType == Res_value::TYPE_REFERENCE) { - def_style_resid = value.data; + def_style_res = value.data; } } } - // Retrieve the style resource ID associated with the current XML tag's style attribute. - uint32_t style_resid = 0u; - uint32_t style_flags = 0u; + // Retrieve the style class associated with the current XML tag. + int style = 0; + uint32_t style_bag_type_set_flags = 0; if (xml_parser != nullptr) { ssize_t idx = xml_parser->indexOfStyle(); if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) { if (value.dataType == value.TYPE_ATTRIBUTE) { - // Resolve the attribute with out theme. - if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) { + if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) { value.dataType = Res_value::TYPE_NULL; } } - if (value.dataType == value.TYPE_REFERENCE) { - style_resid = value.data; + style = value.data; } } } - // Retrieve the default style bag, if requested. - const ResolvedBag* default_style_bag = nullptr; - if (def_style_resid != 0) { - default_style_bag = assetmanager->GetBag(def_style_resid); - if (default_style_bag != nullptr) { - def_style_flags |= default_style_bag->type_spec_flags; - } - } + // Now lock down the resource object and start pulling stuff from it. + res.lock(); - BagAttributeFinder def_style_attr_finder(default_style_bag); + // Retrieve the default style bag, if requested. + const ResTable::bag_entry* def_style_attr_start = nullptr; + uint32_t def_style_type_set_flags = 0; + ssize_t bag_off = def_style_res != 0 + ? res.getBagLocked(def_style_res, &def_style_attr_start, + &def_style_type_set_flags) + : -1; + def_style_type_set_flags |= def_style_bag_type_set_flags; + const ResTable::bag_entry* const def_style_attr_end = + def_style_attr_start + (bag_off >= 0 ? bag_off : 0); + BagAttributeFinder def_style_attr_finder(def_style_attr_start, + def_style_attr_end); // Retrieve the style class bag, if requested. - const ResolvedBag* xml_style_bag = nullptr; - if (style_resid != 0) { - xml_style_bag = assetmanager->GetBag(style_resid); - if (xml_style_bag != nullptr) { - style_flags |= xml_style_bag->type_spec_flags; - } - } - - BagAttributeFinder xml_style_attr_finder(xml_style_bag); + const ResTable::bag_entry* style_attr_start = nullptr; + uint32_t style_type_set_flags = 0; + bag_off = + style != 0 + ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags) + : -1; + style_type_set_flags |= style_bag_type_set_flags; + const ResTable::bag_entry* const style_attr_end = + style_attr_start + (bag_off >= 0 ? bag_off : 0); + BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end); // Retrieve the XML attributes, if requested. + static const ssize_t kXmlBlock = 0x10000000; XmlAttributeFinder xml_attr_finder(xml_parser); + const size_t xml_attr_end = + xml_parser != nullptr ? xml_parser->getAttributeCount() : 0; // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -280,8 +289,8 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ApkAssetsCookie cookie = kInvalidCookie; - uint32_t type_set_flags = 0u; + ssize_t block = kXmlBlock; + uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -293,7 +302,7 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, // Walk through the xml attributes looking for the requested attribute. const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); - if (xml_attr_idx != xml_attr_finder.end()) { + if (xml_attr_idx != xml_attr_end) { // We found the attribute we were looking for. xml_parser->getAttributeValue(xml_attr_idx, &value); if (kDebugStyles) { @@ -303,12 +312,12 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the style class values looking for the requested attribute. - const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident); - if (entry != xml_style_attr_finder.end()) { + const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident); + if (style_attr_entry != style_attr_end) { // We found the attribute we were looking for. - cookie = entry->cookie; - type_set_flags = style_flags; - value = entry->value; + block = style_attr_entry->stringBlock; + type_set_flags = style_type_set_flags; + value = style_attr_entry->map.value; if (kDebugStyles) { ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -317,25 +326,25 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the default style values looking for the requested attribute. - const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident); - if (entry != def_style_attr_finder.end()) { + const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident); + if (def_style_attr_entry != def_style_attr_end) { // We found the attribute we were looking for. - cookie = entry->cookie; - type_set_flags = def_style_flags; - value = entry->value; + block = def_style_attr_entry->stringBlock; + type_set_flags = style_type_set_flags; + value = def_style_attr_entry->map.value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } } } - uint32_t resid = 0u; + uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; + ssize_t new_block = + theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); + if (new_block >= 0) { + block = new_block; } if (kDebugStyles) { @@ -343,15 +352,14 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, } } else if (value.data != Res_value::DATA_NULL_EMPTY) { // If we still don't have a value for this attribute, try to find it in the theme! - ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); - if (new_cookie != kInvalidCookie) { + ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); + if (new_block >= 0) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_cookie = - assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; + new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); + if (new_block >= 0) { + block = new_block; } if (kDebugStyles) { @@ -367,7 +375,7 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + block = kXmlBlock; } if (kDebugStyles) { @@ -377,7 +385,9 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + out_values[STYLE_ASSET_COOKIE] = + block != kXmlBlock ? static_cast(res.getTableCookie(block)) + : static_cast(-1); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -392,28 +402,36 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, out_values += STYLE_NUM_ENTRIES; } + res.unlock(); + // out_indices must NOT be nullptr. out_indices[0] = indices_idx; } -bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { +bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, + uint32_t* attrs, size_t attrs_length, + uint32_t* out_values, uint32_t* out_indices) { ResTable_config config; Res_value value; int indices_idx = 0; + // Now lock down the resource object and start pulling stuff from it. + res->lock(); + // Retrieve the XML attributes, if requested. const size_t xml_attr_count = xml_parser->getAttributeCount(); size_t ix = 0; uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix); + static const ssize_t kXmlBlock = 0x10000000; + // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - ApkAssetsCookie cookie = kInvalidCookie; - uint32_t type_set_flags = 0u; + ssize_t block = kXmlBlock; + uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -432,27 +450,28 @@ bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, u cur_xml_attr = xml_parser->getAttributeNameResID(ix); } - uint32_t resid = 0u; + uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } + // printf("Resolving attribute reference\n"); + ssize_t new_block = res->resolveReference(&value, block, &resid, + &type_set_flags, &config); + if (new_block >= 0) block = new_block; } // Deal with the special @null value -- it turns back to TYPE_NULL. if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + block = kXmlBlock; } // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + out_values[STYLE_ASSET_COOKIE] = + block != kXmlBlock ? static_cast(res->getTableCookie(block)) + : static_cast(-1); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -466,6 +485,8 @@ bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, u out_values += STYLE_NUM_ENTRIES; } + res->unlock(); + if (out_indices != nullptr) { out_indices[0] = indices_idx; } diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index e08848f891f6..28548e27baf0 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -324,6 +324,8 @@ bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_i bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, FindEntryResult* out_entry) const { + ATRACE_CALL(); + // If the type IDs are offset in this package, we need to take that into account when searching // for a type. const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h index 03fad4947dfe..f281921824e7 100644 --- a/libs/androidfw/include/androidfw/AttributeFinder.h +++ b/libs/androidfw/include/androidfw/AttributeFinder.h @@ -58,7 +58,6 @@ class BackTrackingAttributeFinder { BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end); Iterator Find(uint32_t attr); - inline Iterator end(); private: void JumpToClosestAttribute(uint32_t package_id); @@ -202,11 +201,6 @@ Iterator BackTrackingAttributeFinder::Find(uint32_t attr) { return end_; } -template -Iterator BackTrackingAttributeFinder::end() { - return end_; -} - } // namespace android #endif // ANDROIDFW_ATTRIBUTE_FINDER_H diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h index 35ef98d8c704..69b760414846 100644 --- a/libs/androidfw/include/androidfw/AttributeResolution.h +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -17,8 +17,7 @@ #ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H #define ANDROIDFW_ATTRIBUTERESOLUTION_H -#include "androidfw/AssetManager2.h" -#include "androidfw/ResourceTypes.h" +#include namespace android { @@ -43,19 +42,19 @@ enum { // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid, +bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, uint32_t* src_values, size_t src_values_length, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` is NOT optional and must NOT be nullptr. -void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, +bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); } // namespace android diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 1775f5070f4e..965e2dbd2fb2 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -45,17 +45,16 @@ struct FindEntryResult { // A pointer to the resource table entry for this resource. // If the size of the entry is > sizeof(ResTable_entry), it can be cast to // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry; + const ResTable_entry* entry = nullptr; - // The configuration for which the resulting entry was defined. This points to a structure that - // is already swapped to host endianness. - const ResTable_config* config; + // The configuration for which the resulting entry was defined. + const ResTable_config* config = nullptr; - // The bitmask of configuration axis with which the resource value varies. - uint32_t type_flags; + // Stores the resulting bitmask of configuration axis with which the resource value varies. + uint32_t type_flags = 0u; // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table; + const DynamicRefTable* dynamic_ref_table = nullptr; // The string pool reference to the type's name. This uses a different string pool than // the global string pool, but this is hidden from the caller. diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h deleted file mode 100644 index 64924f433245..000000000000 --- a/libs/androidfw/include/androidfw/MutexGuard.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROIDFW_MUTEXGUARD_H -#define ANDROIDFW_MUTEXGUARD_H - -#include -#include - -#include "android-base/macros.h" - -namespace android { - -template -class ScopedLock; - -// Owns the guarded object and protects access to it via a mutex. -// The guarded object is inaccessible via this class. -// The mutex is locked and the object accessed via the ScopedLock class. -// -// NOTE: The template parameter T should not be a raw pointer, since ownership -// is ambiguous and error-prone. Instead use an std::unique_ptr<>. -// -// Example use: -// -// Guarded shared_string("hello"); -// { -// ScopedLock locked_string(shared_string); -// *locked_string += " world"; -// } -// -template -class Guarded { - static_assert(!std::is_pointer::value, "T must not be a raw pointer"); - - public: - explicit Guarded() : guarded_() { - } - - template - explicit Guarded(const T& guarded, - typename std::enable_if::value>::type = void()) - : guarded_(guarded) { - } - - template - explicit Guarded(T&& guarded, - typename std::enable_if::value>::type = void()) - : guarded_(std::move(guarded)) { - } - - private: - friend class ScopedLock; - - DISALLOW_COPY_AND_ASSIGN(Guarded); - - std::mutex lock_; - T guarded_; -}; - -template -class ScopedLock { - public: - explicit ScopedLock(Guarded& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) { - } - - T& operator*() { - return guarded_; - } - - T* operator->() { - return &guarded_; - } - - T* get() { - return &guarded_; - } - - private: - DISALLOW_COPY_AND_ASSIGN(ScopedLock); - - std::lock_guard lock_; - T& guarded_; -}; - -} // namespace android - -#endif // ANDROIDFW_MUTEXGUARD_H diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index cc3053798e7b..2d73ce8f8ee3 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -21,7 +21,6 @@ #include "android-base/file.h" #include "android-base/logging.h" #include "android-base/macros.h" -#include "androidfw/AssetManager2.h" #include "TestHelpers.h" #include "data/styles/R.h" @@ -33,14 +32,15 @@ namespace android { class AttributeResolutionTest : public ::testing::Test { public: virtual void SetUp() override { - styles_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); - ASSERT_NE(nullptr, styles_assets_); - assetmanager_.SetApkAssets({styles_assets_.get()}); + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString( + GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); + ASSERT_EQ(NO_ERROR, table_.add(contents.data(), contents.size(), + 1 /*cookie*/, true /*copyData*/)); } protected: - std::unique_ptr styles_assets_; - AssetManager2 assetmanager_; + ResTable table_; }; class AttributeResolutionXmlTest : public AttributeResolutionTest { @@ -48,12 +48,13 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { virtual void SetUp() override { AttributeResolutionTest::SetUp(); - std::unique_ptr asset = - assetmanager_.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, asset); + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", + "res/layout/layout.xml", &contents)); - ASSERT_EQ(NO_ERROR, - xml_parser_.setTo(asset->getBuffer(true), asset->getLength(), true /*copyData*/)); + ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(), + true /*copyData*/)); // Skip to the first tag. while (xml_parser_.next() != ResXMLParser::START_TAG) { @@ -65,14 +66,14 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { }; TEST_F(AttributeResolutionTest, Theme) { - std::unique_ptr theme = assetmanager_.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); + ResTable::Theme theme(table_); + ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_empty}}; std::array values; - ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/, + ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/, nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(), attrs.size(), values.data(), nullptr /*out_indices*/)); @@ -125,8 +126,8 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { R::attr::attr_four, R::attr::attr_empty}}; std::array values; - ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(), - values.data(), nullptr /*out_indices*/)); + ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(), + nullptr /*out_indices*/)); uint32_t* values_cursor = values.data(); EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]); @@ -170,15 +171,15 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { } TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) { - std::unique_ptr theme = assetmanager_.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); + ResTable::Theme theme(table_); + ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}}; std::array values; std::array indices; - ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), + ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(), attrs.size(), values.data(), indices.data()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index a8abcb5df86c..7149beef797f 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -33,12 +33,12 @@ void GetResourceBenchmarkOld(const std::vector& paths, const ResTab } } - // Make sure to force creation of the ResTable first, or else the configuration doesn't get set. - const ResTable& table = assetmanager.getResources(true); if (config != nullptr) { assetmanager.setConfiguration(*config); } + const ResTable& table = assetmanager.getResources(true); + Res_value value; ResTable_config selected_config; uint32_t flags; diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp index e70d5ea0d566..98e9a42d944d 100644 --- a/native/android/asset_manager.cpp +++ b/native/android/asset_manager.cpp @@ -18,11 +18,9 @@ #include #include -#include #include #include #include -#include #include #include "jni.h" @@ -37,20 +35,21 @@ using namespace android; // ----- struct AAssetDir { - std::unique_ptr mAssetDir; + AssetDir* mAssetDir; size_t mCurFileIndex; String8 mCachedFileName; - explicit AAssetDir(std::unique_ptr dir) : - mAssetDir(std::move(dir)), mCurFileIndex(0) { } + explicit AAssetDir(AssetDir* dir) : mAssetDir(dir), mCurFileIndex(0) { } + ~AAssetDir() { delete mAssetDir; } }; // ----- struct AAsset { - std::unique_ptr mAsset; + Asset* mAsset; - explicit AAsset(std::unique_ptr asset) : mAsset(std::move(asset)) { } + explicit AAsset(Asset* asset) : mAsset(asset) { } + ~AAsset() { delete mAsset; } }; // -------------------- Public native C API -------------------- @@ -105,18 +104,19 @@ AAsset* AAssetManager_open(AAssetManager* amgr, const char* filename, int mode) return NULL; } - ScopedLock locked_mgr(*AssetManagerForNdkAssetManager(amgr)); - std::unique_ptr asset = locked_mgr->Open(filename, amMode); - if (asset == nullptr) { - return nullptr; + AssetManager* mgr = static_cast(amgr); + Asset* asset = mgr->open(filename, amMode); + if (asset == NULL) { + return NULL; } - return new AAsset(std::move(asset)); + + return new AAsset(asset); } AAssetDir* AAssetManager_openDir(AAssetManager* amgr, const char* dirName) { - ScopedLock locked_mgr(*AssetManagerForNdkAssetManager(amgr)); - return new AAssetDir(locked_mgr->OpenDir(dirName)); + AssetManager* mgr = static_cast(amgr); + return new AAssetDir(mgr->openDir(dirName)); } /** diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index 52d0e08e4e7f..b32be736533b 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -24,9 +24,8 @@ #include #include -#include #include -#include +#include #include #include @@ -1665,22 +1664,18 @@ nFileA3DCreateFromAssetStream(JNIEnv *_env, jobject _this, jlong con, jlong nati static jlong nFileA3DCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path) { - Guarded* mgr = AssetManagerForJavaObject(_env, _assetMgr); + AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - std::unique_ptr asset; - { - ScopedLock locked_mgr(*mgr); - asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); - if (asset == nullptr) { - return 0; - } + Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; } - jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset.release()); + jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset); return id; } @@ -1757,25 +1752,22 @@ static jlong nFontCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path, jfloat fontSize, jint dpi) { - Guarded* mgr = AssetManagerForJavaObject(_env, _assetMgr); + AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - std::unique_ptr asset; - { - ScopedLock locked_mgr(*mgr); - asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); - if (asset == nullptr) { - return 0; - } + Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; } jlong id = (jlong)(uintptr_t)rsFontCreateFromMemory((RsContext)con, str.c_str(), str.length(), fontSize, dpi, asset->getBuffer(false), asset->getLength()); + delete asset; return id; } -- cgit v1.2.3-59-g8ed1b From 1187590da38457809dd368d4901c9c47ac5a6958 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 23 Jan 2017 12:58:11 -0800 Subject: Replace AssetManager with AssetManager2 implementation Test: atest CtsContentTestCases:android.content.res.cts Test: make libandroidfw_tests Change-Id: I2bb6d7656d2516d371e83e541ed02f91405f6d94 --- config/hiddenapi-light-greylist.txt | 16 - core/java/android/content/pm/PackageParser.java | 19 +- core/java/android/content/res/ApkAssets.java | 221 ++ core/java/android/content/res/AssetManager.java | 1344 +++++---- core/java/android/content/res/Resources.java | 9 +- core/java/android/content/res/ResourcesImpl.java | 22 +- core/java/android/content/res/TypedArray.java | 185 +- core/java/android/content/res/XmlBlock.java | 8 +- core/jni/Android.bp | 3 +- core/jni/AndroidRuntime.cpp | 2 + core/jni/android/graphics/FontFamily.cpp | 29 +- core/jni/android_app_NativeActivity.cpp | 2 +- core/jni/android_content_res_ApkAssets.cpp | 150 + core/jni/android_util_AssetManager.cpp | 2892 +++++++++----------- .../android_runtime/android_util_AssetManager.h | 15 +- libs/androidfw/AssetManager2.cpp | 6 +- libs/androidfw/AttributeResolution.cpp | 263 +- libs/androidfw/LoadedArsc.cpp | 2 - libs/androidfw/include/androidfw/AttributeFinder.h | 6 + .../include/androidfw/AttributeResolution.h | 11 +- libs/androidfw/include/androidfw/LoadedArsc.h | 13 +- libs/androidfw/include/androidfw/MutexGuard.h | 101 + libs/androidfw/tests/AttributeResolution_test.cpp | 39 +- libs/androidfw/tests/BenchmarkHelpers.cpp | 4 +- native/android/asset_manager.cpp | 28 +- rs/jni/android_renderscript_RenderScript.cpp | 30 +- 26 files changed, 2903 insertions(+), 2517 deletions(-) create mode 100644 core/java/android/content/res/ApkAssets.java create mode 100644 core/jni/android_content_res_ApkAssets.cpp create mode 100644 libs/androidfw/include/androidfw/MutexGuard.h (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 58936fcc4452..c173c2c515bc 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -220,28 +220,13 @@ Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;IZ)Landroid/conte Landroid/content/pm/UserInfo;->id:I Landroid/content/pm/UserInfo;->isPrimary()Z Landroid/content/res/AssetManager;->addAssetPath(Ljava/lang/String;)I -Landroid/content/res/AssetManager;->addAssetPaths([Ljava/lang/String;)[I -Landroid/content/res/AssetManager;->applyStyle(JIIJ[IIJJ)V -Landroid/content/res/AssetManager;->getArraySize(I)I Landroid/content/res/AssetManager;->getAssignedPackageIdentifiers()Landroid/util/SparseArray; -Landroid/content/res/AssetManager;->getCookieName(I)Ljava/lang/String; Landroid/content/res/AssetManager;->getResourceBagText(II)Ljava/lang/CharSequence; -Landroid/content/res/AssetManager;->loadResourceBagValue(IILandroid/util/TypedValue;Z)I -Landroid/content/res/AssetManager;->loadResourceValue(ISLandroid/util/TypedValue;Z)I -Landroid/content/res/AssetManager;->loadThemeAttributeValue(JILandroid/util/TypedValue;Z)I Landroid/content/res/AssetManager;->mObject:J -Landroid/content/res/AssetManager;->openNonAssetFdNative(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor; Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;I)Ljava/io/InputStream; Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;)Ljava/io/InputStream; Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;I)Ljava/io/InputStream; Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;)Ljava/io/InputStream; -Landroid/content/res/AssetManager;->openNonAssetNative(ILjava/lang/String;I)J -Landroid/content/res/AssetManager;->openXmlAssetNative(ILjava/lang/String;)J -Landroid/content/res/AssetManager;->resolveAttrs(JII[I[I[I[I)Z -Landroid/content/res/AssetManager;->retrieveArray(I[I)I -Landroid/content/res/AssetManager;->retrieveAttributes(J[I[I[I)Z -Landroid/content/res/AssetManager;->STYLE_NUM_ENTRIES:I -Landroid/content/res/AssetManager;->STYLE_RESOURCE_ID:I Landroid/content/res/DrawableCache;->getInstance(JLandroid/content/res/Resources;Landroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable; Landroid/content/res/Resources;->getCompatibilityInfo()Landroid/content/res/CompatibilityInfo; Landroid/content/res/ResourcesImpl;->mAccessLock:Ljava/lang/Object; @@ -271,7 +256,6 @@ Landroid/content/res/TypedArray;->mResources:Landroid/content/res/Resources; Landroid/content/res/TypedArray;->mTheme:Landroid/content/res/Resources$Theme; Landroid/content/res/TypedArray;->mValue:Landroid/util/TypedValue; Landroid/content/res/TypedArray;->mXml:Landroid/content/res/XmlBlock$Parser; -Landroid/content/res/XmlBlock;->close()V Landroid/content/res/XmlBlock;->newParser()Landroid/content/res/XmlResourceParser; Landroid/content/res/XmlBlock$Parser;->mParseState:J Landroid/content/SyncStatusInfo;->lastSuccessTime:J diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 2da893771d94..258602ffa232 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -54,6 +54,7 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper; import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.SplitAssetDependencyLoader; import android.content.pm.split.SplitAssetLoader; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -1596,21 +1597,19 @@ public class PackageParser { int flags) throws PackageParserException { final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); - AssetManager assets = null; + ApkAssets apkAssets = null; XmlResourceParser parser = null; try { - assets = newConfiguredAssetManager(); - int cookie = fd != null - ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath); - if (cookie == 0) { + try { + apkAssets = fd != null + ? ApkAssets.loadFromFd(fd, debugPathName, false, false) + : ApkAssets.loadFromPath(apkPath); + } catch (IOException e) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse " + apkPath); } - final DisplayMetrics metrics = new DisplayMetrics(); - metrics.setToDefaults(); - - parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); + parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME); final SigningDetails signingDetails; if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) { @@ -1637,7 +1636,7 @@ public class PackageParser { "Failed to parse " + apkPath, e); } finally { IoUtils.closeQuietly(parser); - IoUtils.closeQuietly(assets); + IoUtils.closeQuietly(apkAssets); } } diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java new file mode 100644 index 000000000000..b087c48d8d4c --- /dev/null +++ b/core/java/android/content/res/ApkAssets.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.content.res; + +import android.annotation.NonNull; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; + +import java.io.FileDescriptor; +import java.io.IOException; + +/** + * The loaded, immutable, in-memory representation of an APK. + * + * The main implementation is native C++ and there is very little API surface exposed here. The APK + * is mainly accessed via {@link AssetManager}. + * + * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers, + * making the creation of AssetManagers very cheap. + * @hide + */ +public final class ApkAssets implements AutoCloseable { + @GuardedBy("this") private long mNativePtr; + @GuardedBy("this") private StringBlock mStringBlock; + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException { + return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @param system When true, the APK is loaded as a system APK (framework). + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system) + throws IOException { + return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @param system When true, the APK is loaded as a system APK (framework). + * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are + * loaded as a shared library. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system, + boolean forceSharedLibrary) throws IOException { + return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications. + * + * Performs a dup of the underlying fd, so you must take care of still closing + * the FileDescriptor yourself (and can do that whenever you want). + * + * @param fd The FileDescriptor of an open, readable APK. + * @param friendlyName The friendly name used to identify this ApkAssets when logging. + * @param system When true, the APK is loaded as a system APK (framework). + * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are + * loaded as a shared library. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd, + @NonNull String friendlyName, boolean system, boolean forceSharedLibrary) + throws IOException { + return new ApkAssets(fd, friendlyName, system, forceSharedLibrary); + } + + /** + * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path + * is encoded within the IDMAP. + * + * @param idmapPath Path to the IDMAP of an overlay APK. + * @param system When true, the APK is loaded as a system APK (framework). + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system) + throws IOException { + return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/); + } + + private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) + throws IOException { + Preconditions.checkNotNull(path, "path"); + mNativePtr = nativeLoad(path, system, forceSharedLib, overlay); + mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); + } + + private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system, + boolean forceSharedLib) throws IOException { + Preconditions.checkNotNull(fd, "fd"); + Preconditions.checkNotNull(friendlyName, "friendlyName"); + mNativePtr = nativeLoadFromFd(fd, friendlyName, system, forceSharedLib); + mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); + } + + @NonNull String getAssetPath() { + synchronized (this) { + ensureValidLocked(); + return nativeGetAssetPath(mNativePtr); + } + } + + CharSequence getStringFromPool(int idx) { + synchronized (this) { + ensureValidLocked(); + return mStringBlock.get(idx); + } + } + + /** + * Retrieve a parser for a compiled XML file. This is associated with a single APK and + * NOT a full AssetManager. This means that shared-library references will not be + * dynamically assigned runtime package IDs. + * + * @param fileName The path to the file within the APK. + * @return An XmlResourceParser. + * @throws IOException if the file was not found or an error occurred retrieving it. + */ + public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException { + Preconditions.checkNotNull(fileName, "fileName"); + synchronized (this) { + ensureValidLocked(); + long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName); + try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) { + XmlResourceParser parser = block.newParser(); + // If nativeOpenXml doesn't throw, it will always return a valid native pointer, + // which makes newParser always return non-null. But let's be paranoid. + if (parser == null) { + throw new AssertionError("block.newParser() returned a null parser"); + } + return parser; + } + } + } + + /** + * Returns false if the underlying APK was changed since this ApkAssets was loaded. + */ + public boolean isUpToDate() { + synchronized (this) { + ensureValidLocked(); + return nativeIsUpToDate(mNativePtr); + } + } + + /** + * Closes the ApkAssets and destroys the underlying native implementation. Further use of the + * ApkAssets object will cause exceptions to be thrown. + * + * Calling close on an already closed ApkAssets does nothing. + */ + @Override + public void close() { + synchronized (this) { + if (mNativePtr == 0) { + return; + } + + mStringBlock = null; + nativeDestroy(mNativePtr); + mNativePtr = 0; + } + } + + @Override + protected void finalize() throws Throwable { + if (mNativePtr != 0) { + nativeDestroy(mNativePtr); + } + } + + private void ensureValidLocked() { + if (mNativePtr == 0) { + throw new RuntimeException("ApkAssets is closed"); + } + } + + private static native long nativeLoad( + @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) + throws IOException; + private static native long nativeLoadFromFd(@NonNull FileDescriptor fd, + @NonNull String friendlyName, boolean system, boolean forceSharedLib) + throws IOException; + private static native void nativeDestroy(long ptr); + private static native @NonNull String nativeGetAssetPath(long ptr); + private static native long nativeGetStringBlock(long ptr); + private static native boolean nativeIsUpToDate(long ptr); + private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException; +} diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 78665609bdd4..2db5c6b13200 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -18,9 +18,11 @@ package android.content.res; import android.annotation.AnyRes; import android.annotation.ArrayRes; +import android.annotation.AttrRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; +import android.annotation.StyleRes; import android.content.pm.ActivityInfo; import android.content.res.Configuration.NativeConfig; import android.os.ParcelFileDescriptor; @@ -28,10 +30,20 @@ import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; -import java.io.FileDescriptor; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; + +import libcore.io.IoUtils; + +import java.io.BufferedReader; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.channels.FileLock; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; /** @@ -42,7 +54,17 @@ import java.util.HashMap; * bytes. */ public final class AssetManager implements AutoCloseable { - /* modes used when opening an asset */ + private static final String TAG = "AssetManager"; + private static final boolean DEBUG_REFS = false; + + private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk"; + + private static final Object sSync = new Object(); + + // Not private for LayoutLib's BridgeAssetManager. + @GuardedBy("sSync") static AssetManager sSystem = null; + + @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0]; /** * Mode for {@link #open(String, int)}: no specific information about how @@ -65,87 +87,300 @@ public final class AssetManager implements AutoCloseable { */ public static final int ACCESS_BUFFER = 3; - private static final String TAG = "AssetManager"; - private static final boolean localLOGV = false || false; - - private static final boolean DEBUG_REFS = false; - - private static final Object sSync = new Object(); - /*package*/ static AssetManager sSystem = null; + @GuardedBy("this") private final TypedValue mValue = new TypedValue(); + @GuardedBy("this") private final long[] mOffsets = new long[2]; - private final TypedValue mValue = new TypedValue(); - private final long[] mOffsets = new long[2]; - - // For communication with native code. - private long mObject; + // Pointer to native implementation, stuffed inside a long. + @GuardedBy("this") private long mObject; + + // The loaded asset paths. + @GuardedBy("this") private ApkAssets[] mApkAssets; + + // Debug/reference counting implementation. + @GuardedBy("this") private boolean mOpen = true; + @GuardedBy("this") private int mNumRefs = 1; + @GuardedBy("this") private HashMap mRefStacks; - private StringBlock mStringBlocks[] = null; - - private int mNumRefs = 1; - private boolean mOpen = true; - private HashMap mRefStacks; - /** * Create a new AssetManager containing only the basic system assets. * Applications will not generally use this method, instead retrieving the * appropriate asset manager with {@link Resources#getAssets}. Not for * use by applications. - * {@hide} + * @hide */ public AssetManager() { - synchronized (this) { - if (DEBUG_REFS) { - mNumRefs = 0; - incRefsLocked(this.hashCode()); - } - init(false); - if (localLOGV) Log.v(TAG, "New asset manager: " + this); - ensureSystemAssets(); + final ApkAssets[] assets; + synchronized (sSync) { + createSystemAssetsInZygoteLocked(); + assets = sSystemApkAssets; } - } - private static void ensureSystemAssets() { - synchronized (sSync) { - if (sSystem == null) { - AssetManager system = new AssetManager(true); - system.makeStringBlocks(null); - sSystem = system; - } + mObject = nativeCreate(); + if (DEBUG_REFS) { + mNumRefs = 0; + incRefsLocked(hashCode()); } + + // Always set the framework resources. + setApkAssets(assets, false /*invalidateCaches*/); } - - private AssetManager(boolean isSystem) { + + /** + * Private constructor that doesn't call ensureSystemAssets. + * Used for the creation of system assets. + */ + @SuppressWarnings("unused") + private AssetManager(boolean sentinel) { + mObject = nativeCreate(); if (DEBUG_REFS) { - synchronized (this) { - mNumRefs = 0; - incRefsLocked(this.hashCode()); + mNumRefs = 0; + incRefsLocked(hashCode()); + } + } + + /** + * This must be called from Zygote so that system assets are shared by all applications. + */ + private static void createSystemAssetsInZygoteLocked() { + if (sSystem != null) { + return; + } + + // Make sure that all IDMAPs are up to date. + nativeVerifySystemIdmaps(); + + try { + ArrayList apkAssets = new ArrayList<>(); + apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/)); + loadStaticRuntimeOverlays(apkAssets); + + sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]); + sSystem = new AssetManager(true /*sentinel*/); + sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/); + } catch (IOException e) { + throw new IllegalStateException("Failed to create system AssetManager", e); + } + } + + /** + * Loads the static runtime overlays declared in /data/resource-cache/overlays.list. + * Throws an exception if the file is corrupt or if loading the APKs referenced by the file + * fails. Returns quietly if the overlays.list file doesn't exist. + * @param outApkAssets The list to fill with the loaded ApkAssets. + */ + private static void loadStaticRuntimeOverlays(ArrayList outApkAssets) + throws IOException { + final FileInputStream fis; + try { + fis = new FileInputStream("/data/resource-cache/overlays.list"); + } catch (FileNotFoundException e) { + // We might not have any overlays, this is fine. We catch here since ApkAssets + // loading can also fail with the same exception, which we would want to propagate. + Log.i(TAG, "no overlays.list file found"); + return; + } + + try { + // Acquire a lock so that any idmap scanning doesn't impact the current set. + // The order of this try-with-resources block matters. We must release the lock, and + // then close the file streams when exiting the block. + try (final BufferedReader br = new BufferedReader(new InputStreamReader(fis)); + final FileLock flock = fis.getChannel().lock(0, Long.MAX_VALUE, true /*shared*/)) { + for (String line; (line = br.readLine()) != null; ) { + final String idmapPath = line.split(" ")[1]; + outApkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/)); + } } + } finally { + // When BufferedReader is closed above, FileInputStream is closed as well. But let's be + // paranoid. + IoUtils.closeQuietly(fis); } - init(true); - if (localLOGV) Log.v(TAG, "New asset manager: " + this); } /** * Return a global shared asset manager that provides access to only * system assets (no application assets). - * {@hide} + * @hide */ public static AssetManager getSystem() { - ensureSystemAssets(); - return sSystem; + synchronized (sSync) { + createSystemAssetsInZygoteLocked(); + return sSystem; + } } /** * Close this asset manager. */ + @Override public void close() { - synchronized(this) { - //System.out.println("Release: num=" + mNumRefs - // + ", released=" + mReleased); - if (mOpen) { - mOpen = false; - decRefsLocked(this.hashCode()); + synchronized (this) { + if (!mOpen) { + return; + } + + mOpen = false; + decRefsLocked(hashCode()); + } + } + + /** + * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)} + * family of methods. + * + * @param apkAssets The new set of paths. + * @param invalidateCaches Whether to invalidate any caches. This should almost always be true. + * Set this to false if you are appending new resources + * (not new configurations). + * @hide + */ + public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) { + Preconditions.checkNotNull(apkAssets, "apkAssets"); + synchronized (this) { + ensureValidLocked(); + mApkAssets = apkAssets; + nativeSetApkAssets(mObject, apkAssets, invalidateCaches); + if (invalidateCaches) { + // Invalidate all caches. + invalidateCachesLocked(-1); + } + } + } + + /** + * Invalidates the caches in this AssetManager according to the bitmask `diff`. + * + * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}. + * @see ActivityInfo.Config + */ + private void invalidateCachesLocked(int diff) { + // TODO(adamlesinski): Currently there are no caches to invalidate in Java code. + } + + /** + * @hide + */ + public @NonNull ApkAssets[] getApkAssets() { + synchronized (this) { + ensureValidLocked(); + return mApkAssets; + } + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addAssetPath(String path) { + return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/); + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addAssetPathAsSharedLibrary(String path) { + return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/); + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addOverlayPath(String path) { + return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/); + } + + private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) { + Preconditions.checkNotNull(path, "path"); + synchronized (this) { + ensureOpenLocked(); + final int count = mApkAssets.length; + for (int i = 0; i < count; i++) { + if (mApkAssets[i].getAssetPath().equals(path)) { + return i + 1; + } + } + + final ApkAssets assets; + try { + if (overlay) { + // TODO(b/70343104): This hardcoded path will be removed once + // addAssetPathInternal is deleted. + final String idmapPath = "/data/resource-cache/" + + path.substring(1).replace('/', '@') + + "@idmap"; + assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/); + } else { + assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib); + } + } catch (IOException e) { + return 0; + } + + final ApkAssets[] newApkAssets = Arrays.copyOf(mApkAssets, count + 1); + newApkAssets[count] = assets; + setApkAssets(newApkAssets, true); + return count + 1; + } + } + + /** + * Ensures that the native implementation has not been destroyed. + * The AssetManager may have been closed, but references to it still exist + * and therefore the native implementation is not destroyed. + */ + private void ensureValidLocked() { + if (mObject == 0) { + throw new RuntimeException("AssetManager has been destroyed"); + } + } + + /** + * Ensures that the AssetManager has not been explicitly closed. If this method passes, + * then this implies that ensureValidLocked() also passes. + */ + private void ensureOpenLocked() { + if (!mOpen) { + throw new RuntimeException("AssetManager has been closed"); + } + } + + /** + * Populates {@code outValue} with the data associated a particular + * resource identifier for the current configuration. + * + * @param resId the resource identifier to load + * @param densityDpi the density bucket for which to load the resource + * @param outValue the typed value in which to put the data + * @param resolveRefs {@code true} to resolve references, {@code false} + * to leave them unresolved + * @return {@code true} if the data was loaded into {@code outValue}, + * {@code false} otherwise + */ + boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, + boolean resolveRefs) { + Preconditions.checkNotNull(outValue, "outValue"); + synchronized (this) { + ensureValidLocked(); + final int cookie = nativeGetResourceValue( + mObject, resId, (short) densityDpi, outValue, resolveRefs); + if (cookie <= 0) { + return false; } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); + } + return true; } } @@ -156,8 +391,7 @@ public final class AssetManager implements AutoCloseable { * @param resId the resource identifier to load * @return the string value, or {@code null} */ - @Nullable - final CharSequence getResourceText(@StringRes int resId) { + @Nullable CharSequence getResourceText(@StringRes int resId) { synchronized (this) { final TypedValue outValue = mValue; if (getResourceValue(resId, 0, outValue, true)) { @@ -172,15 +406,15 @@ public final class AssetManager implements AutoCloseable { * identifier for the current configuration. * * @param resId the resource identifier to load - * @param bagEntryId + * @param bagEntryId the index into the bag to load * @return the string value, or {@code null} */ - @Nullable - final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { + @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { synchronized (this) { + ensureValidLocked(); final TypedValue outValue = mValue; - final int block = loadResourceBagValue(resId, bagEntryId, outValue, true); - if (block < 0) { + final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue); + if (cookie <= 0) { return null; } @@ -189,52 +423,60 @@ public final class AssetManager implements AutoCloseable { outValue.changingConfigurations); if (outValue.type == TypedValue.TYPE_STRING) { - return mStringBlocks[block].get(outValue.data); + return mApkAssets[cookie - 1].getStringFromPool(outValue.data); } return outValue.coerceToString(); } } + int getResourceArraySize(@ArrayRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceArraySize(mObject, resId); + } + } + /** - * Retrieves the string array associated with a particular resource - * identifier for the current configuration. + * Populates `outData` with array elements of `resId`. `outData` is normally + * used with + * {@link TypedArray}. * - * @param resId the resource identifier of the string array - * @return the string array, or {@code null} + * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES} + * long, + * with the indices of the data representing the type, value, asset cookie, + * resource ID, + * configuration change mask, and density of the element. + * + * @param resId The resource ID of an array resource. + * @param outData The array to populate with data. + * @return The length of the array. + * + * @see TypedArray#STYLE_TYPE + * @see TypedArray#STYLE_DATA + * @see TypedArray#STYLE_ASSET_COOKIE + * @see TypedArray#STYLE_RESOURCE_ID + * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS + * @see TypedArray#STYLE_DENSITY */ - @Nullable - final String[] getResourceStringArray(@ArrayRes int resId) { - return getArrayStringResource(resId); + int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) { + Preconditions.checkNotNull(outData, "outData"); + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceArray(mObject, resId, outData); + } } /** - * Populates {@code outValue} with the data associated a particular - * resource identifier for the current configuration. + * Retrieves the string array associated with a particular resource + * identifier for the current configuration. * - * @param resId the resource identifier to load - * @param densityDpi the density bucket for which to load the resource - * @param outValue the typed value in which to put the data - * @param resolveRefs {@code true} to resolve references, {@code false} - * to leave them unresolved - * @return {@code true} if the data was loaded into {@code outValue}, - * {@code false} otherwise + * @param resId the resource identifier of the string array + * @return the string array, or {@code null} */ - final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, - boolean resolveRefs) { + @Nullable String[] getResourceStringArray(@ArrayRes int resId) { synchronized (this) { - final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs); - if (block < 0) { - return false; - } - - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); - - if (outValue.type == TypedValue.TYPE_STRING) { - outValue.string = mStringBlocks[block].get(outValue.data); - } - return true; + ensureValidLocked(); + return nativeGetResourceStringArray(mObject, resId); } } @@ -244,26 +486,48 @@ public final class AssetManager implements AutoCloseable { * * @param resId the resource id of the string array */ - final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { + @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { synchronized (this) { - final int[] rawInfoArray = getArrayStringInfo(resId); + ensureValidLocked(); + final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId); if (rawInfoArray == null) { return null; } + final int rawInfoArrayLen = rawInfoArray.length; final int infoArrayLen = rawInfoArrayLen / 2; - int block; - int index; final CharSequence[] retArray = new CharSequence[infoArrayLen]; for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) { - block = rawInfoArray[i]; - index = rawInfoArray[i + 1]; - retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null; + int cookie = rawInfoArray[i]; + int index = rawInfoArray[i + 1]; + retArray[j] = (index >= 0 && cookie > 0) + ? mApkAssets[cookie - 1].getStringFromPool(index) : null; } return retArray; } } + @Nullable int[] getResourceIntArray(@ArrayRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceIntArray(mObject, resId); + } + } + + /** + * Get the attributes for a style resource. These are the <item> + * elements in + * a <style> resource. + * @param resId The resource ID of the style + * @return An array of attribute IDs. + */ + @AttrRes int[] getStyleAttributes(@StyleRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetStyleAttributes(mObject, resId); + } + } + /** * Populates {@code outValue} with the data associated with a particular * resource identifier for the current configuration. Resolves theme @@ -277,73 +541,88 @@ public final class AssetManager implements AutoCloseable { * @return {@code true} if the data was loaded into {@code outValue}, * {@code false} otherwise */ - final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, + boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, boolean resolveRefs) { - final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs); - if (block < 0) { - return false; + Preconditions.checkNotNull(outValue, "outValue"); + synchronized (this) { + ensureValidLocked(); + final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue, + resolveRefs); + if (cookie <= 0) { + return false; + } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); + } + return true; } + } - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); + void dumpTheme(long theme, int priority, String tag, String prefix) { + synchronized (this) { + ensureValidLocked(); + nativeThemeDump(mObject, theme, priority, tag, prefix); + } + } - if (outValue.type == TypedValue.TYPE_STRING) { - final StringBlock[] blocks = ensureStringBlocks(); - outValue.string = blocks[block].get(outValue.data); + @Nullable String getResourceName(@AnyRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceName(mObject, resId); } - return true; } - /** - * Ensures the string blocks are loaded. - * - * @return the string blocks - */ - @NonNull - final StringBlock[] ensureStringBlocks() { + @Nullable String getResourcePackageName(@AnyRes int resId) { synchronized (this) { - if (mStringBlocks == null) { - makeStringBlocks(sSystem.mStringBlocks); - } - return mStringBlocks; + ensureValidLocked(); + return nativeGetResourcePackageName(mObject, resId); } } - /*package*/ final void makeStringBlocks(StringBlock[] seed) { - final int seedNum = (seed != null) ? seed.length : 0; - final int num = getStringBlockCount(); - mStringBlocks = new StringBlock[num]; - if (localLOGV) Log.v(TAG, "Making string blocks for " + this - + ": " + num); - for (int i=0; i Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len; - } - public final void close() throws IOException { - synchronized (AssetManager.this) { - if (mAsset != 0) { - destroyAsset(mAsset); - mAsset = 0; - decRefsLocked(hashCode()); - } - } - } - public final void mark(int readlimit) { - mMarkPos = seekAsset(mAsset, 0, 0); + ensureOpen(); + return nativeAssetReadChar(mAssetNativePtr); } - public final void reset() throws IOException { - seekAsset(mAsset, mMarkPos, -1); - } - public final int read(byte[] b) throws IOException { - return readAsset(mAsset, b, 0, b.length); + + @Override + public final int read(@NonNull byte[] b) throws IOException { + ensureOpen(); + Preconditions.checkNotNull(b, "b"); + return nativeAssetRead(mAssetNativePtr, b, 0, b.length); } - public final int read(byte[] b, int off, int len) throws IOException { - return readAsset(mAsset, b, off, len); + + @Override + public final int read(@NonNull byte[] b, int off, int len) throws IOException { + ensureOpen(); + Preconditions.checkNotNull(b, "b"); + return nativeAssetRead(mAssetNativePtr, b, off, len); } + + @Override public final long skip(long n) throws IOException { - long pos = seekAsset(mAsset, 0, 0); - if ((pos+n) > mLength) { - n = mLength-pos; + ensureOpen(); + long pos = nativeAssetSeek(mAssetNativePtr, 0, 0); + if ((pos + n) > mLength) { + n = mLength - pos; } if (n > 0) { - seekAsset(mAsset, n, 0); + nativeAssetSeek(mAssetNativePtr, n, 0); } return n; } - protected void finalize() throws Throwable - { - close(); + @Override + public final int available() throws IOException { + ensureOpen(); + final long len = nativeAssetGetRemainingLength(mAssetNativePtr); + return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len; } - private long mAsset; - private long mLength; - private long mMarkPos; - } - - /** - * Add an additional set of assets to the asset manager. This can be - * either a directory or ZIP file. Not for use by applications. Returns - * the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public final int addAssetPath(String path) { - return addAssetPathInternal(path, false); - } - - /** - * Add an application assets to the asset manager and loading it as shared library. - * This can be either a directory or ZIP file. Not for use by applications. Returns - * the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public final int addAssetPathAsSharedLibrary(String path) { - return addAssetPathInternal(path, true); - } - - private final int addAssetPathInternal(String path, boolean appAsLib) { - synchronized (this) { - int res = addAssetPathNative(path, appAsLib); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final boolean markSupported() { + return true; } - } - - private native final int addAssetPathNative(String path, boolean appAsLib); - /** - * Add an additional set of assets to the asset manager from an already open - * FileDescriptor. Not for use by applications. - * This does not give full AssetManager functionality for these assets, - * since the origin of the file is not known for purposes of sharing, - * overlay resolution, and other features. However it does allow you - * to do simple access to the contents of the given fd as an apk file. - * Performs a dup of the underlying fd, so you must take care of still closing - * the FileDescriptor yourself (and can do that whenever you want). - * Returns the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public int addAssetFd(FileDescriptor fd, String debugPathName) { - return addAssetFdInternal(fd, debugPathName, false); - } - - private int addAssetFdInternal(FileDescriptor fd, String debugPathName, - boolean appAsLib) { - synchronized (this) { - int res = addAssetFdNative(fd, debugPathName, appAsLib); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final void mark(int readlimit) { + ensureOpen(); + mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0); } - } - - private native int addAssetFdNative(FileDescriptor fd, String debugPathName, - boolean appAsLib); - /** - * Add a set of assets to overlay an already added set of assets. - * - * This is only intended for application resources. System wide resources - * are handled before any Java code is executed. - * - * {@hide} - */ - - public final int addOverlayPath(String idmapPath) { - synchronized (this) { - int res = addOverlayPathNative(idmapPath); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final void reset() throws IOException { + ensureOpen(); + nativeAssetSeek(mAssetNativePtr, mMarkPos, -1); } - } - /** - * See addOverlayPath. - * - * {@hide} - */ - public native final int addOverlayPathNative(String idmapPath); + @Override + public final void close() throws IOException { + if (mAssetNativePtr != 0) { + nativeAssetDestroy(mAssetNativePtr); + mAssetNativePtr = 0; - /** - * Add multiple sets of assets to the asset manager at once. See - * {@link #addAssetPath(String)} for more information. Returns array of - * cookies for each added asset with 0 indicating failure, or null if - * the input array of paths is null. - * {@hide} - */ - public final int[] addAssetPaths(String[] paths) { - if (paths == null) { - return null; + synchronized (AssetManager.this) { + decRefsLocked(hashCode()); + } + } } - int[] cookies = new int[paths.length]; - for (int i = 0; i < paths.length; i++) { - cookies[i] = addAssetPath(paths[i]); + @Override + protected void finalize() throws Throwable { + close(); } - return cookies; + private void ensureOpen() { + if (mAssetNativePtr == 0) { + throw new IllegalStateException("AssetInputStream is closed"); + } + } } /** * Determine whether the state in this asset manager is up-to-date with * the files on the filesystem. If false is returned, you need to * instantiate a new AssetManager class to see the new data. - * {@hide} + * @hide */ - public native final boolean isUpToDate(); + public boolean isUpToDate() { + for (ApkAssets apkAssets : getApkAssets()) { + if (!apkAssets.isUpToDate()) { + return false; + } + } + return true; + } /** * Get the locales that this asset manager contains data for. @@ -784,7 +1094,12 @@ public final class AssetManager implements AutoCloseable { * are of the form {@code ll_CC} where {@code ll} is a two letter language code, * and {@code CC} is a two letter country code. */ - public native final String[] getLocales(); + public String[] getLocales() { + synchronized (this) { + ensureValidLocked(); + return nativeGetLocales(mObject, false /*excludeSystem*/); + } + } /** * Same as getLocales(), except that locales that are only provided by the system (i.e. those @@ -794,131 +1109,57 @@ public final class AssetManager implements AutoCloseable { * assets support Cherokee and French, getLocales() would return * [Cherokee, English, French, German], while getNonSystemLocales() would return * [Cherokee, French]. - * {@hide} + * @hide */ - public native final String[] getNonSystemLocales(); - - /** {@hide} */ - public native final Configuration[] getSizeConfigurations(); + public String[] getNonSystemLocales() { + synchronized (this) { + ensureValidLocked(); + return nativeGetLocales(mObject, true /*excludeSystem*/); + } + } /** - * Change the configuation used when retrieving resources. Not for use by - * applications. - * {@hide} + * @hide */ - public native final void setConfiguration(int mcc, int mnc, String locale, - int orientation, int touchscreen, int density, int keyboard, - int keyboardHidden, int navigation, int screenWidth, int screenHeight, - int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, - int screenLayout, int uiMode, int colorMode, int majorVersion); + Configuration[] getSizeConfigurations() { + synchronized (this) { + ensureValidLocked(); + return nativeGetSizeConfigurations(mObject); + } + } /** - * Retrieve the resource identifier for the given resource name. + * Change the configuration used when retrieving resources. Not for use by + * applications. + * @hide */ - /*package*/ native final int getResourceIdentifier(String name, - String defType, - String defPackage); + public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation, + int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, + int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, + int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) { + synchronized (this) { + ensureValidLocked(); + nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density, + keyboard, keyboardHidden, navigation, screenWidth, screenHeight, + smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode, + colorMode, majorVersion); + } + } - /*package*/ native final String getResourceName(int resid); - /*package*/ native final String getResourcePackageName(int resid); - /*package*/ native final String getResourceTypeName(int resid); - /*package*/ native final String getResourceEntryName(int resid); - - private native final long openAsset(String fileName, int accessMode); - private final native ParcelFileDescriptor openAssetFd(String fileName, - long[] outOffsets) throws IOException; - private native final long openNonAssetNative(int cookie, String fileName, - int accessMode); - private native ParcelFileDescriptor openNonAssetFdNative(int cookie, - String fileName, long[] outOffsets) throws IOException; - private native final void destroyAsset(long asset); - private native final int readAssetChar(long asset); - private native final int readAsset(long asset, byte[] b, int off, int len); - private native final long seekAsset(long asset, long offset, int whence); - private native final long getAssetLength(long asset); - private native final long getAssetRemainingLength(long asset); - - /** Returns true if the resource was found, filling in mRetStringBlock and - * mRetData. */ - private native final int loadResourceValue(int ident, short density, TypedValue outValue, - boolean resolve); - /** Returns true if the resource was found, filling in mRetStringBlock and - * mRetData. */ - private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue, - boolean resolve); - /*package*/ static final int STYLE_NUM_ENTRIES = 6; - /*package*/ static final int STYLE_TYPE = 0; - /*package*/ static final int STYLE_DATA = 1; - /*package*/ static final int STYLE_ASSET_COOKIE = 2; - /*package*/ static final int STYLE_RESOURCE_ID = 3; - - /* Offset within typed data array for native changingConfigurations. */ - static final int STYLE_CHANGING_CONFIGURATIONS = 4; - - /*package*/ static final int STYLE_DENSITY = 5; - /*package*/ native static final void applyStyle(long theme, - int defStyleAttr, int defStyleRes, long xmlParser, - int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress); - /*package*/ native static final boolean resolveAttrs(long theme, - int defStyleAttr, int defStyleRes, int[] inValues, - int[] inAttrs, int[] outValues, int[] outIndices); - /*package*/ native final boolean retrieveAttributes( - long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); - /*package*/ native final int getArraySize(int resource); - /*package*/ native final int retrieveArray(int resource, int[] outValues); - private native final int getStringBlockCount(); - private native final long getNativeStringBlock(int block); - - /** - * {@hide} - */ - public native final String getCookieName(int cookie); - - /** - * {@hide} - */ - public native final SparseArray getAssignedPackageIdentifiers(); - - /** - * {@hide} - */ - public native static final int getGlobalAssetCount(); - - /** - * {@hide} - */ - public native static final String getAssetAllocations(); - /** - * {@hide} + * @hide */ - public native static final int getGlobalAssetManagerCount(); - - private native final long newTheme(); - private native final void deleteTheme(long theme); - /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force); - /*package*/ native static final void copyTheme(long dest, long source); - /*package*/ native static final void clearTheme(long theme); - /*package*/ native static final int loadThemeAttributeValue(long theme, int ident, - TypedValue outValue, - boolean resolve); - /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix); - /*package*/ native static final @NativeConfig int getThemeChangingConfigurations(long theme); - - private native final long openXmlAssetNative(int cookie, String fileName); - - private native final String[] getArrayStringResource(int arrayRes); - private native final int[] getArrayStringInfo(int arrayRes); - /*package*/ native final int[] getArrayIntResource(int arrayRes); - /*package*/ native final int[] getStyleAttributes(int themeRes); - - private native final void init(boolean isSystem); - private native final void destroy(); - - private final void incRefsLocked(long id) { + public SparseArray getAssignedPackageIdentifiers() { + synchronized (this) { + ensureValidLocked(); + return nativeGetAssignedPackageIdentifiers(mObject); + } + } + + private void incRefsLocked(long id) { if (DEBUG_REFS) { if (mRefStacks == null) { - mRefStacks = new HashMap(); + mRefStacks = new HashMap<>(); } RuntimeException ex = new RuntimeException(); ex.fillInStackTrace(); @@ -926,16 +1167,117 @@ public final class AssetManager implements AutoCloseable { } mNumRefs++; } - - private final void decRefsLocked(long id) { + + private void decRefsLocked(long id) { if (DEBUG_REFS && mRefStacks != null) { mRefStacks.remove(id); } mNumRefs--; - //System.out.println("Dec streams: mNumRefs=" + mNumRefs - // + " mReleased=" + mReleased); - if (mNumRefs == 0) { - destroy(); + if (mNumRefs == 0 && mObject != 0) { + nativeDestroy(mObject); + mObject = 0; } } + + // AssetManager setup native methods. + private static native long nativeCreate(); + private static native void nativeDestroy(long ptr); + private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, + boolean invalidateCaches); + private static native void nativeSetConfiguration(long ptr, int mcc, int mnc, + @Nullable String locale, int orientation, int touchscreen, int density, int keyboard, + int keyboardHidden, int navigation, int screenWidth, int screenHeight, + int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, + int uiMode, int colorMode, int majorVersion); + private static native @NonNull SparseArray nativeGetAssignedPackageIdentifiers( + long ptr); + + // File native methods. + private static native @Nullable String[] nativeList(long ptr, @NonNull String path) + throws IOException; + private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode); + private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr, + @NonNull String fileName, long[] outOffsets) throws IOException; + private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName, + int accessMode); + private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie, + @NonNull String fileName, @NonNull long[] outOffsets) throws IOException; + private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName); + + // Primitive resource native methods. + private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density, + @NonNull TypedValue outValue, boolean resolveReferences); + private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId, + @NonNull TypedValue outValue); + + private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr, + @StyleRes int resId); + private static native @Nullable String[] nativeGetResourceStringArray(long ptr, + @ArrayRes int resId); + private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr, + @ArrayRes int resId); + private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId); + private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId); + private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId, + @NonNull int[] outValues); + + // Resource name/ID native methods. + private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name, + @Nullable String defType, @Nullable String defPackage); + private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid); + private static native @Nullable String nativeGetResourcePackageName(long ptr, + @AnyRes int resid); + private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid); + private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid); + private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem); + private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr); + + // Style attribute retrieval native methods. + private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, + @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs, + long outValuesAddress, long outIndicesAddress); + private static native boolean nativeResolveAttrs(long ptr, long themePtr, + @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, + @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); + private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr, + @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); + + // Theme related native methods + private static native long nativeThemeCreate(long ptr); + private static native void nativeThemeDestroy(long themePtr); + private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, + boolean force); + static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr); + static native void nativeThemeClear(long themePtr); + private static native int nativeThemeGetAttributeValue(long ptr, long themePtr, + @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve); + private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag, + String prefix); + static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr); + + // AssetInputStream related native methods. + private static native void nativeAssetDestroy(long assetPtr); + private static native int nativeAssetReadChar(long assetPtr); + private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len); + private static native long nativeAssetSeek(long assetPtr, long offset, int whence); + private static native long nativeAssetGetLength(long assetPtr); + private static native long nativeAssetGetRemainingLength(long assetPtr); + + private static native void nativeVerifySystemIdmaps(); + + // Global debug native methods. + /** + * @hide + */ + public static native int getGlobalAssetCount(); + + /** + * @hide + */ + public static native String getAssetAllocations(); + + /** + * @hide + */ + public static native int getGlobalAssetManagerCount(); } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index e173653cd961..8f58891ed556 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -590,7 +590,7 @@ public class Resources { */ @NonNull public int[] getIntArray(@ArrayRes int id) throws NotFoundException { - int[] res = mResourcesImpl.getAssets().getArrayIntResource(id); + int[] res = mResourcesImpl.getAssets().getResourceIntArray(id); if (res != null) { return res; } @@ -613,13 +613,13 @@ public class Resources { @NonNull public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException { final ResourcesImpl impl = mResourcesImpl; - int len = impl.getAssets().getArraySize(id); + int len = impl.getAssets().getResourceArraySize(id); if (len < 0) { throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id)); } TypedArray array = TypedArray.obtain(this, len); - array.mLength = impl.getAssets().retrieveArray(id, array.mData); + array.mLength = impl.getAssets().getResourceArray(id, array.mData); array.mIndices[0] = 0; return array; @@ -1789,8 +1789,7 @@ public class Resources { // out the attributes from the XML file (applying type information // contained in the resources and such). XmlBlock.Parser parser = (XmlBlock.Parser)set; - mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, attrs, - array.mData, array.mIndices); + mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices); array.mXml = parser; diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 97cb78bc4243..2a4b278bdca8 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -168,7 +168,6 @@ public class ResourcesImpl { mDisplayAdjustments = displayAdjustments; mConfiguration.setToDefaults(); updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo()); - mAssets.ensureStringBlocks(); } public DisplayAdjustments getDisplayAdjustments() { @@ -1274,8 +1273,7 @@ public class ResourcesImpl { void applyStyle(int resId, boolean force) { synchronized (mKey) { - AssetManager.applyThemeStyle(mTheme, resId, force); - + mAssets.applyStyleToTheme(mTheme, resId, force); mThemeResId = resId; mKey.append(resId, force); } @@ -1284,7 +1282,7 @@ public class ResourcesImpl { void setTo(ThemeImpl other) { synchronized (mKey) { synchronized (other.mKey) { - AssetManager.copyTheme(mTheme, other.mTheme); + AssetManager.nativeThemeCopy(mTheme, other.mTheme); mThemeResId = other.mThemeResId; mKey.setTo(other.getKey()); @@ -1307,12 +1305,10 @@ public class ResourcesImpl { // out the attributes from the XML file (applying type information // contained in the resources and such). final XmlBlock.Parser parser = (XmlBlock.Parser) set; - AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes, - parser != null ? parser.mParseState : 0, - attrs, attrs.length, array.mDataAddress, array.mIndicesAddress); + mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs, + array.mDataAddress, array.mIndicesAddress); array.mTheme = wrapper; array.mXml = parser; - return array; } } @@ -1329,7 +1325,7 @@ public class ResourcesImpl { } final TypedArray array = TypedArray.obtain(wrapper.getResources(), len); - AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); + mAssets.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); array.mTheme = wrapper; array.mXml = null; return array; @@ -1349,14 +1345,14 @@ public class ResourcesImpl { @Config int getChangingConfigurations() { synchronized (mKey) { final @NativeConfig int nativeChangingConfig = - AssetManager.getThemeChangingConfigurations(mTheme); + AssetManager.nativeThemeGetChangingConfigurations(mTheme); return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig); } } public void dump(int priority, String tag, String prefix) { synchronized (mKey) { - AssetManager.dumpTheme(mTheme, priority, tag, prefix); + mAssets.dumpTheme(mTheme, priority, tag, prefix); } } @@ -1385,13 +1381,13 @@ public class ResourcesImpl { */ void rebase() { synchronized (mKey) { - AssetManager.clearTheme(mTheme); + AssetManager.nativeThemeClear(mTheme); // Reapply the same styles in the same order. for (int i = 0; i < mKey.mCount; i++) { final int resId = mKey.mResId[i]; final boolean force = mKey.mForce[i]; - AssetManager.applyThemeStyle(mTheme, resId, force); + mAssets.applyStyleToTheme(mTheme, resId, force); } } } diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index f33c75168a5f..cbb3c6df0558 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -61,6 +61,15 @@ public class TypedArray { return attrs; } + // STYLE_ prefixed constants are offsets within the typed data array. + static final int STYLE_NUM_ENTRIES = 6; + static final int STYLE_TYPE = 0; + static final int STYLE_DATA = 1; + static final int STYLE_ASSET_COOKIE = 2; + static final int STYLE_RESOURCE_ID = 3; + static final int STYLE_CHANGING_CONFIGURATIONS = 4; + static final int STYLE_DENSITY = 5; + private final Resources mResources; private DisplayMetrics mMetrics; private AssetManager mAssets; @@ -78,7 +87,7 @@ public class TypedArray { private void resize(int len) { mLength = len; - final int dataLen = len * AssetManager.STYLE_NUM_ENTRIES; + final int dataLen = len * STYLE_NUM_ENTRIES; final int indicesLen = len + 1; final VMRuntime runtime = VMRuntime.getRuntime(); if (mDataAddress == 0 || mData.length < dataLen) { @@ -166,9 +175,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -203,9 +212,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -242,14 +251,13 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_STRING) { - final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; + final int cookie = data[index + STYLE_ASSET_COOKIE]; if (cookie < 0) { - return mXml.getPooledString( - data[index+AssetManager.STYLE_DATA]).toString(); + return mXml.getPooledString(data[index + STYLE_DATA]).toString(); } } return null; @@ -274,11 +282,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); + data[index + STYLE_CHANGING_CONFIGURATIONS]); if ((changingConfigs & ~allowedChangingConfigs) != 0) { return null; } @@ -320,14 +328,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA] != 0; + return data[index + STYLE_DATA] != 0; } final TypedValue v = mValue; @@ -359,14 +367,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } final TypedValue v = mValue; @@ -396,16 +404,16 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FLOAT) { - return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]); + return Float.intBitsToFloat(data[index + STYLE_DATA]); } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } final TypedValue v = mValue; @@ -446,15 +454,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_STRING) { final TypedValue value = mValue; if (getValueAt(index, value)) { @@ -498,7 +506,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -533,7 +541,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -564,15 +572,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -612,15 +620,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimension( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -661,15 +668,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelOffset( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -711,15 +717,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index+AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -755,16 +760,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index+AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -795,15 +799,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } return defValue; @@ -833,15 +836,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FRACTION) { - return TypedValue.complexToFraction( - data[index+AssetManager.STYLE_DATA], base, pbase); + return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -874,10 +876,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) { - final int resid = data[index+AssetManager.STYLE_RESOURCE_ID]; + if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) { + final int resid = data[index + STYLE_RESOURCE_ID]; if (resid != 0) { return resid; } @@ -902,10 +904,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { - return data[index + AssetManager.STYLE_DATA]; + if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { + return data[index + STYLE_DATA]; } return defValue; } @@ -939,7 +941,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -975,7 +977,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -1006,7 +1008,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { return mResources.getTextArray(value.resourceId); } return null; @@ -1027,7 +1029,7 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue); + return getValueAt(index * STYLE_NUM_ENTRIES, outValue); } /** @@ -1043,8 +1045,8 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; - return mData[index + AssetManager.STYLE_TYPE]; + index *= STYLE_NUM_ENTRIES; + return mData[index + STYLE_TYPE]; } /** @@ -1063,9 +1065,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; return type != TypedValue.TYPE_NULL; } @@ -1084,11 +1086,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; return type != TypedValue.TYPE_NULL - || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; + || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; } /** @@ -1109,7 +1111,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { return value; } return null; @@ -1181,16 +1183,16 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * AssetManager.STYLE_NUM_ENTRIES; - if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { + final int index = i * STYLE_NUM_ENTRIES; + if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { // Not an attribute, ignore. continue; } // Null the entry so that we can safely call getZzz(). - data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL; + data[index + STYLE_TYPE] = TypedValue.TYPE_NULL; - final int attr = data[index + AssetManager.STYLE_DATA]; + final int attr = data[index + STYLE_DATA]; if (attr == 0) { // Useless data, ignore. continue; @@ -1231,45 +1233,44 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * AssetManager.STYLE_NUM_ENTRIES; - final int type = data[index + AssetManager.STYLE_TYPE]; + final int index = i * STYLE_NUM_ENTRIES; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { continue; } changingConfig |= ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); + data[index + STYLE_CHANGING_CONFIGURATIONS]); } return changingConfig; } private boolean getValueAt(int index, TypedValue outValue) { final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return false; } outValue.type = type; - outValue.data = data[index+AssetManager.STYLE_DATA]; - outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; - outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID]; + outValue.data = data[index + STYLE_DATA]; + outValue.assetCookie = data[index + STYLE_ASSET_COOKIE]; + outValue.resourceId = data[index + STYLE_RESOURCE_ID]; outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); - outValue.density = data[index+AssetManager.STYLE_DENSITY]; + data[index + STYLE_CHANGING_CONFIGURATIONS]); + outValue.density = data[index + STYLE_DENSITY]; outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null; return true; } private CharSequence loadStringValueAt(int index) { final int[] data = mData; - final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; + final int cookie = data[index + STYLE_ASSET_COOKIE]; if (cookie < 0) { if (mXml != null) { - return mXml.getPooledString( - data[index+AssetManager.STYLE_DATA]); + return mXml.getPooledString(data[index + STYLE_DATA]); } return null; } - return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]); + return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]); } /** @hide */ diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java index e6b957414ea8..d4ccffb83ca5 100644 --- a/core/java/android/content/res/XmlBlock.java +++ b/core/java/android/content/res/XmlBlock.java @@ -16,6 +16,7 @@ package android.content.res; +import android.annotation.Nullable; import android.util.TypedValue; import com.android.internal.util.XmlUtils; @@ -33,7 +34,7 @@ import java.io.Reader; * * {@hide} */ -final class XmlBlock { +final class XmlBlock implements AutoCloseable { private static final boolean DEBUG=false; public XmlBlock(byte[] data) { @@ -48,6 +49,7 @@ final class XmlBlock { mStrings = new StringBlock(nativeGetStringBlock(mNative), false); } + @Override public void close() { synchronized (this) { if (mOpen) { @@ -478,13 +480,13 @@ final class XmlBlock { * are doing! The given native object must exist for the entire lifetime * of this newly creating XmlBlock. */ - XmlBlock(AssetManager assets, long xmlBlock) { + XmlBlock(@Nullable AssetManager assets, long xmlBlock) { mAssets = assets; mNative = xmlBlock; mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false); } - private final AssetManager mAssets; + private @Nullable final AssetManager mAssets; private final long mNative; /*package*/ final StringBlock mStrings; private boolean mOpen = true; diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 33f80ce8ffb0..78a3e137ccb0 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -110,8 +110,8 @@ cc_library_shared { "android_util_AssetManager.cpp", "android_util_Binder.cpp", "android_util_EventLog.cpp", - "android_util_MemoryIntArray.cpp", "android_util_Log.cpp", + "android_util_MemoryIntArray.cpp", "android_util_PathParser.cpp", "android_util_Process.cpp", "android_util_StringBlock.cpp", @@ -191,6 +191,7 @@ cc_library_shared { "android_backup_FileBackupHelperBase.cpp", "android_backup_BackupHelperDispatcher.cpp", "android_app_backup_FullBackup.cpp", + "android_content_res_ApkAssets.cpp", "android_content_res_ObbScanner.cpp", "android_content_res_Configuration.cpp", "android_animation_PropertyValuesHolder.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index d20217386b1e..4a032c4bbae4 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -123,6 +123,7 @@ extern int register_android_util_MemoryIntArray(JNIEnv* env); extern int register_android_util_PathParser(JNIEnv* env); extern int register_android_content_StringBlock(JNIEnv* env); extern int register_android_content_XmlBlock(JNIEnv* env); +extern int register_android_content_res_ApkAssets(JNIEnv* env); extern int register_android_graphics_Canvas(JNIEnv* env); extern int register_android_graphics_CanvasProperty(JNIEnv* env); extern int register_android_graphics_ColorFilter(JNIEnv* env); @@ -1346,6 +1347,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_AssetManager), REG_JNI(register_android_content_StringBlock), REG_JNI(register_android_content_XmlBlock), + REG_JNI(register_android_content_res_ApkAssets), REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_Hyphenator), REG_JNI(register_android_text_MeasuredParagraph), diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index 937b3ffb9d60..f6223fad3dcb 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include "Utils.h" #include "FontUtils.h" @@ -217,7 +217,8 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b NPE_CHECK_RETURN_ZERO(env, jpath); NativeFamilyBuilder* builder = reinterpret_cast(builderPtr); - AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr); + + Guarded* mgr = AssetManagerForJavaObject(env, jassetMgr); if (NULL == mgr) { builder->axes.clear(); return false; @@ -229,27 +230,33 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b return false; } - Asset* asset; - if (isAsset) { - asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); - } else { - asset = cookie ? mgr->openNonAsset(static_cast(cookie), str.c_str(), - Asset::ACCESS_BUFFER) : mgr->openNonAsset(str.c_str(), Asset::ACCESS_BUFFER); + std::unique_ptr asset; + { + ScopedLock locked_mgr(*mgr); + if (isAsset) { + asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); + } else if (cookie > 0) { + // Valid java cookies are 1-based, but AssetManager cookies are 0-based. + asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast(cookie - 1), + Asset::ACCESS_BUFFER); + } else { + asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER); + } } - if (NULL == asset) { + if (nullptr == asset) { builder->axes.clear(); return false; } const void* buf = asset->getBuffer(false); if (NULL == buf) { - delete asset; builder->axes.clear(); return false; } - sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset)); + sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, + asset.release())); return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); } diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 09e37e1a3de6..49a24a30f77e 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -361,7 +361,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName code->sdkVersion = sdkVersion; code->javaAssetManager = env->NewGlobalRef(jAssetMgr); - code->assetManager = assetManagerForJavaObject(env, jAssetMgr); + code->assetManager = NdkAssetManagerForJavaObject(env, jAssetMgr); if (obbDir != NULL) { dirStr = env->GetStringUTFChars(obbDir, NULL); diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp new file mode 100644 index 000000000000..c0f151b71c93 --- /dev/null +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "android-base/macros.h" +#include "android-base/stringprintf.h" +#include "android-base/unique_fd.h" +#include "androidfw/ApkAssets.h" +#include "utils/misc.h" + +#include "core_jni_helpers.h" +#include "jni.h" +#include "nativehelper/ScopedUtfChars.h" + +using ::android::base::unique_fd; + +namespace android { + +static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboolean system, + jboolean force_shared_lib, jboolean overlay) { + ScopedUtfChars path(env, java_path); + if (path.c_str() == nullptr) { + return 0; + } + + std::unique_ptr apk_assets; + if (overlay) { + apk_assets = ApkAssets::LoadOverlay(path.c_str(), system); + } else if (force_shared_lib) { + apk_assets = ApkAssets::LoadAsSharedLibrary(path.c_str(), system); + } else { + apk_assets = ApkAssets::Load(path.c_str(), system); + } + + if (apk_assets == nullptr) { + std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str()); + jniThrowException(env, "java/io/IOException", error_msg.c_str()); + return 0; + } + return reinterpret_cast(apk_assets.release()); +} + +static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor, + jstring friendly_name, jboolean system, jboolean force_shared_lib) { + ScopedUtfChars friendly_name_utf8(env, friendly_name); + if (friendly_name_utf8.c_str() == nullptr) { + return 0; + } + + int fd = jniGetFDFromFileDescriptor(env, file_descriptor); + if (fd < 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); + return 0; + } + + unique_fd dup_fd(::dup(fd)); + if (dup_fd < 0) { + jniThrowIOException(env, errno); + return 0; + } + + std::unique_ptr apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), + friendly_name_utf8.c_str(), + system, force_shared_lib); + if (apk_assets == nullptr) { + std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d", + friendly_name_utf8.c_str(), dup_fd.get()); + jniThrowException(env, "java/io/IOException", error_msg.c_str()); + return 0; + } + return reinterpret_cast(apk_assets.release()); +} + +static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + delete reinterpret_cast(ptr); +} + +static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + return env->NewStringUTF(apk_assets->GetPath().c_str()); +} + +static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + return reinterpret_cast(apk_assets->GetLoadedArsc()->GetStringPool()); +} + +static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + (void)apk_assets; + return JNI_TRUE; +} + +static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) { + ScopedUtfChars path_utf8(env, file_name); + if (path_utf8.c_str() == nullptr) { + return 0; + } + + const ApkAssets* apk_assets = reinterpret_cast(ptr); + std::unique_ptr asset = apk_assets->Open(path_utf8.c_str(), + Asset::AccessMode::ACCESS_RANDOM); + if (asset == nullptr) { + jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str()); + return 0; + } + + // DynamicRefTable is only needed when looking up resource references. Opening an XML file + // directly from an ApkAssets has no notion of proper resource references. + std::unique_ptr xml_tree = util::make_unique(nullptr /*dynamicRefTable*/); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); + asset.reset(); + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + return reinterpret_cast(xml_tree.release()); +} + +// JNI registration. +static const JNINativeMethod gApkAssetsMethods[] = { + {"nativeLoad", "(Ljava/lang/String;ZZZ)J", (void*)NativeLoad}, + {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZ)J", + (void*)NativeLoadFromFd}, + {"nativeDestroy", "(J)V", (void*)NativeDestroy}, + {"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath}, + {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock}, + {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate}, + {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml}, +}; + +int register_android_content_res_ApkAssets(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods, + arraysize(gApkAssetsMethods)); +} + +} // namespace android diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 683b4c490ec3..c623ca621292 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -1,1851 +1,1441 @@ -/* //device/libs/android_runtime/android_util_AssetManager.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ +/* + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #define LOG_TAG "asset" -#include - #include #include #include -#include -#include #include #include +#include +#include #include // for AID_SYSTEM +#include "android-base/logging.h" +#include "android-base/properties.h" +#include "android-base/stringprintf.h" +#include "android_runtime/android_util_AssetManager.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_util_Binder.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" +#include "androidfw/AssetManager2.h" #include "androidfw/AttributeResolution.h" +#include "androidfw/MutexGuard.h" #include "androidfw/ResourceTypes.h" -#include "android_runtime/AndroidRuntime.h" -#include "android_util_Binder.h" #include "core_jni_helpers.h" #include "jni.h" -#include -#include -#include +#include "nativehelper/JNIHelp.h" +#include "nativehelper/ScopedPrimitiveArray.h" +#include "nativehelper/ScopedStringChars.h" +#include "nativehelper/ScopedUtfChars.h" #include "utils/Log.h" -#include "utils/misc.h" #include "utils/String8.h" +#include "utils/misc.h" extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap); extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap); +using ::android::base::StringPrintf; namespace android { -static const bool kThrowOnBadId = false; - // ---------------------------------------------------------------------------- -static struct typedvalue_offsets_t -{ - jfieldID mType; - jfieldID mData; - jfieldID mString; - jfieldID mAssetCookie; - jfieldID mResourceId; - jfieldID mChangingConfigurations; - jfieldID mDensity; +static struct typedvalue_offsets_t { + jfieldID mType; + jfieldID mData; + jfieldID mString; + jfieldID mAssetCookie; + jfieldID mResourceId; + jfieldID mChangingConfigurations; + jfieldID mDensity; } gTypedValueOffsets; -static struct assetfiledescriptor_offsets_t -{ - jfieldID mFd; - jfieldID mStartOffset; - jfieldID mLength; +static struct assetfiledescriptor_offsets_t { + jfieldID mFd; + jfieldID mStartOffset; + jfieldID mLength; } gAssetFileDescriptorOffsets; -static struct assetmanager_offsets_t -{ - jfieldID mObject; +static struct assetmanager_offsets_t { + jfieldID mObject; } gAssetManagerOffsets; -static struct sparsearray_offsets_t -{ - jclass classObject; - jmethodID constructor; - jmethodID put; +static struct { + jfieldID native_ptr; +} gApkAssetsFields; + +static struct sparsearray_offsets_t { + jclass classObject; + jmethodID constructor; + jmethodID put; } gSparseArrayOffsets; -static struct configuration_offsets_t -{ - jclass classObject; - jmethodID constructor; - jfieldID mSmallestScreenWidthDpOffset; - jfieldID mScreenWidthDpOffset; - jfieldID mScreenHeightDpOffset; +static struct configuration_offsets_t { + jclass classObject; + jmethodID constructor; + jfieldID mSmallestScreenWidthDpOffset; + jfieldID mScreenWidthDpOffset; + jfieldID mScreenHeightDpOffset; } gConfigurationOffsets; -jclass g_stringClass = NULL; +jclass g_stringClass = nullptr; // ---------------------------------------------------------------------------- -static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, - const Res_value& value, uint32_t ref, ssize_t block, - uint32_t typeSpecFlags, ResTable_config* config = NULL); - -jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, - const Res_value& value, uint32_t ref, ssize_t block, - uint32_t typeSpecFlags, ResTable_config* config) -{ - env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType); - env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie, - static_cast(table->getTableCookie(block))); - env->SetIntField(outValue, gTypedValueOffsets.mData, value.data); - env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL); - env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref); - env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations, - typeSpecFlags); - if (config != NULL) { - env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density); - } - return block; +// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. +constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { + return cookie != kInvalidCookie ? static_cast(cookie + 1) : -1; } -// This is called by zygote (running as user root) as part of preloadResources. -static void verifySystemIdmaps() -{ - pid_t pid; - char system_id[10]; - - snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM); - - switch (pid = fork()) { - case -1: - ALOGE("failed to fork for idmap: %s", strerror(errno)); - break; - case 0: // child - { - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata; - - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - - capheader.version = _LINUX_CAPABILITY_VERSION; - capheader.pid = 0; - - if (capget(&capheader, &capdata) != 0) { - ALOGE("capget: %s\n", strerror(errno)); - exit(1); - } - - capdata.effective = capdata.permitted; - if (capset(&capheader, &capdata) != 0) { - ALOGE("capset: %s\n", strerror(errno)); - exit(1); - } - - if (setgid(AID_SYSTEM) != 0) { - ALOGE("setgid: %s\n", strerror(errno)); - exit(1); - } - - if (setuid(AID_SYSTEM) != 0) { - ALOGE("setuid: %s\n", strerror(errno)); - exit(1); - } - - // Generic idmap parameters - const char* argv[8]; - int argc = 0; - struct stat st; - - memset(argv, NULL, sizeof(argv)); - argv[argc++] = AssetManager::IDMAP_BIN; - argv[argc++] = "--scan"; - argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; - argv[argc++] = AssetManager::TARGET_APK_PATH; - argv[argc++] = AssetManager::IDMAP_DIR; - - // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, - // use OVERLAY_DIR/ in addition to OVERLAY_DIR. - char subdir[PROP_VALUE_MAX]; - int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir); - if (len > 0) { - String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir; - if (stat(overlayPath.string(), &st) == 0) { - argv[argc++] = overlayPath.string(); - } - } - if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::OVERLAY_DIR; - } - - if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; - } - - // Finally, invoke idmap (if any overlay directory exists) - if (argc > 5) { - execv(AssetManager::IDMAP_BIN, (char* const*)argv); - ALOGE("failed to execv for idmap: %s", strerror(errno)); - exit(1); // should never get here - } else { - exit(0); - } - } - break; - default: // parent - waitpid(pid, NULL, 0); - break; - } +constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) { + return cookie > 0 ? static_cast(cookie - 1) : kInvalidCookie; } - -// ---------------------------------------------------------------------------- - -// this guy is exported to other jni routines -AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj) -{ - jlong amHandle = env->GetLongField(obj, gAssetManagerOffsets.mObject); - AssetManager* am = reinterpret_cast(amHandle); - if (am != NULL) { - return am; - } - jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); - return NULL; +// This is called by zygote (running as user root) as part of preloadResources. +static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { + switch (pid_t pid = fork()) { + case -1: + PLOG(ERROR) << "failed to fork for idmap"; + break; + + // child + case 0: { + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata; + + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + + capheader.version = _LINUX_CAPABILITY_VERSION; + capheader.pid = 0; + + if (capget(&capheader, &capdata) != 0) { + PLOG(ERROR) << "capget"; + exit(1); + } + + capdata.effective = capdata.permitted; + if (capset(&capheader, &capdata) != 0) { + PLOG(ERROR) << "capset"; + exit(1); + } + + if (setgid(AID_SYSTEM) != 0) { + PLOG(ERROR) << "setgid"; + exit(1); + } + + if (setuid(AID_SYSTEM) != 0) { + PLOG(ERROR) << "setuid"; + exit(1); + } + + // Generic idmap parameters + const char* argv[8]; + int argc = 0; + struct stat st; + + memset(argv, 0, sizeof(argv)); + argv[argc++] = AssetManager::IDMAP_BIN; + argv[argc++] = "--scan"; + argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; + argv[argc++] = AssetManager::TARGET_APK_PATH; + argv[argc++] = AssetManager::IDMAP_DIR; + + // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, + // use OVERLAY_DIR/ in addition to OVERLAY_DIR. + std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY, + ""); + if (!overlay_theme_path.empty()) { + overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path; + if (stat(overlay_theme_path.c_str(), &st) == 0) { + argv[argc++] = overlay_theme_path.c_str(); + } + } + + if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::OVERLAY_DIR; + } + + if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; + } + + // Finally, invoke idmap (if any overlay directory exists) + if (argc > 5) { + execv(AssetManager::IDMAP_BIN, (char* const*)argv); + PLOG(ERROR) << "failed to execv for idmap"; + exit(1); // should never get here + } else { + exit(0); + } + } break; + + // parent + default: + waitpid(pid, nullptr, 0); + break; + } } -static jlong android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz, - jstring fileName, jint mode) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openAsset in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Empty file name"); - return -1; - } - - if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM - && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return -1; - } - - Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return -1; - } - - //printf("Created Asset Stream: %p\n", a); - - return reinterpret_cast(a); +static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref, + uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType); + env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie, + ApkAssetsCookieToJavaCookie(cookie)); + env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data); + env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr); + env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref); + env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags); + if (config != nullptr) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density); + } + return static_cast(ApkAssetsCookieToJavaCookie(cookie)); } -static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets) -{ - off64_t startOffset, length; - int fd = a->openFileDescriptor(&startOffset, &length); - delete a; - - if (fd < 0) { - jniThrowException(env, "java/io/FileNotFoundException", - "This file can not be opened as a file descriptor; it is probably compressed"); - return NULL; - } - - jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0); - if (offsets == NULL) { - close(fd); - return NULL; - } - - offsets[0] = startOffset; - offsets[1] = length; - - env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0); +// ---------------------------------------------------------------------------- - jobject fileDesc = jniCreateFileDescriptor(env, fd); - if (fileDesc == NULL) { - close(fd); - return NULL; - } +// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance. +struct GuardedAssetManager : public ::AAssetManager { + Guarded guarded_assetmanager; +}; - return newParcelFileDescriptor(env, fileDesc); +::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { + jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject); + ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle); + if (am == nullptr) { + jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); + return nullptr; + } + return am; } -static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz, - jstring fileName, jlongArray outOffsets) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ALOGV("openAssetFd in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - //printf("Created Asset Stream: %p\n", a); - - return returnParcelFileDescriptor(env, a, outOffsets); +Guarded* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) { + if (assetmanager == nullptr) { + return nullptr; + } + return &reinterpret_cast(assetmanager)->guarded_assetmanager; } -static jlong android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName, - jint mode) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return -1; - } - - if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM - && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return -1; - } - - Asset* a = cookie - ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), - (Asset::AccessMode)mode) - : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return -1; - } - - //printf("Created Asset Stream: %p\n", a); - - return reinterpret_cast(a); +Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { + return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager)); } -static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName, - jlongArray outOffsets) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ALOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - Asset* a = cookie - ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), Asset::ACCESS_RANDOM) - : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - //printf("Created Asset Stream: %p\n", a); - - return returnParcelFileDescriptor(env, a, outOffsets); +static Guarded& AssetManagerFromLong(jlong ptr) { + return *AssetManagerForNdkAssetManager(reinterpret_cast(ptr)); } -static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz, - jstring fileName) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - AssetDir* dir = am->openDir(fileName8.c_str()); - - if (dir == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - size_t N = dir->getFileCount(); - - jobjectArray array = env->NewObjectArray(dir->getFileCount(), - g_stringClass, NULL); - if (array == NULL) { - delete dir; - return NULL; - } - - for (size_t i=0; igetFileName(i); - jstring str = env->NewStringUTF(name.string()); - if (str == NULL) { - delete dir; - return NULL; - } - env->SetObjectArrayElement(array, i, str); - env->DeleteLocalRef(str); - } - - delete dir; - - return array; +static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr asset, + jlongArray out_offsets) { + off64_t start_offset, length; + int fd = asset->openFileDescriptor(&start_offset, &length); + asset.reset(); + + if (fd < 0) { + jniThrowException(env, "java/io/FileNotFoundException", + "This file can not be opened as a file descriptor; it is probably " + "compressed"); + return nullptr; + } + + jlong* offsets = reinterpret_cast(env->GetPrimitiveArrayCritical(out_offsets, 0)); + if (offsets == nullptr) { + close(fd); + return nullptr; + } + + offsets[0] = start_offset; + offsets[1] = length; + + env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0); + + jobject file_desc = jniCreateFileDescriptor(env, fd); + if (file_desc == nullptr) { + close(fd); + return nullptr; + } + return newParcelFileDescriptor(env, file_desc); } -static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - //printf("Destroying Asset Stream: %p\n", a); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return; - } - - delete a; +static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) { + return Asset::getGlobalCount(); } -static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - uint8_t b; - ssize_t res = a->read(&b, 1); - return res == 1 ? b : -1; +static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) { + String8 alloc = Asset::getAssetAllocations(); + if (alloc.length() <= 0) { + return nullptr; + } + return env->NewStringUTF(alloc.string()); } -static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz, - jlong assetHandle, jbyteArray bArray, - jint off, jint len) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL || bArray == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - if (len == 0) { - return 0; - } - - jsize bLen = env->GetArrayLength(bArray); - if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); - return -1; - } - - jbyte* b = env->GetByteArrayElements(bArray, NULL); - ssize_t res = a->read(b+off, len); - env->ReleaseByteArrayElements(bArray, b, 0); - - if (res > 0) return static_cast(res); - - if (res < 0) { - jniThrowException(env, "java/io/IOException", ""); - } - return -1; +static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) { + // TODO(adamlesinski): Switch to AssetManager2. + return AssetManager::getGlobalCount(); } -static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz, - jlong assetHandle, - jlong offset, jint whence) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - return a->seek( - offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)); +static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) { + // AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and + // AssetManager2 in a contiguous block (GuardedAssetManager). + return reinterpret_cast(new GuardedAssetManager()); } -static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - return a->getLength(); +static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + delete reinterpret_cast(ptr); } -static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; +static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jobjectArray apk_assets_array, jboolean invalidate_caches) { + const jsize apk_assets_len = env->GetArrayLength(apk_assets_array); + std::vector apk_assets; + apk_assets.reserve(apk_assets_len); + for (jsize i = 0; i < apk_assets_len; i++) { + jobject obj = env->GetObjectArrayElement(apk_assets_array, i); + if (obj == nullptr) { + std::string msg = StringPrintf("ApkAssets at index %d is null", i); + jniThrowNullPointerException(env, msg.c_str()); + return; } - return a->getRemainingLength(); -} - -static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz, - jstring path, jboolean appAsLib) -{ - ScopedUtfChars path8(env, path); - if (path8.c_str() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; + jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr); + if (env->ExceptionCheck()) { + return; } + apk_assets.push_back(reinterpret_cast(apk_assets_native_ptr)); + } - int32_t cookie; - bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib); - - return (res) ? static_cast(cookie) : 0; + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + assetmanager->SetApkAssets(apk_assets, invalidate_caches); } -static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz, - jstring idmapPath) -{ - ScopedUtfChars idmapPath8(env, idmapPath); - if (idmapPath8.c_str() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - int32_t cookie; - bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie); - - return (res) ? (jint)cookie : 0; +static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc, + jstring locale, jint orientation, jint touchscreen, jint density, + jint keyboard, jint keyboard_hidden, jint navigation, + jint screen_width, jint screen_height, + jint smallest_screen_width_dp, jint screen_width_dp, + jint screen_height_dp, jint screen_layout, jint ui_mode, + jint color_mode, jint major_version) { + ResTable_config configuration; + memset(&configuration, 0, sizeof(configuration)); + configuration.mcc = static_cast(mcc); + configuration.mnc = static_cast(mnc); + configuration.orientation = static_cast(orientation); + configuration.touchscreen = static_cast(touchscreen); + configuration.density = static_cast(density); + configuration.keyboard = static_cast(keyboard); + configuration.inputFlags = static_cast(keyboard_hidden); + configuration.navigation = static_cast(navigation); + configuration.screenWidth = static_cast(screen_width); + configuration.screenHeight = static_cast(screen_height); + configuration.smallestScreenWidthDp = static_cast(smallest_screen_width_dp); + configuration.screenWidthDp = static_cast(screen_width_dp); + configuration.screenHeightDp = static_cast(screen_height_dp); + configuration.screenLayout = static_cast(screen_layout); + configuration.uiMode = static_cast(ui_mode); + configuration.colorMode = static_cast(color_mode); + configuration.sdkVersion = static_cast(major_version); + + if (locale != nullptr) { + ScopedUtfChars locale_utf8(env, locale); + CHECK(locale_utf8.c_str() != nullptr); + configuration.setBcp47Locale(locale_utf8.c_str()); + } + + // Constants duplicated from Java class android.content.res.Configuration. + static const jint kScreenLayoutRoundMask = 0x300; + static const jint kScreenLayoutRoundShift = 8; + + // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer + // in C++. We must extract the round qualifier out of the Java screenLayout and put it + // into screenLayout2. + configuration.screenLayout2 = + static_cast((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + assetmanager->SetConfiguration(configuration); } -static jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz, - jobject fileDescriptor, jstring debugPathName, - jboolean appAsLib) -{ - ScopedUtfChars debugPathName8(env, debugPathName); +static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); - int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (fd < 0) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); - return 0; - } + jobject sparse_array = + env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor); - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } + if (sparse_array == nullptr) { + // An exception is pending. + return nullptr; + } - int dupfd = ::dup(fd); - if (dupfd < 0) { - jniThrowIOException(env, errno); - return 0; + assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) { + jstring jpackage_name = env->NewStringUTF(package_name.c_str()); + if (jpackage_name == nullptr) { + // An exception is pending. + return; } - int32_t cookie; - bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib); - - return (res) ? static_cast(cookie) : 0; -} - -static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_TRUE; - } - return am->isUpToDate() ? JNI_TRUE : JNI_FALSE; + env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast(package_id), + jpackage_name); + }); + return sparse_array; } -static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales) -{ - Vector locales; - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; +static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) { + ScopedUtfChars path_utf8(env, path); + if (path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + std::vector all_file_paths; + { + StringPiece normalized_path = path_utf8.c_str(); + if (normalized_path.data()[0] == '/') { + normalized_path = normalized_path.substr(1); + } + std::string root_path = StringPrintf("assets/%s", normalized_path.data()); + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + for (const ApkAssets* assets : assetmanager->GetApkAssets()) { + assets->ForEachFile(root_path, [&](const StringPiece& file_path, FileType type) { + if (type == FileType::kFileTypeRegular) { + all_file_paths.push_back(file_path.to_string()); + } + }); } + } - am->getLocales(&locales, includeSystemLocales); + jobjectArray array = env->NewObjectArray(all_file_paths.size(), g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } - const int N = locales.size(); + jsize index = 0; + for (const std::string& file_path : all_file_paths) { + jstring java_string = env->NewStringUTF(file_path.c_str()); - jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL); - if (result == NULL) { - return NULL; + // Check for errors creating the strings (if malformed or no memory). + if (env->ExceptionCheck()) { + return nullptr; } - for (int i=0; iNewStringUTF(locales[i].string()); - if (str == NULL) { - return NULL; - } - env->SetObjectArrayElement(result, i, str); - env->DeleteLocalRef(str); - } + env->SetObjectArrayElement(array, index++, java_string); - return result; + // If we have a large amount of string in our array, we might overflow the + // local reference table of the VM. + env->DeleteLocalRef(java_string); + } + return array; } -static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz) -{ - return getLocales(env, clazz, true /* include system locales */); +static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, + jint access_mode) { + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && + access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset = + assetmanager->Open(asset_path_utf8.c_str(), static_cast(access_mode)); + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + return reinterpret_cast(asset.release()); } -static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz) -{ - return getLocales(env, clazz, false /* don't include system locales */); +static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, + jlongArray out_offsets) { + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return nullptr; + } + return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); } -static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) { - jobject result = env->NewObject(gConfigurationOffsets.classObject, - gConfigurationOffsets.constructor); - if (result == NULL) { - return NULL; - } - - env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, - config.smallestScreenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); - - return result; +static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path, jint access_mode) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && + access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, + static_cast(access_mode)); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), + static_cast(access_mode)); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + return reinterpret_cast(asset.release()); } -static jobjectArray getSizeConfigurationsInternal(JNIEnv* env, - const Vector& configs) { - const int N = configs.size(); - jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL); - if (result == NULL) { - return NULL; - } - - for (int i=0; iDeleteLocalRef(result); - return NULL; - } - - env->SetObjectArrayElement(result, i, config); - env->DeleteLocalRef(config); - } - - return result; +static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path, jlongArray out_offsets) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return nullptr; + } + return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); } -static jobjectArray android_content_AssetManager_getSizeConfigurations(JNIEnv* env, jobject clazz) { - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - const ResTable& res(am->getResources()); - Vector configs; - res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */); - - return getSizeConfigurationsInternal(env, configs); +static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + + // May be nullptr. + const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie); + + std::unique_ptr xml_tree = util::make_unique(dynamic_ref_table); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); + asset.reset(); + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + return reinterpret_cast(xml_tree.release()); } -static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz, - jint mcc, jint mnc, - jstring locale, jint orientation, - jint touchscreen, jint density, - jint keyboard, jint keyboardHidden, - jint navigation, - jint screenWidth, jint screenHeight, - jint smallestScreenWidthDp, - jint screenWidthDp, jint screenHeightDp, - jint screenLayout, jint uiMode, - jint colorMode, jint sdkVersion) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return; - } - - ResTable_config config; - memset(&config, 0, sizeof(config)); - - const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL; - - // Constants duplicated from Java class android.content.res.Configuration. - static const jint kScreenLayoutRoundMask = 0x300; - static const jint kScreenLayoutRoundShift = 8; - - config.mcc = (uint16_t)mcc; - config.mnc = (uint16_t)mnc; - config.orientation = (uint8_t)orientation; - config.touchscreen = (uint8_t)touchscreen; - config.density = (uint16_t)density; - config.keyboard = (uint8_t)keyboard; - config.inputFlags = (uint8_t)keyboardHidden; - config.navigation = (uint8_t)navigation; - config.screenWidth = (uint16_t)screenWidth; - config.screenHeight = (uint16_t)screenHeight; - config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp; - config.screenWidthDp = (uint16_t)screenWidthDp; - config.screenHeightDp = (uint16_t)screenHeightDp; - config.screenLayout = (uint8_t)screenLayout; - config.uiMode = (uint8_t)uiMode; - config.colorMode = (uint8_t)colorMode; - config.sdkVersion = (uint16_t)sdkVersion; - config.minorVersion = 0; - - // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer - // in C++. We must extract the round qualifier out of the Java screenLayout and put it - // into screenLayout2. - config.screenLayout2 = - (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); - - am->setConfiguration(config, locale8); - - if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8); +static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jshort density, jobject typed_value, + jboolean resolve_references) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Res_value value; + ResTable_config selected_config; + uint32_t flags; + ApkAssetsCookie cookie = + assetmanager->GetResource(static_cast(resid), false /*may_be_bag*/, + static_cast(density), &value, &selected_config, &flags); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t ref = static_cast(resid); + if (resolve_references) { + cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + } + return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value); } -static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz, - jstring name, - jstring defType, - jstring defPackage) -{ - ScopedStringChars name16(env, name); - if (name16.get() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - const char16_t* defType16 = reinterpret_cast(defType) - ? reinterpret_cast(env->GetStringChars(defType, NULL)) - : NULL; - jsize defTypeLen = defType - ? env->GetStringLength(defType) : 0; - const char16_t* defPackage16 = reinterpret_cast(defPackage) - ? reinterpret_cast(env->GetStringChars(defPackage, - NULL)) - : NULL; - jsize defPackageLen = defPackage - ? env->GetStringLength(defPackage) : 0; - - jint ident = am->getResources().identifierForName( - reinterpret_cast(name16.get()), name16.size(), - defType16, defTypeLen, defPackage16, defPackageLen); - - if (defPackage16) { - env->ReleaseStringChars(defPackage, - reinterpret_cast(defPackage16)); - } - if (defType16) { - env->ReleaseStringChars(defType, - reinterpret_cast(defType16)); - } - - return ident; +static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jint bag_entry_id, jobject typed_value) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t type_spec_flags = bag->type_spec_flags; + ApkAssetsCookie cookie = kInvalidCookie; + const Res_value* bag_value = nullptr; + for (const ResolvedBag::Entry& entry : bag) { + if (entry.key == static_cast(bag_entry_id)) { + cookie = entry.cookie; + bag_value = &entry.value; + + // Keep searching (the old implementation did that). + } + } + + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + Res_value value = *bag_value; + uint32_t ref = static_cast(resid); + ResTable_config selected_config; + cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value); } -static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - String16 str; - if (name.package != NULL) { - str.setTo(name.package, name.packageLen); - } - if (name.type8 != NULL || name.type != NULL) { - if (str.size() > 0) { - char16_t div = ':'; - str.append(&div, 1); - } - if (name.type8 != NULL) { - str.append(String16(name.type8, name.typeLen)); - } else { - str.append(name.type, name.typeLen); - } - } - if (name.name8 != NULL || name.name != NULL) { - if (str.size() > 0) { - char16_t div = '/'; - str.append(&div, 1); - } - if (name.name8 != NULL) { - str.append(String16(name.name8, name.nameLen)); - } else { - str.append(name.name, name.nameLen); - } - } - - return env->NewString((const jchar*)str.string(), str.size()); +static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count); + if (env->ExceptionCheck()) { + return nullptr; + } + + for (uint32_t i = 0; i < bag->entry_count; i++) { + jint attr_resid = bag->entries[i].key; + env->SetIntArrayRegion(array, i, 1, &attr_resid); + } + return array; } -static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - if (name.package != NULL) { - return env->NewString((const jchar*)name.package, name.packageLen); - } - - return NULL; +static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } + + for (uint32_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + + // Resolve any references to their final value. + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return nullptr; + } + + if (value.dataType == Res_value::TYPE_STRING) { + const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie]; + const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool(); + + jstring java_string = nullptr; + size_t str_len; + const char* str_utf8 = pool->string8At(value.data, &str_len); + if (str_utf8 != nullptr) { + java_string = env->NewStringUTF(str_utf8); + } else { + const char16_t* str_utf16 = pool->stringAt(value.data, &str_len); + java_string = env->NewString(reinterpret_cast(str_utf16), str_len); + } + + // Check for errors creating the strings (if malformed or no memory). + if (env->ExceptionCheck()) { + return nullptr; + } + + env->SetObjectArrayElement(array, i, java_string); + + // If we have a large amount of string in our array, we might overflow the + // local reference table of the VM. + env->DeleteLocalRef(java_string); + } + } + return array; } -static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - if (name.type8 != NULL) { - return env->NewStringUTF(name.type8); - } - - if (name.type != NULL) { - return env->NewString((const jchar*)name.type, name.typeLen); - } +static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count * 2); + if (array == nullptr) { + return nullptr; + } + + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); + if (buffer == nullptr) { + return nullptr; + } + + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); + return nullptr; + } + + jint string_index = -1; + if (value.dataType == Res_value::TYPE_STRING) { + string_index = static_cast(value.data); + } + + buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie); + buffer[(i * 2) + 1] = string_index; + } + env->ReleasePrimitiveArrayCritical(array, buffer, 0); + return array; +} - return NULL; +static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count); + if (array == nullptr) { + return nullptr; + } + + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); + if (buffer == nullptr) { + return nullptr; + } + + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); + return nullptr; + } + + if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) { + buffer[i] = static_cast(value.data); + } + } + env->ReleasePrimitiveArrayCritical(array, buffer, 0); + return array; } -static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } +static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return -1; + } + return static_cast(bag->entry_count); +} - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } +static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jintArray out_data) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return -1; + } - if (name.name8 != NULL) { - return env->NewStringUTF(name.name8); - } + const jsize out_data_length = env->GetArrayLength(out_data); + if (env->ExceptionCheck()) { + return -1; + } - if (name.name != NULL) { - return env->NewString((const jchar*)name.name, name.nameLen); - } + if (static_cast(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough"); + return -1; + } - return NULL; + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(out_data, nullptr)); + if (buffer == nullptr) { + return -1; + } + + jint* cursor = buffer; + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + selected_config.density = 0; + uint32_t flags = bag->type_spec_flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT); + return -1; + } + + // Deal with the special @null value -- it turns back to TYPE_NULL. + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + } + + cursor[STYLE_TYPE] = static_cast(value.dataType); + cursor[STYLE_DATA] = static_cast(value.data); + cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + cursor[STYLE_RESOURCE_ID] = static_cast(ref); + cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast(flags); + cursor[STYLE_DENSITY] = static_cast(selected_config.density); + cursor += STYLE_NUM_ENTRIES; + } + env->ReleasePrimitiveArrayCritical(out_data, buffer, 0); + return static_cast(bag->entry_count); } -static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz, - jint ident, - jshort density, - jobject outValue, - jboolean resolve) -{ - if (outValue == NULL) { - jniThrowNullPointerException(env, "outValue"); - return 0; - } - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - Res_value value; - ResTable_config config; - uint32_t typeSpecFlags; - ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - uint32_t ref = ident; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - } - if (block >= 0) { - return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config); - } - - return static_cast(block); +static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name, + jstring def_type, jstring def_package) { + ScopedUtfChars name_utf8(env, name); + if (name_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + std::string type; + if (def_type != nullptr) { + ScopedUtfChars type_utf8(env, def_type); + CHECK(type_utf8.c_str() != nullptr); + type = type_utf8.c_str(); + } + + std::string package; + if (def_package != nullptr) { + ScopedUtfChars package_utf8(env, def_package); + CHECK(package_utf8.c_str() != nullptr); + package = package_utf8.c_str(); + } + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + return static_cast(assetmanager->GetResourceId(name_utf8.c_str(), type, package)); } -static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz, - jint ident, jint bagEntryId, - jobject outValue, jboolean resolve) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - - ssize_t block = -1; - Res_value value; +static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } - const ResTable::bag_entry* entry = NULL; - uint32_t typeSpecFlags; - ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags); + std::string result; + if (name.package != nullptr) { + result.append(name.package, name.package_len); + } - for (ssize_t i=0; imap.name.ident) { - block = entry->stringBlock; - value = entry->map.value; - } - entry++; + if (name.type != nullptr || name.type16 != nullptr) { + if (!result.empty()) { + result += ":"; } - res.unlock(); - - if (block < 0) { - return static_cast(block); + if (name.type != nullptr) { + result.append(name.type, name.type_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len)); } + } - uint32_t ref = ident; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - } - if (block >= 0) { - return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags); + if (name.entry != nullptr || name.entry16 != nullptr) { + if (!result.empty()) { + result += "/"; } - return static_cast(block); -} - -static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; + if (name.entry != nullptr) { + result.append(name.entry, name.entry_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); } - return am->getResources().getTableCount(); + } + return env->NewStringUTF(result.c_str()); } -static jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz, - jint block) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - return reinterpret_cast(am->getResources().getTableStringBlock(block)); +static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.package != nullptr) { + return env->NewStringUTF(name.package); + } + return nullptr; } -static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz, - jint cookie) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - String8 name(am->getAssetPath(static_cast(cookie))); - if (name.length() == 0) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name"); - return NULL; - } - jstring str = env->NewStringUTF(name.string()); - return str; +static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.type != nullptr) { + return env->NewStringUTF(name.type); + } else if (name.type16 != nullptr) { + return env->NewString(reinterpret_cast(name.type16), name.type_len); + } + return nullptr; } -static jobject android_content_AssetManager_getAssignedPackageIdentifiers(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - const ResTable& res = am->getResources(); - - jobject sparseArray = env->NewObject(gSparseArrayOffsets.classObject, - gSparseArrayOffsets.constructor); - const size_t N = res.getBasePackageCount(); - for (size_t i = 0; i < N; i++) { - const String16 name = res.getBasePackageName(i); - env->CallVoidMethod( - sparseArray, gSparseArrayOffsets.put, - static_cast(res.getBasePackageId(i)), - env->NewString(reinterpret_cast(name.string()), - name.size())); - } - return sparseArray; +static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.entry != nullptr) { + return env->NewStringUTF(name.entry); + } else if (name.entry16 != nullptr) { + return env->NewString(reinterpret_cast(name.entry16), name.entry_len); + } + return nullptr; } -static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - return reinterpret_cast(new ResTable::Theme(am->getResources())); +static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr, + jboolean exclude_system) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::set locales = + assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/); + + jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } + + size_t idx = 0; + for (const std::string& locale : locales) { + jstring java_string = env->NewStringUTF(locale.c_str()); + if (java_string == nullptr) { + return nullptr; + } + env->SetObjectArrayElement(array, idx++, java_string); + env->DeleteLocalRef(java_string); + } + return array; } -static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz, - jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - delete theme; +static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) { + jobject result = + env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor); + if (result == nullptr) { + return nullptr; + } + + env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, + config.smallestScreenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); + return result; } -static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz, - jlong themeHandle, - jint styleRes, - jboolean force) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - theme->applyStyle(styleRes, force ? true : false); -} - -static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz, - jlong destHandle, jlong srcHandle) -{ - ResTable::Theme* dest = reinterpret_cast(destHandle); - ResTable::Theme* src = reinterpret_cast(srcHandle); - dest->setTo(*src); -} +static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::set configurations = + assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/); -static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - theme->clear(); -} + jobjectArray array = + env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr); + if (array == nullptr) { + return nullptr; + } -static jint android_content_AssetManager_loadThemeAttributeValue( - JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - const ResTable& res(theme->getResTable()); - - Res_value value; - // XXX value could be different in different configs! - uint32_t typeSpecFlags = 0; - ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags); - uint32_t ref = 0; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } + size_t idx = 0; + for (const ResTable_config& configuration : configurations) { + jobject java_configuration = ConstructConfigurationObject(env, configuration); + if (java_configuration == nullptr) { + return nullptr; } - return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block; -} -static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz, - jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - return theme->getChangingConfigurations(); + env->SetObjectArrayElement(array, idx++, java_configuration); + env->DeleteLocalRef(java_configuration); + } + return array; } -static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz, - jlong themeHandle, jint pri, - jstring tag, jstring prefix) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - const ResTable& res(theme->getResTable()); - (void)res; - - // XXX Need to use params. - theme->dumpToLog(); +static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr, + jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); + uint32_t* out_values = reinterpret_cast(out_values_ptr); + uint32_t* out_indices = reinterpret_cast(out_indices_ptr); + + jsize attrs_len = env->GetArrayLength(java_attrs); + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return; + } + + ApplyStyle(theme, xml_parser, static_cast(def_style_attr), + static_cast(def_style_resid), reinterpret_cast(attrs), attrs_len, + out_values, out_indices); + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); } -static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz, - jlong themeToken, - jint defStyleAttr, - jint defStyleRes, - jintArray inValues, - jintArray attrs, - jintArray outValues, - jintArray outIndices) -{ - if (themeToken == 0) { - jniThrowNullPointerException(env, "theme token"); - return JNI_FALSE; - } - if (attrs == NULL) { - jniThrowNullPointerException(env, "attrs"); - return JNI_FALSE; - } - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - const jsize NI = env->GetArrayLength(attrs); - const jsize NV = env->GetArrayLength(outValues); - if (NV < (NI*STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); +static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint def_style_attr, jint def_style_resid, jintArray java_values, + jintArray java_attrs, jintArray out_java_values, + jintArray out_java_indices) { + const jsize attrs_len = env->GetArrayLength(java_attrs); + const jsize out_values_len = env->GetArrayLength(out_java_values); + if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); + return JNI_FALSE; + } + + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return JNI_FALSE; + } + + jint* values = nullptr; + jsize values_len = 0; + if (java_values != nullptr) { + values_len = env->GetArrayLength(java_values); + values = reinterpret_cast(env->GetPrimitiveArrayCritical(java_values, nullptr)); + if (values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return JNI_FALSE; + } + } + + jint* out_values = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); + if (out_values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + return JNI_FALSE; + } + + jint* out_indices = nullptr; + if (out_java_indices != nullptr) { + jsize out_indices_len = env->GetArrayLength(out_java_indices); + if (out_indices_len > attrs_len) { + out_indices = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); + if (out_indices == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); return JNI_FALSE; - } + } + } + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + bool result = ResolveAttrs( + theme, static_cast(def_style_attr), static_cast(def_style_resid), + reinterpret_cast(values), values_len, reinterpret_cast(attrs), + attrs_len, reinterpret_cast(out_values), reinterpret_cast(out_indices)); + if (out_indices != nullptr) { + env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); + } + + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return result ? JNI_TRUE : JNI_FALSE; +} - jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); - if (src == NULL) { +static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jlong xml_parser_ptr, jintArray java_attrs, + jintArray out_java_values, jintArray out_java_indices) { + const jsize attrs_len = env->GetArrayLength(java_attrs); + const jsize out_values_len = env->GetArrayLength(out_java_values); + if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); + return JNI_FALSE; + } + + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return JNI_FALSE; + } + + jint* out_values = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); + if (out_values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return JNI_FALSE; + } + + jint* out_indices = nullptr; + if (out_java_indices != nullptr) { + jsize out_indices_len = env->GetArrayLength(out_java_indices); + if (out_indices_len > attrs_len) { + out_indices = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); + if (out_indices == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); return JNI_FALSE; + } } + } - jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0); - const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues); + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - if (baseDest == NULL) { - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return JNI_FALSE; - } - - jint* indices = NULL; - if (outIndices != NULL) { - if (env->GetArrayLength(outIndices) > NI) { - indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); - } - } + bool result = RetrieveAttributes(assetmanager.get(), xml_parser, + reinterpret_cast(attrs), attrs_len, + reinterpret_cast(out_values), + reinterpret_cast(out_indices)); - ResTable::Theme* theme = reinterpret_cast(themeToken); - bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes, - (uint32_t*) srcValues, NSV, - (uint32_t*) src, NI, - (uint32_t*) baseDest, - (uint32_t*) indices); - - if (indices != NULL) { - env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); - } - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0); - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return result ? JNI_TRUE : JNI_FALSE; + if (out_indices != nullptr) { + env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); + } + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return result ? JNI_TRUE : JNI_FALSE; } -static void android_content_AssetManager_applyStyle(JNIEnv* env, jobject, jlong themeToken, - jint defStyleAttr, jint defStyleRes, jlong xmlParserToken, jintArray attrsObj, jint length, - jlong outValuesAddress, jlong outIndicesAddress) { - jint* attrs = env->GetIntArrayElements(attrsObj, 0); - ResTable::Theme* theme = reinterpret_cast(themeToken); - ResXMLParser* xmlParser = reinterpret_cast(xmlParserToken); - uint32_t* outValues = reinterpret_cast(static_cast(outValuesAddress)); - uint32_t* outIndices = reinterpret_cast(static_cast(outIndicesAddress)); - ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes, - reinterpret_cast(attrs), length, outValues, outIndices); - env->ReleaseIntArrayElements(attrsObj, attrs, JNI_ABORT); +static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + return reinterpret_cast(assetmanager->NewTheme().release()); } -static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz, - jlong xmlParserToken, - jintArray attrs, - jintArray outValues, - jintArray outIndices) -{ - if (xmlParserToken == 0) { - jniThrowNullPointerException(env, "xmlParserToken"); - return JNI_FALSE; - } - if (attrs == NULL) { - jniThrowNullPointerException(env, "attrs"); - return JNI_FALSE; - } - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_FALSE; - } - const ResTable& res(am->getResources()); - ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken; - - const jsize NI = env->GetArrayLength(attrs); - const jsize NV = env->GetArrayLength(outValues); - if (NV < (NI*STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); - return JNI_FALSE; - } - - jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); - if (src == NULL) { - return JNI_FALSE; - } - - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - if (baseDest == NULL) { - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return JNI_FALSE; - } - - jint* indices = NULL; - if (outIndices != NULL) { - if (env->GetArrayLength(outIndices) > NI) { - indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); - } - } - - bool result = RetrieveAttributes(&res, xmlParser, - (uint32_t*) src, NI, - (uint32_t*) baseDest, - (uint32_t*) indices); - - if (indices != NULL) { - env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); - } - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return result ? JNI_TRUE : JNI_FALSE; +static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { + delete reinterpret_cast(theme_ptr); } -static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz, - jint id) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - res.lock(); - const ResTable::bag_entry* defStyleEnt = NULL; - ssize_t bagOff = res.getBagLocked(id, &defStyleEnt); - res.unlock(); - - return static_cast(bagOff); +static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint resid, jboolean force) { + // AssetManager is accessed via the theme, so grab an explicit lock here. + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + theme->ApplyStyle(static_cast(resid), force); + + // TODO(adamlesinski): Consider surfacing exception when result is failure. + // CTS currently expects no exceptions from this method. + // std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid); + // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str()); } -static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz, - jint id, - jintArray outValues) -{ - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_FALSE; - } - const ResTable& res(am->getResources()); - ResTable_config config; - Res_value value; - ssize_t block; - - const jsize NV = env->GetArrayLength(outValues); - - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - jint* dest = baseDest; - if (dest == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", ""); - return JNI_FALSE; - } - - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - - const ResTable::bag_entry* arrayEnt = NULL; - uint32_t arrayTypeSetFlags = 0; - ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags); - const ResTable::bag_entry* endArrayEnt = arrayEnt + - (bagOff >= 0 ? bagOff : 0); - - int i = 0; - uint32_t typeSetFlags; - while (i < NV && arrayEnt < endArrayEnt) { - block = arrayEnt->stringBlock; - typeSetFlags = arrayTypeSetFlags; - config.density = 0; - value = arrayEnt->map.value; - - uint32_t resid = 0; - if (value.dataType != Res_value::TYPE_NULL) { - // Take care of resolving the found resource to its final value. - //printf("Resolving attribute reference\n"); - ssize_t newBlock = res.resolveReference(&value, block, &resid, - &typeSetFlags, &config); - if (kThrowOnBadId) { - if (newBlock == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return JNI_FALSE; - } - } - if (newBlock >= 0) block = newBlock; - } - - // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; - } - - //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data); - - // Write the final value back to Java. - dest[STYLE_TYPE] = value.dataType; - dest[STYLE_DATA] = value.data; - dest[STYLE_ASSET_COOKIE] = reinterpret_cast(res.getTableCookie(block)); - dest[STYLE_RESOURCE_ID] = resid; - dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags; - dest[STYLE_DENSITY] = config.density; - dest += STYLE_NUM_ENTRIES; - i+= STYLE_NUM_ENTRIES; - arrayEnt++; - } - - i /= STYLE_NUM_ENTRIES; - - res.unlock(); - - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - - return i; +static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr, + jlong src_theme_ptr) { + Theme* dst_theme = reinterpret_cast(dst_theme_ptr); + Theme* src_theme = reinterpret_cast(src_theme_ptr); + if (!dst_theme->SetTo(*src_theme)) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "Themes are from different AssetManagers"); + } } -static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return 0; - } - - int32_t assetCookie = static_cast(cookie); - Asset* a = assetCookie - ? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER) - : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return 0; - } - - const DynamicRefTable* dynamicRefTable = - am->getResources().getDynamicRefTableForCookie(assetCookie); - ResXMLTree* block = new ResXMLTree(dynamicRefTable); - status_t err = block->setTo(a->getBuffer(true), a->getLength(), true); - a->close(); - delete a; - - if (err != NO_ERROR) { - jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); - return 0; - } - - return reinterpret_cast(block); +static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { + reinterpret_cast(theme_ptr)->Clear(); } -static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N * 2); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - for (size_t i = 0, j = 0; ((ssize_t)i)map.value; - - // Take care of resolving the found resource to its final value. - stringBlock = res.resolveReference(&value, bag->stringBlock, NULL); - if (value.dataType == Res_value::TYPE_STRING) { - stringIndex = value.data; - } - - if (kThrowOnBadId) { - if (stringBlock == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - - //todo: It might be faster to allocate a C array to contain - // the blocknums and indices, put them in there and then - // do just one SetIntArrayRegion() - env->SetIntArrayRegion(array, j, 1, &stringBlock); - env->SetIntArrayRegion(array, j + 1, 1, &stringIndex); - j = j + 2; - } - res.unlockBag(startOfBag); - return array; +static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint resid, jobject typed_value, + jboolean resolve_references) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie = theme->GetAttribute(static_cast(resid), &value, &flags); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t ref = 0u; + if (resolve_references) { + ResTable_config selected_config; + cookie = + theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + } + return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value); } -static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL); - if (env->ExceptionCheck()) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - size_t strLen = 0; - for (size_t i=0; ((ssize_t)i)map.value; - jstring str = NULL; - - // Take care of resolving the found resource to its final value. - ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - if (value.dataType == Res_value::TYPE_STRING) { - const ResStringPool* pool = res.getTableStringBlock(block); - const char* str8 = pool->string8At(value.data, &strLen); - if (str8 != NULL) { - str = env->NewStringUTF(str8); - } else { - const char16_t* str16 = pool->stringAt(value.data, &strLen); - str = env->NewString(reinterpret_cast(str16), - strLen); - } - - // If one of our NewString{UTF} calls failed due to memory, an - // exception will be pending. - if (env->ExceptionCheck()) { - res.unlockBag(startOfBag); - return NULL; - } - - env->SetObjectArrayElement(array, i, str); - - // str is not NULL at that point, otherwise ExceptionCheck would have been true. - // If we have a large amount of strings in our array, we might - // overflow the local reference table of the VM. - env->DeleteLocalRef(str); - } - } - res.unlockBag(startOfBag); - return array; +static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint priority, jstring tag, jstring prefix) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + (void) theme; + (void) priority; + (void) tag; + (void) prefix; } -static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - for (size_t i=0; ((ssize_t)i)map.value; - - // Take care of resolving the found resource to its final value. - ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - if (value.dataType >= Res_value::TYPE_FIRST_INT - && value.dataType <= Res_value::TYPE_LAST_INT) { - int intVal = value.data; - env->SetIntArrayRegion(array, i, 1, &intVal); - } - } - res.unlockBag(startOfBag); - return array; +static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/, + jlong theme_ptr) { + Theme* theme = reinterpret_cast(theme_ptr); + return static_cast(theme->GetChangingConfigurations()); } -static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz, - jint styleId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(styleId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } +static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + delete reinterpret_cast(asset_ptr); +} - const ResTable::bag_entry* bag = startOfBag; - for (size_t i=0; ((ssize_t)i)map.name.ident; - env->SetIntArrayRegion(array, i, 1, &resourceId); - } - res.unlockBag(startOfBag); - return array; +static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + uint8_t b; + ssize_t res = asset->read(&b, sizeof(b)); + return res == sizeof(b) ? static_cast(b) : -1; } -static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem) -{ - if (isSystem) { - verifySystemIdmaps(); - } - AssetManager* am = new AssetManager(); - if (am == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", ""); - return; - } +static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer, + jint offset, jint len) { + if (len == 0) { + return 0; + } - am->addDefaultAssets(); + jsize buffer_len = env->GetArrayLength(java_buffer); + if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len || + offset > buffer_len - len) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); + return -1; + } - ALOGV("Created AssetManager %p for Java object %p\n", am, clazz); - env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast(am)); -} + ScopedByteArrayRW byte_array(env, java_buffer); + if (byte_array.get() == nullptr) { + return -1; + } -static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz) -{ - AssetManager* am = (AssetManager*) - (env->GetLongField(clazz, gAssetManagerOffsets.mObject)); - ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz); - if (am != NULL) { - delete am; - env->SetLongField(clazz, gAssetManagerOffsets.mObject, 0); - } + Asset* asset = reinterpret_cast(asset_ptr); + ssize_t res = asset->read(byte_array.get() + offset, len); + if (res < 0) { + jniThrowException(env, "java/io/IOException", ""); + return -1; + } + return res > 0 ? static_cast(res) : -1; } -static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz) -{ - return Asset::getGlobalCount(); +static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset, + jint whence) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->seek( + static_cast(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)))); } -static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz) -{ - String8 alloc = Asset::getAssetAllocations(); - if (alloc.length() <= 0) { - return NULL; - } - - jstring str = env->NewStringUTF(alloc.string()); - return str; +static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->getLength()); } -static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz) -{ - return AssetManager::getGlobalCount(); +static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->getRemainingLength()); } // ---------------------------------------------------------------------------- -/* - * JNI registration. - */ +// JNI registration. static const JNINativeMethod gAssetManagerMethods[] = { - /* name, signature, funcPtr */ - - // Basic asset stuff. - { "openAsset", "(Ljava/lang/String;I)J", - (void*) android_content_AssetManager_openAsset }, - { "openAssetFd", "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*) android_content_AssetManager_openAssetFd }, - { "openNonAssetNative", "(ILjava/lang/String;I)J", - (void*) android_content_AssetManager_openNonAssetNative }, - { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*) android_content_AssetManager_openNonAssetFdNative }, - { "list", "(Ljava/lang/String;)[Ljava/lang/String;", - (void*) android_content_AssetManager_list }, - { "destroyAsset", "(J)V", - (void*) android_content_AssetManager_destroyAsset }, - { "readAssetChar", "(J)I", - (void*) android_content_AssetManager_readAssetChar }, - { "readAsset", "(J[BII)I", - (void*) android_content_AssetManager_readAsset }, - { "seekAsset", "(JJI)J", - (void*) android_content_AssetManager_seekAsset }, - { "getAssetLength", "(J)J", - (void*) android_content_AssetManager_getAssetLength }, - { "getAssetRemainingLength", "(J)J", - (void*) android_content_AssetManager_getAssetRemainingLength }, - { "addAssetPathNative", "(Ljava/lang/String;Z)I", - (void*) android_content_AssetManager_addAssetPath }, - { "addAssetFdNative", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)I", - (void*) android_content_AssetManager_addAssetFd }, - { "addOverlayPathNative", "(Ljava/lang/String;)I", - (void*) android_content_AssetManager_addOverlayPath }, - { "isUpToDate", "()Z", - (void*) android_content_AssetManager_isUpToDate }, - - // Resources. - { "getLocales", "()[Ljava/lang/String;", - (void*) android_content_AssetManager_getLocales }, - { "getNonSystemLocales", "()[Ljava/lang/String;", - (void*) android_content_AssetManager_getNonSystemLocales }, - { "getSizeConfigurations", "()[Landroid/content/res/Configuration;", - (void*) android_content_AssetManager_getSizeConfigurations }, - { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIIII)V", - (void*) android_content_AssetManager_setConfiguration }, - { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", - (void*) android_content_AssetManager_getResourceIdentifier }, - { "getResourceName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceName }, - { "getResourcePackageName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourcePackageName }, - { "getResourceTypeName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceTypeName }, - { "getResourceEntryName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceEntryName }, - { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadResourceValue }, - { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadResourceBagValue }, - { "getStringBlockCount","()I", - (void*) android_content_AssetManager_getStringBlockCount }, - { "getNativeStringBlock","(I)J", - (void*) android_content_AssetManager_getNativeStringBlock }, - { "getCookieName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getCookieName }, - { "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;", - (void*) android_content_AssetManager_getAssignedPackageIdentifiers }, - - // Themes. - { "newTheme", "()J", - (void*) android_content_AssetManager_newTheme }, - { "deleteTheme", "(J)V", - (void*) android_content_AssetManager_deleteTheme }, - { "applyThemeStyle", "(JIZ)V", - (void*) android_content_AssetManager_applyThemeStyle }, - { "copyTheme", "(JJ)V", - (void*) android_content_AssetManager_copyTheme }, - { "clearTheme", "(J)V", - (void*) android_content_AssetManager_clearTheme }, - { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadThemeAttributeValue }, - { "getThemeChangingConfigurations", "(J)I", - (void*) android_content_AssetManager_getThemeChangingConfigurations }, - { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V", - (void*) android_content_AssetManager_dumpTheme }, - { "applyStyle","(JIIJ[IIJJ)V", - (void*) android_content_AssetManager_applyStyle }, - { "resolveAttrs","(JII[I[I[I[I)Z", - (void*) android_content_AssetManager_resolveAttrs }, - { "retrieveAttributes","(J[I[I[I)Z", - (void*) android_content_AssetManager_retrieveAttributes }, - { "getArraySize","(I)I", - (void*) android_content_AssetManager_getArraySize }, - { "retrieveArray","(I[I)I", - (void*) android_content_AssetManager_retrieveArray }, - - // XML files. - { "openXmlAssetNative", "(ILjava/lang/String;)J", - (void*) android_content_AssetManager_openXmlAssetNative }, - - // Arrays. - { "getArrayStringResource","(I)[Ljava/lang/String;", - (void*) android_content_AssetManager_getArrayStringResource }, - { "getArrayStringInfo","(I)[I", - (void*) android_content_AssetManager_getArrayStringInfo }, - { "getArrayIntResource","(I)[I", - (void*) android_content_AssetManager_getArrayIntResource }, - { "getStyleAttributes","(I)[I", - (void*) android_content_AssetManager_getStyleAttributes }, - - // Bookkeeping. - { "init", "(Z)V", - (void*) android_content_AssetManager_init }, - { "destroy", "()V", - (void*) android_content_AssetManager_destroy }, - { "getGlobalAssetCount", "()I", - (void*) android_content_AssetManager_getGlobalAssetCount }, - { "getAssetAllocations", "()Ljava/lang/String;", - (void*) android_content_AssetManager_getAssetAllocations }, - { "getGlobalAssetManagerCount", "()I", - (void*) android_content_AssetManager_getGlobalAssetManagerCount }, + // AssetManager setup methods. + {"nativeCreate", "()J", (void*)NativeCreate}, + {"nativeDestroy", "(J)V", (void*)NativeDestroy}, + {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets}, + {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V", + (void*)NativeSetConfiguration}, + {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;", + (void*)NativeGetAssignedPackageIdentifiers}, + + // AssetManager file methods. + {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList}, + {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset}, + {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*)NativeOpenAssetFd}, + {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset}, + {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*)NativeOpenNonAssetFd}, + {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset}, + + // AssetManager resource methods. + {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue}, + {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I", + (void*)NativeGetResourceBagValue}, + {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes}, + {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;", + (void*)NativeGetResourceStringArray}, + {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo}, + {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray}, + {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize}, + {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray}, + + // AssetManager resource name/ID methods. + {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + (void*)NativeGetResourceIdentifier}, + {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName}, + {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName}, + {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName}, + {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName}, + {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales}, + {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;", + (void*)NativeGetSizeConfigurations}, + + // Style attribute related methods. + {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle}, + {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs}, + {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes}, + + // Theme related methods. + {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate}, + {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy}, + {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle}, + {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy}, + {"nativeThemeClear", "(J)V", (void*)NativeThemeClear}, + {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I", + (void*)NativeThemeGetAttributeValue}, + {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump}, + {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations}, + + // AssetInputStream methods. + {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy}, + {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar}, + {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead}, + {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek}, + {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength}, + {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength}, + + // System/idmap related methods. + {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps}, + + // Global management/debug methods. + {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount}, + {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations}, + {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount}, }; -int register_android_content_AssetManager(JNIEnv* env) -{ - jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); - gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); - gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); - gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string", - "Ljava/lang/CharSequence;"); - gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); - gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); - gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue, - "changingConfigurations", "I"); - gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); - - jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); - gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd", - "Landroid/os/ParcelFileDescriptor;"); - gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); - gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); - - jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); - gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); - - jclass stringClass = FindClassOrDie(env, "java/lang/String"); - g_stringClass = MakeGlobalRefOrDie(env, stringClass); - - jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); - gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); - gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, - "", "()V"); - gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", - "(ILjava/lang/Object;)V"); - - jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); - gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); - gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, - "", "()V"); - gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, - "smallestScreenWidthDp", "I"); - gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, - "screenWidthDp", "I"); - gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass, - "screenHeightDp", "I"); - - return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, - NELEM(gAssetManagerMethods)); +int register_android_content_AssetManager(JNIEnv* env) { + jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets"); + gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J"); + + jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); + gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); + gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); + gTypedValueOffsets.mString = + GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;"); + gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); + gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); + gTypedValueOffsets.mChangingConfigurations = + GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I"); + gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); + + jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); + gAssetFileDescriptorOffsets.mFd = + GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;"); + gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); + gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); + + jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); + gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); + + jclass stringClass = FindClassOrDie(env, "java/lang/String"); + g_stringClass = MakeGlobalRefOrDie(env, stringClass); + + jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); + gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); + gSparseArrayOffsets.constructor = + GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "", "()V"); + gSparseArrayOffsets.put = + GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V"); + + jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); + gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); + gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "", "()V"); + gConfigurationOffsets.mSmallestScreenWidthDpOffset = + GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I"); + gConfigurationOffsets.mScreenWidthDpOffset = + GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I"); + gConfigurationOffsets.mScreenHeightDpOffset = + GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I"); + + return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, + NELEM(gAssetManagerMethods)); } }; // namespace android diff --git a/core/jni/include/android_runtime/android_util_AssetManager.h b/core/jni/include/android_runtime/android_util_AssetManager.h index 8dd933707a6a..2c1e3579eb92 100644 --- a/core/jni/include/android_runtime/android_util_AssetManager.h +++ b/core/jni/include/android_runtime/android_util_AssetManager.h @@ -14,17 +14,20 @@ * limitations under the License. */ -#ifndef android_util_AssetManager_H -#define android_util_AssetManager_H +#ifndef ANDROID_RUNTIME_ASSETMANAGER_H +#define ANDROID_RUNTIME_ASSETMANAGER_H -#include +#include "androidfw/AssetManager2.h" +#include "androidfw/MutexGuard.h" #include "jni.h" namespace android { -extern AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject assetMgr); +extern AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); +extern Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); +extern Guarded* AssetManagerForNdkAssetManager(AAssetManager* assetmanager); -} +} // namespace android -#endif +#endif // ANDROID_RUNTIME_ASSETMANAGER_H diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 415d3e36adf9..2fc8e952707b 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -265,8 +265,6 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, FindEntryResult* out_entry) { - ATRACE_CALL(); - // Might use this if density_override != 0. ResTable_config density_override_config; @@ -429,9 +427,7 @@ ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_valu for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && in_out_value->data != 0u && iteration < kMaxIterations; iteration++) { - if (out_last_reference != nullptr) { - *out_last_reference = in_out_value->data; - } + *out_last_reference = in_out_value->data; uint32_t new_flags = 0u; cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/, in_out_value, in_out_selected_config, &new_flags); diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index 60e3845d98a9..f912af4f7190 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -20,13 +20,18 @@ #include +#include "androidfw/AssetManager2.h" #include "androidfw/AttributeFinder.h" -#include "androidfw/ResourceTypes.h" constexpr bool kDebugStyles = false; namespace android { +// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. +static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { + return cookie != kInvalidCookie ? static_cast(cookie + 1) : static_cast(-1); +} + class XmlAttributeFinder : public BackTrackingAttributeFinder { public: @@ -44,58 +49,53 @@ class XmlAttributeFinder }; class BagAttributeFinder - : public BackTrackingAttributeFinder { + : public BackTrackingAttributeFinder { public: - BagAttributeFinder(const ResTable::bag_entry* start, - const ResTable::bag_entry* end) - : BackTrackingAttributeFinder(start, end) {} + BagAttributeFinder(const ResolvedBag* bag) + : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr, + bag != nullptr ? bag->entries + bag->entry_count : nullptr) { + } - inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const { - return entry->map.name.ident; + inline uint32_t GetAttribute(const ResolvedBag::Entry* entry) const { + return entry->key; } }; -bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, - uint32_t def_style_res, uint32_t* src_values, - size_t src_values_length, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, - uint32_t* out_indices) { +bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, + uint32_t* src_values, size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr, def_style_res); } - const ResTable& res = theme->getResTable(); + AssetManager2* assetmanager = theme->GetAssetManager(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_bag_type_set_flags = 0; + uint32_t def_style_flags = 0u; if (def_style_attr != 0) { Res_value value; - if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) { + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { if (value.dataType == Res_value::TYPE_REFERENCE) { def_style_res = value.data; } } } - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - // Retrieve the default style bag, if requested. - const ResTable::bag_entry* def_style_start = nullptr; - uint32_t def_style_type_set_flags = 0; - ssize_t bag_off = def_style_res != 0 - ? res.getBagLocked(def_style_res, &def_style_start, - &def_style_type_set_flags) - : -1; - def_style_type_set_flags |= def_style_bag_type_set_flags; - const ResTable::bag_entry* const def_style_end = - def_style_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end); + const ResolvedBag* default_style_bag = nullptr; + if (def_style_res != 0) { + default_style_bag = assetmanager->GetBag(def_style_res); + if (default_style_bag != nullptr) { + def_style_flags |= default_style_bag->type_spec_flags; + } + } + + BagAttributeFinder def_style_attr_finder(default_style_bag); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -106,7 +106,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ssize_t block = -1; + ApkAssetsCookie cookie = kInvalidCookie; uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; @@ -122,15 +122,14 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, value.dataType = Res_value::TYPE_ATTRIBUTE; value.data = src_values[ii]; if (kDebugStyles) { - ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, - value.data); + ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data); } } else { - const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident); - if (def_style_entry != def_style_end) { - block = def_style_entry->stringBlock; - type_set_flags = def_style_type_set_flags; - value = def_style_entry->map.value; + const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident); + if (entry != def_style_attr_finder.end()) { + cookie = entry->cookie; + type_set_flags = def_style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -140,22 +139,26 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ssize_t new_block = - theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); - if (new_block >= 0) block = new_block; + ApkAssetsCookie new_cookie = + theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } if (kDebugStyles) { ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data); } } else if (value.data != Res_value::DATA_NULL_EMPTY) { - // If we still don't have a value for this attribute, try to find - // it in the theme! - ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); - if (new_block >= 0) { + // If we still don't have a value for this attribute, try to find it in the theme! + ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); + if (new_cookie != kInvalidCookie) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); - if (new_block >= 0) block = new_block; + new_cookie = + assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } if (kDebugStyles) { ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -169,7 +172,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = -1; + cookie = kInvalidCookie; } if (kDebugStyles) { @@ -179,9 +182,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != -1 ? static_cast(res.getTableCookie(block)) - : static_cast(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -195,90 +196,80 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, out_values += STYLE_NUM_ENTRIES; } - res.unlock(); - if (out_indices != nullptr) { out_indices[0] = indices_idx; } return true; } -void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { - ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", - theme, def_style_attr, def_style_res, xml_parser); + ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, + def_style_attr, def_style_resid, xml_parser); } - const ResTable& res = theme->getResTable(); + AssetManager2* assetmanager = theme->GetAssetManager(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_bag_type_set_flags = 0; + uint32_t def_style_flags = 0u; if (def_style_attr != 0) { Res_value value; - if (theme->getAttribute(def_style_attr, &value, - &def_style_bag_type_set_flags) >= 0) { + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { if (value.dataType == Res_value::TYPE_REFERENCE) { - def_style_res = value.data; + def_style_resid = value.data; } } } - // Retrieve the style class associated with the current XML tag. - int style = 0; - uint32_t style_bag_type_set_flags = 0; + // Retrieve the style resource ID associated with the current XML tag's style attribute. + uint32_t style_resid = 0u; + uint32_t style_flags = 0u; if (xml_parser != nullptr) { ssize_t idx = xml_parser->indexOfStyle(); if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) { if (value.dataType == value.TYPE_ATTRIBUTE) { - if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) { + // Resolve the attribute with out theme. + if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) { value.dataType = Res_value::TYPE_NULL; } } + if (value.dataType == value.TYPE_REFERENCE) { - style = value.data; + style_resid = value.data; } } } - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - // Retrieve the default style bag, if requested. - const ResTable::bag_entry* def_style_attr_start = nullptr; - uint32_t def_style_type_set_flags = 0; - ssize_t bag_off = def_style_res != 0 - ? res.getBagLocked(def_style_res, &def_style_attr_start, - &def_style_type_set_flags) - : -1; - def_style_type_set_flags |= def_style_bag_type_set_flags; - const ResTable::bag_entry* const def_style_attr_end = - def_style_attr_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder def_style_attr_finder(def_style_attr_start, - def_style_attr_end); + const ResolvedBag* default_style_bag = nullptr; + if (def_style_resid != 0) { + default_style_bag = assetmanager->GetBag(def_style_resid); + if (default_style_bag != nullptr) { + def_style_flags |= default_style_bag->type_spec_flags; + } + } + + BagAttributeFinder def_style_attr_finder(default_style_bag); // Retrieve the style class bag, if requested. - const ResTable::bag_entry* style_attr_start = nullptr; - uint32_t style_type_set_flags = 0; - bag_off = - style != 0 - ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags) - : -1; - style_type_set_flags |= style_bag_type_set_flags; - const ResTable::bag_entry* const style_attr_end = - style_attr_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end); + const ResolvedBag* xml_style_bag = nullptr; + if (style_resid != 0) { + xml_style_bag = assetmanager->GetBag(style_resid); + if (xml_style_bag != nullptr) { + style_flags |= xml_style_bag->type_spec_flags; + } + } + + BagAttributeFinder xml_style_attr_finder(xml_style_bag); // Retrieve the XML attributes, if requested. - static const ssize_t kXmlBlock = 0x10000000; XmlAttributeFinder xml_attr_finder(xml_parser); - const size_t xml_attr_end = - xml_parser != nullptr ? xml_parser->getAttributeCount() : 0; // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -289,8 +280,8 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ssize_t block = kXmlBlock; - uint32_t type_set_flags = 0; + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0u; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -302,7 +293,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s // Walk through the xml attributes looking for the requested attribute. const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); - if (xml_attr_idx != xml_attr_end) { + if (xml_attr_idx != xml_attr_finder.end()) { // We found the attribute we were looking for. xml_parser->getAttributeValue(xml_attr_idx, &value); if (kDebugStyles) { @@ -312,12 +303,12 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the style class values looking for the requested attribute. - const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident); - if (style_attr_entry != style_attr_end) { + const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident); + if (entry != xml_style_attr_finder.end()) { // We found the attribute we were looking for. - block = style_attr_entry->stringBlock; - type_set_flags = style_type_set_flags; - value = style_attr_entry->map.value; + cookie = entry->cookie; + type_set_flags = style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -326,25 +317,25 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the default style values looking for the requested attribute. - const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident); - if (def_style_attr_entry != def_style_attr_end) { + const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident); + if (entry != def_style_attr_finder.end()) { // We found the attribute we were looking for. - block = def_style_attr_entry->stringBlock; - type_set_flags = style_type_set_flags; - value = def_style_attr_entry->map.value; + cookie = entry->cookie; + type_set_flags = def_style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } } } - uint32_t resid = 0; + uint32_t resid = 0u; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ssize_t new_block = - theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); - if (new_block >= 0) { - block = new_block; + ApkAssetsCookie new_cookie = + theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; } if (kDebugStyles) { @@ -352,14 +343,15 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s } } else if (value.data != Res_value::DATA_NULL_EMPTY) { // If we still don't have a value for this attribute, try to find it in the theme! - ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); - if (new_block >= 0) { + ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); + if (new_cookie != kInvalidCookie) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); - if (new_block >= 0) { - block = new_block; + new_cookie = + assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; } if (kDebugStyles) { @@ -375,7 +367,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = kXmlBlock; + cookie = kInvalidCookie; } if (kDebugStyles) { @@ -385,9 +377,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != kXmlBlock ? static_cast(res.getTableCookie(block)) - : static_cast(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -402,36 +392,28 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s out_values += STYLE_NUM_ENTRIES; } - res.unlock(); - // out_indices must NOT be nullptr. out_indices[0] = indices_idx; } -bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, - uint32_t* attrs, size_t attrs_length, - uint32_t* out_values, uint32_t* out_indices) { +bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { ResTable_config config; Res_value value; int indices_idx = 0; - // Now lock down the resource object and start pulling stuff from it. - res->lock(); - // Retrieve the XML attributes, if requested. const size_t xml_attr_count = xml_parser->getAttributeCount(); size_t ix = 0; uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix); - static const ssize_t kXmlBlock = 0x10000000; - // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - ssize_t block = kXmlBlock; - uint32_t type_set_flags = 0; + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0u; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -450,28 +432,27 @@ bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, cur_xml_attr = xml_parser->getAttributeNameResID(ix); } - uint32_t resid = 0; + uint32_t resid = 0u; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - // printf("Resolving attribute reference\n"); - ssize_t new_block = res->resolveReference(&value, block, &resid, - &type_set_flags, &config); - if (new_block >= 0) block = new_block; + ApkAssetsCookie new_cookie = + assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } } // Deal with the special @null value -- it turns back to TYPE_NULL. if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = kXmlBlock; + cookie = kInvalidCookie; } // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != kXmlBlock ? static_cast(res->getTableCookie(block)) - : static_cast(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -485,8 +466,6 @@ bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, out_values += STYLE_NUM_ENTRIES; } - res->unlock(); - if (out_indices != nullptr) { out_indices[0] = indices_idx; } diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 28548e27baf0..e08848f891f6 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -324,8 +324,6 @@ bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_i bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, FindEntryResult* out_entry) const { - ATRACE_CALL(); - // If the type IDs are offset in this package, we need to take that into account when searching // for a type. const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h index f281921824e7..03fad4947dfe 100644 --- a/libs/androidfw/include/androidfw/AttributeFinder.h +++ b/libs/androidfw/include/androidfw/AttributeFinder.h @@ -58,6 +58,7 @@ class BackTrackingAttributeFinder { BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end); Iterator Find(uint32_t attr); + inline Iterator end(); private: void JumpToClosestAttribute(uint32_t package_id); @@ -201,6 +202,11 @@ Iterator BackTrackingAttributeFinder::Find(uint32_t attr) { return end_; } +template +Iterator BackTrackingAttributeFinder::end() { + return end_; +} + } // namespace android #endif // ANDROIDFW_ATTRIBUTE_FINDER_H diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h index 69b760414846..35ef98d8c704 100644 --- a/libs/androidfw/include/androidfw/AttributeResolution.h +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -17,7 +17,8 @@ #ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H #define ANDROIDFW_ATTRIBUTERESOLUTION_H -#include +#include "androidfw/AssetManager2.h" +#include "androidfw/ResourceTypes.h" namespace android { @@ -42,19 +43,19 @@ enum { // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, +bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid, uint32_t* src_values, size_t src_values_length, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` is NOT optional and must NOT be nullptr. -void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs, +bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); } // namespace android diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 965e2dbd2fb2..1775f5070f4e 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -45,16 +45,17 @@ struct FindEntryResult { // A pointer to the resource table entry for this resource. // If the size of the entry is > sizeof(ResTable_entry), it can be cast to // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry = nullptr; + const ResTable_entry* entry; - // The configuration for which the resulting entry was defined. - const ResTable_config* config = nullptr; + // The configuration for which the resulting entry was defined. This points to a structure that + // is already swapped to host endianness. + const ResTable_config* config; - // Stores the resulting bitmask of configuration axis with which the resource value varies. - uint32_t type_flags = 0u; + // The bitmask of configuration axis with which the resource value varies. + uint32_t type_flags; // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table = nullptr; + const DynamicRefTable* dynamic_ref_table; // The string pool reference to the type's name. This uses a different string pool than // the global string pool, but this is hidden from the caller. diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h new file mode 100644 index 000000000000..64924f433245 --- /dev/null +++ b/libs/androidfw/include/androidfw/MutexGuard.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROIDFW_MUTEXGUARD_H +#define ANDROIDFW_MUTEXGUARD_H + +#include +#include + +#include "android-base/macros.h" + +namespace android { + +template +class ScopedLock; + +// Owns the guarded object and protects access to it via a mutex. +// The guarded object is inaccessible via this class. +// The mutex is locked and the object accessed via the ScopedLock class. +// +// NOTE: The template parameter T should not be a raw pointer, since ownership +// is ambiguous and error-prone. Instead use an std::unique_ptr<>. +// +// Example use: +// +// Guarded shared_string("hello"); +// { +// ScopedLock locked_string(shared_string); +// *locked_string += " world"; +// } +// +template +class Guarded { + static_assert(!std::is_pointer::value, "T must not be a raw pointer"); + + public: + explicit Guarded() : guarded_() { + } + + template + explicit Guarded(const T& guarded, + typename std::enable_if::value>::type = void()) + : guarded_(guarded) { + } + + template + explicit Guarded(T&& guarded, + typename std::enable_if::value>::type = void()) + : guarded_(std::move(guarded)) { + } + + private: + friend class ScopedLock; + + DISALLOW_COPY_AND_ASSIGN(Guarded); + + std::mutex lock_; + T guarded_; +}; + +template +class ScopedLock { + public: + explicit ScopedLock(Guarded& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) { + } + + T& operator*() { + return guarded_; + } + + T* operator->() { + return &guarded_; + } + + T* get() { + return &guarded_; + } + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedLock); + + std::lock_guard lock_; + T& guarded_; +}; + +} // namespace android + +#endif // ANDROIDFW_MUTEXGUARD_H diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index 2d73ce8f8ee3..cc3053798e7b 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -21,6 +21,7 @@ #include "android-base/file.h" #include "android-base/logging.h" #include "android-base/macros.h" +#include "androidfw/AssetManager2.h" #include "TestHelpers.h" #include "data/styles/R.h" @@ -32,15 +33,14 @@ namespace android { class AttributeResolutionTest : public ::testing::Test { public: virtual void SetUp() override { - std::string contents; - ASSERT_TRUE(ReadFileFromZipToString( - GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, table_.add(contents.data(), contents.size(), - 1 /*cookie*/, true /*copyData*/)); + styles_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + ASSERT_NE(nullptr, styles_assets_); + assetmanager_.SetApkAssets({styles_assets_.get()}); } protected: - ResTable table_; + std::unique_ptr styles_assets_; + AssetManager2 assetmanager_; }; class AttributeResolutionXmlTest : public AttributeResolutionTest { @@ -48,13 +48,12 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { virtual void SetUp() override { AttributeResolutionTest::SetUp(); - std::string contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", - "res/layout/layout.xml", &contents)); + std::unique_ptr asset = + assetmanager_.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); + ASSERT_NE(nullptr, asset); - ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(), - true /*copyData*/)); + ASSERT_EQ(NO_ERROR, + xml_parser_.setTo(asset->getBuffer(true), asset->getLength(), true /*copyData*/)); // Skip to the first tag. while (xml_parser_.next() != ResXMLParser::START_TAG) { @@ -66,14 +65,14 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { }; TEST_F(AttributeResolutionTest, Theme) { - ResTable::Theme theme(table_); - ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); + std::unique_ptr theme = assetmanager_.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_empty}}; std::array values; - ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/, + ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/, nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(), attrs.size(), values.data(), nullptr /*out_indices*/)); @@ -126,8 +125,8 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { R::attr::attr_four, R::attr::attr_empty}}; std::array values; - ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(), - nullptr /*out_indices*/)); + ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(), + values.data(), nullptr /*out_indices*/)); uint32_t* values_cursor = values.data(); EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]); @@ -171,15 +170,15 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { } TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) { - ResTable::Theme theme(table_); - ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); + std::unique_ptr theme = assetmanager_.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}}; std::array values; std::array indices; - ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(), + ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), attrs.size(), values.data(), indices.data()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index 7149beef797f..a8abcb5df86c 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -33,12 +33,12 @@ void GetResourceBenchmarkOld(const std::vector& paths, const ResTab } } + // Make sure to force creation of the ResTable first, or else the configuration doesn't get set. + const ResTable& table = assetmanager.getResources(true); if (config != nullptr) { assetmanager.setConfiguration(*config); } - const ResTable& table = assetmanager.getResources(true); - Res_value value; ResTable_config selected_config; uint32_t flags; diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp index 98e9a42d944d..e70d5ea0d566 100644 --- a/native/android/asset_manager.cpp +++ b/native/android/asset_manager.cpp @@ -18,9 +18,11 @@ #include #include +#include #include #include #include +#include #include #include "jni.h" @@ -35,21 +37,20 @@ using namespace android; // ----- struct AAssetDir { - AssetDir* mAssetDir; + std::unique_ptr mAssetDir; size_t mCurFileIndex; String8 mCachedFileName; - explicit AAssetDir(AssetDir* dir) : mAssetDir(dir), mCurFileIndex(0) { } - ~AAssetDir() { delete mAssetDir; } + explicit AAssetDir(std::unique_ptr dir) : + mAssetDir(std::move(dir)), mCurFileIndex(0) { } }; // ----- struct AAsset { - Asset* mAsset; + std::unique_ptr mAsset; - explicit AAsset(Asset* asset) : mAsset(asset) { } - ~AAsset() { delete mAsset; } + explicit AAsset(std::unique_ptr asset) : mAsset(std::move(asset)) { } }; // -------------------- Public native C API -------------------- @@ -104,19 +105,18 @@ AAsset* AAssetManager_open(AAssetManager* amgr, const char* filename, int mode) return NULL; } - AssetManager* mgr = static_cast(amgr); - Asset* asset = mgr->open(filename, amMode); - if (asset == NULL) { - return NULL; + ScopedLock locked_mgr(*AssetManagerForNdkAssetManager(amgr)); + std::unique_ptr asset = locked_mgr->Open(filename, amMode); + if (asset == nullptr) { + return nullptr; } - - return new AAsset(asset); + return new AAsset(std::move(asset)); } AAssetDir* AAssetManager_openDir(AAssetManager* amgr, const char* dirName) { - AssetManager* mgr = static_cast(amgr); - return new AAssetDir(mgr->openDir(dirName)); + ScopedLock locked_mgr(*AssetManagerForNdkAssetManager(amgr)); + return new AAssetDir(locked_mgr->OpenDir(dirName)); } /** diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index b32be736533b..52d0e08e4e7f 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -24,8 +24,9 @@ #include #include +#include #include -#include +#include #include #include @@ -1664,18 +1665,22 @@ nFileA3DCreateFromAssetStream(JNIEnv *_env, jobject _this, jlong con, jlong nati static jlong nFileA3DCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path) { - AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); + Guarded* mgr = AssetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); - if (asset == nullptr) { - return 0; + std::unique_ptr asset; + { + ScopedLock locked_mgr(*mgr); + asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; + } } - jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset); + jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset.release()); return id; } @@ -1752,22 +1757,25 @@ static jlong nFontCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path, jfloat fontSize, jint dpi) { - AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); + Guarded* mgr = AssetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); - if (asset == nullptr) { - return 0; + std::unique_ptr asset; + { + ScopedLock locked_mgr(*mgr); + asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; + } } jlong id = (jlong)(uintptr_t)rsFontCreateFromMemory((RsContext)con, str.c_str(), str.length(), fontSize, dpi, asset->getBuffer(false), asset->getLength()); - delete asset; return id; } -- cgit v1.2.3-59-g8ed1b From 88c9959e5b417320bbc2484fab42ab4b12379533 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 8 Jan 2018 17:38:30 -0800 Subject: libandroidfw: Improve performance of AssetManager2 AssetManager2 relied on creating a list of configurations present in the resource table so as to avoid copying and converting ResTable_config's from the APK on every resource retrieval. ResTable, however, had a better optimization that pruned the configurations that didn't match the currently set configuration. This vastly reduced the number of ResTable_configs to test. In this CL, AssetManager2 follows suite with this optimization and only maintains the filtered ResTable_configs, falling back to the slow path when the configuration is overridden. Test: mma frameworks/base/libs/androidfw Test: adb sync system data Test: adb shell /data/benchmarktest64/libandroidfw_benchmarks/libandroidfw_benchmarks Change-Id: Ib57b75fbb32e1d310eec146e5a12dfc6de4385f9 --- libs/androidfw/Android.bp | 1 + libs/androidfw/AssetManager2.cpp | 276 +++++++++++++++++------ libs/androidfw/LoadedArsc.cpp | 261 +++++++-------------- libs/androidfw/include/androidfw/AssetManager2.h | 66 ++++-- libs/androidfw/include/androidfw/LoadedArsc.h | 111 +++++---- libs/androidfw/tests/ApkAssets_test.cpp | 78 +++---- libs/androidfw/tests/LoadedArsc_test.cpp | 169 +++++++------- libs/androidfw/tests/TestHelpers.h | 1 + 8 files changed, 539 insertions(+), 424 deletions(-) (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 7c9078b164a2..70d52164ff74 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -145,6 +145,7 @@ cc_test { "tests/TypeWrappers_test.cpp", "tests/ZipUtils_test.cpp", ], + static_libs: ["libgmock"], target: { android: { srcs: [ diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 20dba37fbe66..a8c916bbaea6 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -36,6 +36,31 @@ namespace android { +struct FindEntryResult { + // A pointer to the resource table entry for this resource. + // If the size of the entry is > sizeof(ResTable_entry), it can be cast to + // a ResTable_map_entry and processed as a bag/map. + const ResTable_entry* entry; + + // The configuration for which the resulting entry was defined. This is already swapped to host + // endianness. + ResTable_config config; + + // The bitmask of configuration axis with which the resource value varies. + uint32_t type_flags; + + // The dynamic package ID map for the package from which this resource came from. + const DynamicRefTable* dynamic_ref_table; + + // The string pool reference to the type's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef type_string_ref; + + // The string pool reference to the entry's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef entry_string_ref; +}; + AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } @@ -44,6 +69,7 @@ bool AssetManager2::SetApkAssets(const std::vector& apk_assets bool invalidate_caches) { apk_assets_ = apk_assets; BuildDynamicRefTable(); + RebuildFilterList(); if (invalidate_caches) { InvalidateCaches(static_cast(-1)); } @@ -81,7 +107,7 @@ void AssetManager2::BuildDynamicRefTable() { PackageGroup* package_group = &package_groups_[idx]; // Add the package and to the set of packages with the same ID. - package_group->packages_.push_back(package.get()); + package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); package_group->cookies_.push_back(static_cast(i)); // Add the package name -> build time ID mappings. @@ -96,7 +122,7 @@ void AssetManager2::BuildDynamicRefTable() { // Now assign the runtime IDs so that we have a build-time to runtime ID map. const auto package_groups_end = package_groups_.end(); for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) { - const std::string& package_name = iter->packages_[0]->GetPackageName(); + const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName(); for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) { iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()), iter->dynamic_ref_table.mAssignedPackageId); @@ -118,20 +144,22 @@ void AssetManager2::DumpToLog() const { list = ""; for (size_t i = 0; i < package_ids_.size(); i++) { if (package_ids_[i] != 0xff) { - base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]); + base::StringAppendF(&list, "%02x -> %d, ", (int)i, package_ids_[i]); } } LOG(INFO) << "Package ID map: " << list; for (const auto& package_group: package_groups_) { - list = ""; - for (const auto& package : package_group.packages_) { - base::StringAppendF(&list, "%s(%02x%s), ", package->GetPackageName().c_str(), - package->GetPackageId(), (package->IsDynamic() ? " dynamic" : "")); - } - LOG(INFO) << base::StringPrintf("PG (%02x): ", - package_group.dynamic_ref_table.mAssignedPackageId) - << list; + list = ""; + for (const auto& package : package_group.packages_) { + const LoadedPackage* loaded_package = package.loaded_package_; + base::StringAppendF(&list, "%s(%02x%s), ", loaded_package->GetPackageName().c_str(), + loaded_package->GetPackageId(), + (loaded_package->IsDynamic() ? " dynamic" : "")); + } + LOG(INFO) << base::StringPrintf("PG (%02x): ", + package_group.dynamic_ref_table.mAssignedPackageId) + << list; } } @@ -170,52 +198,54 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { configuration_ = configuration; if (diff) { + RebuildFilterList(); InvalidateCaches(static_cast(diff)); } } std::set AssetManager2::GetResourceConfigurations(bool exclude_system, - bool exclude_mipmap) { + bool exclude_mipmap) const { ATRACE_CALL(); std::set configurations; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { - if (exclude_system && package->IsSystem()) { + for (const ConfiguredPackage& package : package_group.packages_) { + if (exclude_system && package.loaded_package_->IsSystem()) { continue; } - package->CollectConfigurations(exclude_mipmap, &configurations); + package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations); } } return configurations; } std::set AssetManager2::GetResourceLocales(bool exclude_system, - bool merge_equivalent_languages) { + bool merge_equivalent_languages) const { ATRACE_CALL(); std::set locales; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { - if (exclude_system && package->IsSystem()) { + for (const ConfiguredPackage& package : package_group.packages_) { + if (exclude_system && package.loaded_package_->IsSystem()) { continue; } - package->CollectLocales(merge_equivalent_languages, &locales); + package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales); } } return locales; } -std::unique_ptr AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) { +std::unique_ptr AssetManager2::Open(const std::string& filename, + Asset::AccessMode mode) const { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, mode); } std::unique_ptr AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) { + Asset::AccessMode mode) const { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, cookie, mode); } -std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) { +std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) const { ATRACE_CALL(); std::string full_path = "assets/" + dirname; @@ -249,7 +279,7 @@ std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) { // is inconsistent for split APKs. std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie) { + ApkAssetsCookie* out_cookie) const { ATRACE_CALL(); for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) { std::unique_ptr asset = apk_assets_[i]->Open(filename, mode); @@ -268,7 +298,8 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, - ApkAssetsCookie cookie, Asset::AccessMode mode) { + ApkAssetsCookie cookie, + Asset::AccessMode mode) const { ATRACE_CALL(); if (cookie < 0 || static_cast(cookie) >= apk_assets_.size()) { return {}; @@ -277,12 +308,13 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, - bool stop_at_first_match, FindEntryResult* out_entry) { + bool /*stop_at_first_match*/, + FindEntryResult* out_entry) const { // Might use this if density_override != 0. ResTable_config density_override_config; // Select our configuration or generate a density override configuration. - ResTable_config* desired_config = &configuration_; + const ResTable_config* desired_config = &configuration_; if (density_override != 0 && density_override != configuration_.density) { density_override_config = configuration_; density_override_config.density = density_override; @@ -296,53 +328,135 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri const uint32_t package_id = get_package_id(resid); const uint8_t type_idx = get_type_id(resid) - 1; - const uint16_t entry_id = get_entry_id(resid); + const uint16_t entry_idx = get_entry_id(resid); - const uint8_t idx = package_ids_[package_id]; - if (idx == 0xff) { + const uint8_t package_idx = package_ids_[package_id]; + if (package_idx == 0xff) { LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); return kInvalidCookie; } - FindEntryResult best_entry; - ApkAssetsCookie best_cookie = kInvalidCookie; - uint32_t cumulated_flags = 0u; - - const PackageGroup& package_group = package_groups_[idx]; + const PackageGroup& package_group = package_groups_[package_idx]; const size_t package_count = package_group.packages_.size(); - FindEntryResult current_entry; - for (size_t i = 0; i < package_count; i++) { - const LoadedPackage* loaded_package = package_group.packages_[i]; - if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry)) { + + ApkAssetsCookie best_cookie = kInvalidCookie; + const LoadedPackage* best_package = nullptr; + const ResTable_type* best_type = nullptr; + const ResTable_config* best_config = nullptr; + ResTable_config best_config_copy; + uint32_t best_offset = 0u; + uint32_t type_flags = 0u; + + // If desired_config is the same as the set configuration, then we can use our filtered list + // and we don't need to match the configurations, since they already matched. + const bool use_fast_path = desired_config == &configuration_; + + for (size_t pi = 0; pi < package_count; pi++) { + const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; + const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_; + ApkAssetsCookie cookie = package_group.cookies_[pi]; + + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx); + if (UNLIKELY(type_spec == nullptr)) { continue; } - cumulated_flags |= current_entry.type_flags; + uint16_t local_entry_idx = entry_idx; - const ResTable_config* current_config = current_entry.config; - const ResTable_config* best_config = best_entry.config; - if (best_cookie == kInvalidCookie || - current_config->isBetterThan(*best_config, desired_config) || - (loaded_package->IsOverlay() && current_config->compare(*best_config) == 0)) { - best_entry = current_entry; - best_cookie = package_group.cookies_[i]; - if (stop_at_first_match) { - break; + // If there is an IDMAP supplied with this package, translate the entry ID. + if (type_spec->idmap_entries != nullptr) { + if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) { + // There is no mapping, so the resource is not meant to be in this overlay package. + continue; + } + } + + type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx); + + // If the package is an overlay, then even configurations that are the same MUST be chosen. + const bool package_is_overlay = loaded_package->IsOverlay(); + + const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; + if (use_fast_path) { + const std::vector& candidate_configs = filtered_group.configurations; + const size_t type_count = candidate_configs.size(); + for (uint32_t i = 0; i < type_count; i++) { + const ResTable_config& this_config = candidate_configs[i]; + + // We can skip calling ResTable_config::match() because we know that all candidate + // configurations that do NOT match have been filtered-out. + if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || + (package_is_overlay && this_config.compare(*best_config) == 0)) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const ResTable_type* type_chunk = filtered_group.types[i]; + const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + best_cookie = cookie; + best_package = loaded_package; + best_type = type_chunk; + best_config = &this_config; + best_offset = offset; + } + } + } else { + // This is the slower path, which doesn't use the filtered list of configurations. + // Here we must read the ResTable_config from the mmapped APK, convert it to host endianness + // and fill in any new fields that did not exist when the APK was compiled. + // Furthermore when selecting configurations we can't just record the pointer to the + // ResTable_config, we must copy it. + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config this_config; + this_config.copyFromDtoH((*iter)->config); + + if (this_config.match(*desired_config)) { + if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || + (package_is_overlay && this_config.compare(*best_config) == 0)) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + best_cookie = cookie; + best_package = loaded_package; + best_type = *iter; + best_config_copy = this_config; + best_config = &best_config_copy; + best_offset = offset; + } + } } } } - if (best_cookie == kInvalidCookie) { + if (UNLIKELY(best_cookie == kInvalidCookie)) { return kInvalidCookie; } - *out_entry = best_entry; + const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset); + if (UNLIKELY(best_entry == nullptr)) { + return kInvalidCookie; + } + + out_entry->entry = best_entry; + out_entry->config = *best_config; + out_entry->type_flags = type_flags; + out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); + out_entry->entry_string_ref = + StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; - out_entry->type_flags = cumulated_flags; return best_cookie; } -bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { +bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const { ATRACE_CALL(); FindEntryResult entry; @@ -352,7 +466,8 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { return false; } - const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid); + const LoadedPackage* package = + apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); if (package == nullptr) { return false; } @@ -380,7 +495,7 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { return true; } -bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { +bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const { FindEntryResult entry; ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); @@ -394,7 +509,7 @@ bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) { + uint32_t* out_flags) const { ATRACE_CALL(); FindEntryResult entry; @@ -413,7 +528,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Create a reference since we can't represent this complex type as a Res_value. out_value->dataType = Res_value::TYPE_REFERENCE; out_value->data = resid; - *out_selected_config = *entry.config; + *out_selected_config = entry.config; *out_flags = entry.type_flags; return cookie; } @@ -425,7 +540,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Convert the package ID to the runtime assigned package ID. entry.dynamic_ref_table->lookupResourceValue(out_value); - *out_selected_config = *entry.config; + *out_selected_config = entry.config; *out_flags = entry.type_flags; return cookie; } @@ -433,7 +548,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) { + uint32_t* out_last_reference) const { ATRACE_CALL(); constexpr const int kMaxIterations = 20; @@ -501,7 +616,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { // Attributes, arrays, etc don't have a resource id as the name. They specify // other data, which would be wrong to change via a lookup. if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, + resid); return nullptr; } } @@ -533,7 +649,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { const ResolvedBag* parent_bag = GetBag(parent_resid); if (parent_bag == nullptr) { // Failed to get the parent that should exist. - LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid); + LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, + resid); return nullptr; } @@ -552,7 +669,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t child_key = dtohl(map_entry->name.ident); if (!is_internal_resid(child_key)) { if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, + resid); return nullptr; } } @@ -591,7 +709,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t new_key = dtohl(map_entry->name.ident); if (!is_internal_resid(new_key)) { if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, + resid); return nullptr; } } @@ -647,7 +766,7 @@ static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) { uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const std::string& fallback_type, - const std::string& fallback_package) { + const std::string& fallback_package) const { StringPiece package_name, type, entry; if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) { return 0u; @@ -679,7 +798,8 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const static std::u16string kAttrPrivate16 = u"^attr-private"; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { + for (const ConfiguredPackage& package_impl : package_group.packages_) { + const LoadedPackage* package = package_impl.loaded_package_; if (package_name != package->GetPackageName()) { // All packages in the same group are expected to have the same package name. break; @@ -701,6 +821,32 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, return 0u; } +void AssetManager2::RebuildFilterList() { + for (PackageGroup& group : package_groups_) { + for (ConfiguredPackage& impl : group.packages_) { + // Destroy it. + impl.filtered_configs_.~ByteBucketArray(); + + // Re-create it. + new (&impl.filtered_configs_) ByteBucketArray(); + + // Create the filters here. + impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec* spec, uint8_t type_index) { + FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_index); + const auto iter_end = spec->types + spec->type_count; + for (auto iter = spec->types; iter != iter_end; ++iter) { + ResTable_config this_config; + this_config.copyFromDtoH((*iter)->config); + if (this_config.match(configuration_)) { + group.configurations.push_back(this_config); + group.types.push_back(*iter); + } + } + }); + } + } +} + void AssetManager2::InvalidateCaches(uint32_t diff) { if (diff == 0xffffffffu) { // Everything must go. @@ -881,7 +1027,7 @@ ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_type_spec_flags, - uint32_t* out_last_ref) { + uint32_t* out_last_ref) const { if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) { uint32_t new_flags; cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags); diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index e08848f891f6..1d2c597c4c8c 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -44,44 +44,6 @@ namespace android { constexpr const static int kAppPackageId = 0x7f; -// Element of a TypeSpec array. See TypeSpec. -struct Type { - // The configuration for which this type defines entries. - // This is already converted to host endianness. - ResTable_config configuration; - - // Pointer to the mmapped data where entry definitions are kept. - const ResTable_type* type; -}; - -// TypeSpec is going to be immediately proceeded by -// an array of Type structs, all in the same block of memory. -struct TypeSpec { - // Pointer to the mmapped data where flags are kept. - // Flags denote whether the resource entry is public - // and under which configurations it varies. - const ResTable_typeSpec* type_spec; - - // Pointer to the mmapped data where the IDMAP mappings for this type - // exist. May be nullptr if no IDMAP exists. - const IdmapEntry_header* idmap_entries; - - // The number of types that follow this struct. - // There is a type for each configuration - // that entries are defined for. - size_t type_count; - - // Trick to easily access a variable number of Type structs - // proceeding this struct, and to ensure their alignment. - const Type types[0]; -}; - -// TypeSpecPtr points to the block of memory that holds -// a TypeSpec struct, followed by an array of Type structs. -// TypeSpecPtr is a managed pointer that knows how to delete -// itself. -using TypeSpecPtr = util::unique_cptr; - namespace { // Builder that helps accumulate Type structs and then create a single @@ -95,21 +57,22 @@ class TypeSpecPtrBuilder { } void AddType(const ResTable_type* type) { - ResTable_config config; - config.copyFromDtoH(type->config); - types_.push_back(Type{config, type}); + types_.push_back(type); } TypeSpecPtr Build() { // Check for overflow. - if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) { + using ElementType = const ResTable_type*; + if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(ElementType) < + types_.size()) { return {}; } - TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type))); + TypeSpec* type_spec = + (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType))); type_spec->type_spec = header_; type_spec->idmap_entries = idmap_header_; type_spec->type_count = types_.size(); - memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type)); + memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType)); return TypeSpecPtr(type_spec); } @@ -118,7 +81,7 @@ class TypeSpecPtrBuilder { const ResTable_typeSpec* header_; const IdmapEntry_header* idmap_header_; - std::vector types_; + std::vector types_; }; } // namespace @@ -162,18 +125,17 @@ static bool VerifyResTableType(const ResTable_type* header) { return true; } -static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset, - size_t entry_idx) { +static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) { // Check that the offset is aligned. if (entry_offset & 0x03) { - LOG(ERROR) << "Entry offset at index " << entry_idx << " is not 4-byte aligned."; + LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned."; return false; } // Check that the offset doesn't overflow. if (entry_offset > std::numeric_limits::max() - dtohl(type->entriesStart)) { // Overflow in offset. - LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large."; + LOG(ERROR) << "Entry at offset " << entry_offset << " is too large."; return false; } @@ -181,7 +143,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset entry_offset += dtohl(type->entriesStart); if (entry_offset > chunk_size - sizeof(ResTable_entry)) { - LOG(ERROR) << "Entry offset at index " << entry_idx + LOG(ERROR) << "Entry at offset " << entry_offset << " is too large. No room for ResTable_entry."; return false; } @@ -191,13 +153,13 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t entry_size = dtohs(entry->size); if (entry_size < sizeof(*entry)) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx + LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too small."; return false; } if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx + LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too large."; return false; } @@ -205,7 +167,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset if (entry_size < sizeof(ResTable_map_entry)) { // There needs to be room for one Res_value struct. if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) { - LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << entry_idx + LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset << " for type " << (int)type->id << "."; return false; } @@ -214,12 +176,12 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset reinterpret_cast(reinterpret_cast(entry) + entry_size); const size_t value_size = dtohs(value->size); if (value_size < sizeof(Res_value)) { - LOG(ERROR) << "Res_value at index " << entry_idx << " is too small."; + LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small."; return false; } if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) { - LOG(ERROR) << "Res_value size " << value_size << " at index " << entry_idx + LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset << " is too large."; return false; } @@ -228,117 +190,76 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t map_entry_count = dtohl(map->count); size_t map_entries_start = entry_offset + entry_size; if (map_entries_start & 0x03) { - LOG(ERROR) << "Map entries at index " << entry_idx << " start at unaligned offset."; + LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset."; return false; } // Each entry is sizeof(ResTable_map) big. if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) { - LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << entry_idx << "."; + LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << "."; return false; } } return true; } -bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_idx, - const ResTable_config& config, FindEntryResult* out_entry) const { - const ResTable_config* best_config = nullptr; - const ResTable_type* best_type = nullptr; - uint32_t best_offset = 0; - - for (uint32_t i = 0; i < type_spec_ptr->type_count; i++) { - const Type* type = &type_spec_ptr->types[i]; - const ResTable_type* type_chunk = type->type; - - if (type->configuration.match(config) && - (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const size_t entry_count = dtohl(type_chunk->entryCount); - const size_t offsets_offset = dtohs(type_chunk->header.headerSize); - - // Check if there is the desired entry in this type. - - if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { - // This is encoded as a sparse map, so perform a binary search. - const ResTable_sparseTypeEntry* sparse_indices = - reinterpret_cast( - reinterpret_cast(type_chunk) + offsets_offset); - const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; - const ResTable_sparseTypeEntry* result = - std::lower_bound(sparse_indices, sparse_indices_end, entry_idx, - [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { - return dtohs(entry.idx) < entry_idx; - }); - - if (result == sparse_indices_end || dtohs(result->idx) != entry_idx) { - // No entry found. - continue; - } - - // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as - // the real offset divided by 4. - best_offset = uint32_t{dtohs(result->offset)} * 4u; - } else { - if (entry_idx >= entry_count) { - // This entry cannot be here. - continue; - } +const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk, + uint16_t entry_index) { + uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index); + if (entry_offset == ResTable_type::NO_ENTRY) { + return nullptr; + } + return GetEntryFromOffset(type_chunk, entry_offset); +} - const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type_chunk) + offsets_offset); - const uint32_t offset = dtohl(entry_offsets[entry_idx]); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } +uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const size_t entry_count = dtohl(type_chunk->entryCount); + const size_t offsets_offset = dtohs(type_chunk->header.headerSize); - // There is an entry for this resource, record it. - best_offset = offset; - } + // Check if there is the desired entry in this type. - best_config = &type->configuration; - best_type = type_chunk; + if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { + // This is encoded as a sparse map, so perform a binary search. + const ResTable_sparseTypeEntry* sparse_indices = + reinterpret_cast( + reinterpret_cast(type_chunk) + offsets_offset); + const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; + const ResTable_sparseTypeEntry* result = + std::lower_bound(sparse_indices, sparse_indices_end, entry_index, + [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { + return dtohs(entry.idx) < entry_idx; + }); + + if (result == sparse_indices_end || dtohs(result->idx) != entry_index) { + // No entry found. + return ResTable_type::NO_ENTRY; } - } - if (best_type == nullptr) { - return false; + // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as + // the real offset divided by 4. + return uint32_t{dtohs(result->offset)} * 4u; } - if (UNLIKELY(!VerifyResTableEntry(best_type, best_offset, entry_idx))) { - return false; + // This type is encoded as a dense array. + if (entry_index >= entry_count) { + // This entry cannot be here. + return ResTable_type::NO_ENTRY; } - const ResTable_entry* best_entry = reinterpret_cast( - reinterpret_cast(best_type) + best_offset + dtohl(best_type->entriesStart)); - - const uint32_t* flags = reinterpret_cast(type_spec_ptr->type_spec + 1); - out_entry->type_flags = dtohl(flags[entry_idx]); - out_entry->entry = best_entry; - out_entry->config = best_config; - out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1); - out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index)); - return true; + const uint32_t* entry_offsets = reinterpret_cast( + reinterpret_cast(type_chunk) + offsets_offset); + return dtohl(entry_offsets[entry_index]); } -bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, - FindEntryResult* out_entry) const { - // If the type IDs are offset in this package, we need to take that into account when searching - // for a type. - const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; - if (UNLIKELY(ptr == nullptr)) { - return false; +const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk, + uint32_t offset) { + if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) { + return nullptr; } - - // If there is an IDMAP supplied with this package, translate the entry ID. - if (ptr->idmap_entries != nullptr) { - if (!LoadedIdmap::Lookup(ptr->idmap_entries, entry_idx, &entry_idx)) { - // There is no mapping, so the resource is not meant to be in this overlay package. - return false; - } - } - return FindEntry(ptr, entry_idx, config, out_entry); + return reinterpret_cast(reinterpret_cast(type_chunk) + + offset + dtohl(type_chunk->entriesStart)); } void LoadedPackage::CollectConfigurations(bool exclude_mipmap, @@ -346,7 +267,7 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, const static std::u16string kMipMap = u"mipmap"; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const util::unique_cptr& type_spec = type_specs_[i]; + const TypeSpecPtr& type_spec = type_specs_[i]; if (type_spec != nullptr) { if (exclude_mipmap) { const int type_idx = type_spec->type_spec->id - 1; @@ -367,8 +288,11 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, } } - for (size_t j = 0; j < type_spec->type_count; j++) { - out_configs->insert(type_spec->types[j].configuration); + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config config; + config.copyFromDtoH((*iter)->config); + out_configs->insert(config); } } } @@ -378,10 +302,12 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set* out char temp_locale[RESTABLE_MAX_LOCALE_LEN]; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const util::unique_cptr& type_spec = type_specs_[i]; + const TypeSpecPtr& type_spec = type_specs_[i]; if (type_spec != nullptr) { - for (size_t j = 0; j < type_spec->type_count; j++) { - const ResTable_config& configuration = type_spec->types[j].configuration; + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config configuration; + configuration.copyFromDtoH((*iter)->config); if (configuration.locale != 0) { configuration.getBcp47Locale(temp_locale, canonicalize); std::string locale(temp_locale); @@ -409,17 +335,17 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } - for (size_t ti = 0; ti < type_spec->type_count; ti++) { - const Type* type = &type_spec->types[ti]; - size_t entry_count = dtohl(type->type->entryCount); + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + const ResTable_type* type = *iter; + size_t entry_count = dtohl(type->entryCount); for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) { const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type->type) + dtohs(type->type->header.headerSize)); + reinterpret_cast(type) + dtohs(type->header.headerSize)); const uint32_t offset = dtohl(entry_offsets[entry_idx]); if (offset != ResTable_type::NO_ENTRY) { - const ResTable_entry* entry = - reinterpret_cast(reinterpret_cast(type->type) + - dtohl(type->type->entriesStart) + offset); + const ResTable_entry* entry = reinterpret_cast( + reinterpret_cast(type) + dtohl(type->entriesStart) + offset); if (dtohl(entry->key.index) == static_cast(key_idx)) { // The package ID will be overridden by the caller (due to runtime assignment of package // IDs for shared libraries). @@ -431,8 +357,7 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } -const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const { - const uint8_t package_id = get_package_id(resid); +const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { for (const auto& loaded_package : packages_) { if (loaded_package->GetPackageId() == package_id) { return loaded_package.get(); @@ -680,26 +605,6 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, return std::move(loaded_package); } -bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, - FindEntryResult* out_entry) const { - ATRACE_CALL(); - - const uint8_t package_id = get_package_id(resid); - const uint8_t type_id = get_type_id(resid); - const uint16_t entry_id = get_entry_id(resid); - - if (UNLIKELY(type_id == 0)) { - LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); - return false; - } - - for (const auto& loaded_package : packages_) { - if (loaded_package->GetPackageId() == package_id) { - return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry); - } - } - return false; -} bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library) { diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index b033137b4764..ef08897d997a 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -69,6 +69,8 @@ struct ResolvedBag { Entry entries[0]; }; +struct FindEntryResult; + // AssetManager2 is the main entry point for accessing assets and resources. // AssetManager2 provides caching of resources retrieved via the underlying ApkAssets. class AssetManager2 { @@ -127,7 +129,7 @@ class AssetManager2 { // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap' // will be excluded from the list. std::set GetResourceConfigurations(bool exclude_system = false, - bool exclude_mipmap = false); + bool exclude_mipmap = false) const; // Returns all the locales for which there are resources defined. This includes resource // locales in all the ApkAssets set for this AssetManager. @@ -136,24 +138,24 @@ class AssetManager2 { // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized // and de-duped in the resulting list. std::set GetResourceLocales(bool exclude_system = false, - bool merge_equivalent_languages = false); + bool merge_equivalent_languages = false) const; // Searches the set of APKs loaded by this AssetManager and opens the first one found located // in the assets/ directory. // `mode` controls how the file is opened. // // NOTE: The loaded APKs are searched in reverse order. - std::unique_ptr Open(const std::string& filename, Asset::AccessMode mode); + std::unique_ptr Open(const std::string& filename, Asset::AccessMode mode) const; // Opens a file within the assets/ directory of the APK specified by `cookie`. // `mode` controls how the file is opened. std::unique_ptr Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode); + Asset::AccessMode mode) const; // Opens the directory specified by `dirname`. The result is an AssetDir that is the combination // of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded. // The entries are sorted by their ASCII name. - std::unique_ptr OpenDir(const std::string& dirname); + std::unique_ptr OpenDir(const std::string& dirname) const; // Searches the set of APKs loaded by this AssetManager and opens the first one found. // `mode` controls how the file is opened. @@ -161,24 +163,24 @@ class AssetManager2 { // // NOTE: The loaded APKs are searched in reverse order. std::unique_ptr OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie = nullptr); + ApkAssetsCookie* out_cookie = nullptr) const; // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened. // This is typically used to open a specific AndroidManifest.xml, or a binary XML file // referenced by a resource lookup with GetResource(). std::unique_ptr OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode); + Asset::AccessMode mode) const; // Populates the `out_name` parameter with resource name information. // Utf8 strings are preferred, and only if they are unavailable are // the Utf16 variants populated. // Returns false if the resource was not found or the name was missing/corrupt. - bool GetResourceName(uint32_t resid, ResourceName* out_name); + bool GetResourceName(uint32_t resid, ResourceName* out_name) const; // Populates `out_flags` with the bitmask of configuration axis that this resource varies with. // See ResTable_config for the list of configuration axis. // Returns false if the resource was not found. - bool GetResourceFlags(uint32_t resid, uint32_t* out_flags); + bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const; // Finds the resource ID assigned to `resource_name`. // `resource_name` must be of the form '[package:][type/]entry'. @@ -186,7 +188,7 @@ class AssetManager2 { // If no type is specified in `resource_name`, then `fallback_type` is used as the type. // Returns 0x0 if no resource by that name was found. uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {}, - const std::string& fallback_package = {}); + const std::string& fallback_package = {}) const; // Retrieves the best matching resource with ID `resid`. The resource value is filled into // `out_value` and the configuration for the selected value is populated in `out_selected_config`. @@ -199,7 +201,7 @@ class AssetManager2 { // this function logs if the resource was a map/bag type before returning kInvalidCookie. ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags); + uint32_t* out_flags) const; // Resolves the resource reference in `in_out_value` if the data type is // Res_value::TYPE_REFERENCE. @@ -215,7 +217,7 @@ class AssetManager2 { // it was not found. ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference); + uint32_t* out_last_reference) const; // Retrieves the best matching bag/map resource with ID `resid`. // This method will resolve all parent references for this bag and merge keys with the child. @@ -233,9 +235,9 @@ class AssetManager2 { std::unique_ptr NewTheme(); template - void ForEachPackage(Func func) { + void ForEachPackage(Func func) const { for (const PackageGroup& package_group : package_groups_) { - func(package_group.packages_.front()->GetPackageName(), + func(package_group.packages_.front().loaded_package_->GetPackageName(), package_group.dynamic_ref_table.mAssignedPackageId); } } @@ -260,7 +262,7 @@ class AssetManager2 { // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds. ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, - FindEntryResult* out_entry); + FindEntryResult* out_entry) const; // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. @@ -270,13 +272,43 @@ class AssetManager2 { // bitmask `diff`. void InvalidateCaches(uint32_t diff); + // Triggers the re-construction of lists of types that match the set configuration. + // This should always be called when mutating the AssetManager's configuration or ApkAssets set. + void RebuildFilterList(); + // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector apk_assets_; + // A collection of configurations and their associated ResTable_type that match the current + // AssetManager configuration. + struct FilteredConfigGroup { + std::vector configurations; + std::vector types; + }; + + // Represents an single package. + struct ConfiguredPackage { + // A pointer to the immutable, loaded package info. + const LoadedPackage* loaded_package_; + + // A mutable AssetManager-specific list of configurations that match the AssetManager's + // current configuration. This is used as an optimization to avoid checking every single + // candidate configuration when looking up resources. + ByteBucketArray filtered_configs_; + }; + + // Represents a logical package, which can be made up of many individual packages. Each package + // in a PackageGroup shares the same package name and package ID. struct PackageGroup { - std::vector packages_; + // The set of packages that make-up this group. + std::vector packages_; + + // The cookies associated with each package in the group. They share the same order as + // packages_. std::vector cookies_; + + // A library reference table that contains build-package ID to runtime-package ID mappings. DynamicRefTable dynamic_ref_table; }; @@ -350,7 +382,7 @@ class Theme { ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config = nullptr, uint32_t* in_out_type_spec_flags = nullptr, - uint32_t* out_last_ref = nullptr); + uint32_t* out_last_ref = nullptr) const; private: DISALLOW_COPY_AND_ASSIGN(Theme); diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 1775f5070f4e..35ae5fcd9e7b 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -41,33 +41,40 @@ class DynamicPackageEntry { int package_id = 0; }; -struct FindEntryResult { - // A pointer to the resource table entry for this resource. - // If the size of the entry is > sizeof(ResTable_entry), it can be cast to - // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry; - - // The configuration for which the resulting entry was defined. This points to a structure that - // is already swapped to host endianness. - const ResTable_config* config; - - // The bitmask of configuration axis with which the resource value varies. - uint32_t type_flags; - - // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table; - - // The string pool reference to the type's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef type_string_ref; - - // The string pool reference to the entry's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef entry_string_ref; +// TypeSpec is going to be immediately proceeded by +// an array of Type structs, all in the same block of memory. +struct TypeSpec { + // Pointer to the mmapped data where flags are kept. + // Flags denote whether the resource entry is public + // and under which configurations it varies. + const ResTable_typeSpec* type_spec; + + // Pointer to the mmapped data where the IDMAP mappings for this type + // exist. May be nullptr if no IDMAP exists. + const IdmapEntry_header* idmap_entries; + + // The number of types that follow this struct. + // There is a type for each configuration that entries are defined for. + size_t type_count; + + // Trick to easily access a variable number of Type structs + // proceeding this struct, and to ensure their alignment. + const ResTable_type* types[0]; + + inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const { + if (entry_index >= dtohl(type_spec->entryCount)) { + return 0u; + } + + const uint32_t* flags = reinterpret_cast(type_spec + 1); + return flags[entry_index]; + } }; -struct TypeSpec; -class LoadedArsc; +// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of +// ResTable_type pointers. +// TypeSpecPtr is a managed pointer that knows how to delete itself. +using TypeSpecPtr = util::unique_cptr; class LoadedPackage { public: @@ -77,9 +84,6 @@ class LoadedPackage { ~LoadedPackage(); - bool FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, - FindEntryResult* out_entry) const; - // Finds the entry with the specified type name and entry name. The names are in UTF-16 because // the underlying ResStringPool API expects this. For now this is acceptable, but since // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. @@ -87,6 +91,12 @@ class LoadedPackage { // for patching the correct package ID to the resource ID. uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; + static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index); + + static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index); + + static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset); + // Returns the string pool where type names are stored. inline const ResStringPool* GetTypeStringPool() const { return &type_string_pool_; @@ -136,14 +146,32 @@ class LoadedPackage { // before being inserted into the set. This may cause some equivalent locales to de-dupe. void CollectLocales(bool canonicalize, std::set* out_locales) const; + // type_idx is TT - 1 from 0xPPTTEEEE. + inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const { + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + return type_specs_[type_index - type_id_offset_].get(); + } + + template + void ForEachTypeSpec(Func f) const { + for (size_t i = 0; i < type_specs_.size(); i++) { + const TypeSpecPtr& ptr = type_specs_[i]; + if (ptr != nullptr) { + uint8_t type_id = ptr->type_spec->id; + if (ptr->idmap_entries != nullptr) { + type_id = ptr->idmap_entries->target_type_id; + } + f(ptr.get(), type_id - 1); + } + } + } + private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); LoadedPackage(); - bool FindEntry(const util::unique_cptr& type_spec_ptr, uint16_t entry_idx, - const ResTable_config& config, FindEntryResult* out_entry) const; - ResStringPool type_string_pool_; ResStringPool key_string_pool_; std::string package_name_; @@ -153,7 +181,7 @@ class LoadedPackage { bool system_ = false; bool overlay_ = false; - ByteBucketArray> type_specs_; + ByteBucketArray type_specs_; std::vector dynamic_package_map_; }; @@ -181,25 +209,20 @@ class LoadedArsc { return &global_string_pool_; } - // Finds the resource with ID `resid` with the best value for configuration `config`. - // The parameter `out_entry` will be filled with the resulting resource entry. - // The resource entry can be a simple entry (ResTable_entry) or a complex bag - // (ResTable_entry_map). - bool FindEntry(uint32_t resid, const ResTable_config& config, FindEntryResult* out_entry) const; + // Gets a pointer to the package with the specified package ID, or nullptr if no such package + // exists. + const LoadedPackage* GetPackageById(uint8_t package_id) const; - // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist. - const LoadedPackage* GetPackageForId(uint32_t resid) const; + // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. + inline const std::vector>& GetPackages() const { + return packages_; + } // Returns true if this is a system provided resource. inline bool IsSystem() const { return system_; } - // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. - inline const std::vector>& GetPackages() const { - return packages_; - } - private: DISALLOW_COPY_AND_ASSIGN(LoadedArsc); diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index 6c43a67e602f..e2b9f0040989 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -26,58 +26,56 @@ using ::android::base::unique_fd; using ::com::android::basic::R; +using ::testing::Eq; +using ::testing::Ge; +using ::testing::NotNull; +using ::testing::SizeIs; +using ::testing::StrEq; namespace android { TEST(ApkAssetsTest, LoadApk) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - - const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); - ASSERT_NE(nullptr, loaded_package); - - std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); + ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkFromFd) { const std::string path = GetTestDataPath() + "/basic/basic.apk"; unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY)); - ASSERT_GE(fd.get(), 0); + ASSERT_THAT(fd.get(), Ge(0)); std::unique_ptr loaded_apk = ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - - const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); - ASSERT_NE(nullptr, loaded_package); - - std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); + ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); + const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic()); loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic()); } @@ -86,19 +84,22 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { ResTable target_table; const std::string target_path = GetTestDataPath() + "/basic/basic.apk"; ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, target_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); + ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/), + Eq(NO_ERROR)); ResTable overlay_table; const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk"; ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); + ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/), + Eq(NO_ERROR)); util::unique_cptr idmap_data; void* temp_data; size_t idmap_len; - ASSERT_EQ(NO_ERROR, target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), - overlay_path.c_str(), &temp_data, &idmap_len)); + ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), + overlay_path.c_str(), &temp_data, &idmap_len), + Eq(NO_ERROR)); idmap_data.reset(temp_data); TemporaryFile tf; @@ -108,37 +109,30 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { // Open something so that the destructor of TemporaryFile closes a valid fd. tf.fd = open("/dev/null", O_WRONLY); - std::unique_ptr loaded_overlay_apk = ApkAssets::LoadOverlay(tf.path); - ASSERT_NE(nullptr, loaded_overlay_apk); + ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull()); } TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); - { - std::unique_ptr assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, assets); - } + { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } - { - std::unique_ptr assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, assets); - } + { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } } TEST(ApkAssetsTest, OpenUncompressedAssetFd) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(asset, NotNull()); off64_t start, length; unique_fd fd(asset->openFileDescriptor(&start, &length)); - EXPECT_GE(fd.get(), 0); + ASSERT_THAT(fd.get(), Ge(0)); lseek64(fd.get(), start, SEEK_SET); @@ -146,7 +140,7 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) { buffer.resize(length); ASSERT_TRUE(base::ReadFully(fd.get(), &*buffer.begin(), length)); - EXPECT_EQ("This should be uncompressed.\n\n", buffer); + EXPECT_THAT(buffer, StrEq("This should be uncompressed.\n\n")); } } // namespace android diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 37ddafb14fd3..bedebd66cb2f 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -16,6 +16,8 @@ #include "androidfw/LoadedArsc.h" +#include "androidfw/ResourceUtils.h" + #include "TestHelpers.h" #include "data/basic/R.h" #include "data/libclient/R.h" @@ -27,6 +29,13 @@ namespace basic = com::android::basic; namespace libclient = com::android::libclient; namespace sparse = com::android::sparse; +using ::testing::Eq; +using ::testing::Ge; +using ::testing::IsNull; +using ::testing::NotNull; +using ::testing::SizeIs; +using ::testing::StrEq; + namespace android { TEST(LoadedArscTest, LoadSinglePackageArsc) { @@ -35,39 +44,24 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); - - const std::vector>& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - EXPECT_EQ(std::string("com.android.app"), packages[0]->GetPackageName()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); - - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.sdkVersion = 24; - - FindEntryResult entry; + ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_TRUE(loaded_arsc->FindEntry(app::R::string::string_one, config, &entry)); - ASSERT_NE(nullptr, entry.entry); -} + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(app::R::string::string_one)); + ASSERT_THAT(package, NotNull()); + EXPECT_THAT(package->GetPackageName(), StrEq("com.android.app")); + EXPECT_THAT(package->GetPackageId(), Eq(0x7f)); -TEST(LoadedArscTest, FindDefaultEntry) { - std::string contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); + const uint8_t type_index = get_type_id(app::R::string::string_one) - 1; + const uint16_t entry_index = get_entry_id(app::R::string::string_one); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); - desired_config.language[0] = 'd'; - desired_config.language[1] = 'e'; - - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry)); - ASSERT_NE(nullptr, entry.entry); + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); } TEST(LoadedArscTest, LoadSparseEntryApp) { @@ -76,15 +70,22 @@ TEST(LoadedArscTest, LoadSparseEntryApp) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); + + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9)); + ASSERT_THAT(package, NotNull()); - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.sdkVersion = 26; + const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1; + const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9); - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(sparse::R::integer::foo_9, config, &entry)); - ASSERT_NE(nullptr, entry.entry); + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); } TEST(LoadedArscTest, LoadSharedLibrary) { @@ -93,14 +94,13 @@ TEST(LoadedArscTest, LoadSharedLibrary) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_EQ(std::string("com.android.lib_one"), packages[0]->GetPackageName()); - EXPECT_EQ(0, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.lib_one")); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0)); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); @@ -114,25 +114,23 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) { "resources.arsc", &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_FALSE(packages[0]->IsDynamic()); - EXPECT_EQ(std::string("com.android.libclient"), packages[0]->GetPackageName()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.libclient")); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); // The library has two dependencies. - ASSERT_EQ(2u, dynamic_pkg_map.size()); + ASSERT_THAT(dynamic_pkg_map, SizeIs(2u)); + EXPECT_THAT(dynamic_pkg_map[0].package_name, StrEq("com.android.lib_one")); + EXPECT_THAT(dynamic_pkg_map[0].package_id, Eq(0x02)); - EXPECT_EQ(std::string("com.android.lib_one"), dynamic_pkg_map[0].package_name); - EXPECT_EQ(0x02, dynamic_pkg_map[0].package_id); - - EXPECT_EQ(std::string("com.android.lib_two"), dynamic_pkg_map[1].package_name); - EXPECT_EQ(0x03, dynamic_pkg_map[1].package_id); + EXPECT_THAT(dynamic_pkg_map[1].package_name, StrEq("com.android.lib_two")); + EXPECT_THAT(dynamic_pkg_map[1].package_id, Eq(0x03)); } TEST(LoadedArscTest, LoadAppAsSharedLibrary) { @@ -143,13 +141,12 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, true /*load_as_shared_library*/); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); } TEST(LoadedArscTest, LoadFeatureSplit) { @@ -157,21 +154,27 @@ TEST(LoadedArscTest, LoadFeatureSplit) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc", &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(basic::R::string::test3)); + ASSERT_THAT(package, NotNull()); - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry)); + uint8_t type_index = get_type_id(basic::R::string::test3) - 1; + uint8_t entry_index = get_entry_id(basic::R::string::test3); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); size_t len; - const char16_t* type_name16 = entry.type_string_ref.string16(&len); - ASSERT_NE(nullptr, type_name16); - ASSERT_NE(0u, len); + const char16_t* type_name16 = + package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len); + ASSERT_THAT(type_name16, NotNull()); + EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string")); - std::string type_name = util::Utf16ToUtf8(StringPiece16(type_name16, len)); - EXPECT_EQ(std::string("string"), type_name); + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull()); } class MockLoadedIdmap : public LoadedIdmap { @@ -199,23 +202,33 @@ class MockLoadedIdmap : public LoadedIdmap { }; TEST(LoadedArscTest, LoadOverlay) { - std::string contents, overlay_contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); + std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc", - &overlay_contents)); + &contents)); MockLoadedIdmap loaded_idmap; std::unique_ptr loaded_arsc = - LoadedArsc::Load(StringPiece(overlay_contents), &loaded_idmap); - ASSERT_NE(nullptr, loaded_arsc); - - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); - - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(0x08030001u, desired_config, &entry)); + LoadedArsc::Load(StringPiece(contents), &loaded_idmap); + ASSERT_THAT(loaded_arsc, NotNull()); + + const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u); + ASSERT_THAT(package, NotNull()); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); + + // The entry being overlaid doesn't exist at the original entry index. + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull()); + + // Since this is an overlay, the actual entry ID must be mapped. + ASSERT_THAT(type_spec->idmap_entries, NotNull()); + uint16_t target_entry_id = 0u; + ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id)); + ASSERT_THAT(target_entry_id, Eq(0x0u)); + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull()); } // structs with size fields (like Res_value, ResTable_entry) should be diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h index 43a995536d89..df0c642f4565 100644 --- a/libs/androidfw/tests/TestHelpers.h +++ b/libs/androidfw/tests/TestHelpers.h @@ -20,6 +20,7 @@ #include #include "androidfw/ResourceTypes.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "CommonHelpers.h" -- cgit v1.2.3-59-g8ed1b From 78695c354342bd95ba5f63937b4e789139b50072 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Thu, 8 Feb 2018 14:50:38 -0800 Subject: AssetManager2: Allow out of order type/type spec AssetManager2 assumes that RES_TABLE_TYPE_SPEC_TYPEs must immediately precede their associated RES_TABLE_TYPE_TYPEs. This is not correct. RES_TABLE_TYPE_SPEC_TYPEs must precede their associated RES_TABLE_TYPE_TYPEs, but they do not need to immediately precede them. For example, this is what we currently expect: RES_TABLE_TYPE_SPEC_TYPE id=1 RES_TABLE_TYPE_TYPE id=1 RES_TABLE_TYPE_SPEC_TYPE id=2 RES_TABLE_TYPE_TYPE id=2 but this is also valid: RES_TABLE_TYPE_SPEC_TYPE id=1 RES_TABLE_TYPE_SPEC_TYPE id=2 RES_TABLE_TYPE_TYPE id=1 RES_TABLE_TYPE_TYPE id=2 Bug: 73052092 Test: make libandroidfw_tests Change-Id: I1f3c43760f8108eee24c2c6ed7bc16f70e951c2b --- libs/androidfw/LoadedArsc.cpp | 84 ++++++++------------- libs/androidfw/tests/LoadedArsc_test.cpp | 42 +++++++++++ .../data/out_of_order_types/AndroidManifest.xml | 18 +++++ libs/androidfw/tests/data/out_of_order_types/build | 22 ++++++ .../out_of_order_types/edited_resources.arsc.txt | 43 +++++++++++ .../data/out_of_order_types/out_of_order_types.apk | Bin 0 -> 1151 bytes .../data/out_of_order_types/res/values/values.xml | 20 +++++ 7 files changed, 178 insertions(+), 51 deletions(-) create mode 100644 libs/androidfw/tests/data/out_of_order_types/AndroidManifest.xml create mode 100755 libs/androidfw/tests/data/out_of_order_types/build create mode 100644 libs/androidfw/tests/data/out_of_order_types/edited_resources.arsc.txt create mode 100644 libs/androidfw/tests/data/out_of_order_types/out_of_order_types.apk create mode 100644 libs/androidfw/tests/data/out_of_order_types/res/values/values.xml (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 1d2c597c4c8c..a65d49b7e32f 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -409,14 +409,10 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, util::ReadUtf16StringFromDevice(header->name, arraysize(header->name), &loaded_package->package_name_); - // A TypeSpec builder. We use this to accumulate the set of Types - // available for a TypeSpec, and later build a single, contiguous block - // of memory that holds all the Types together with the TypeSpec. - std::unique_ptr types_builder; - - // Keep track of the last seen type index. Since type IDs are 1-based, - // this records their index, which is 0-based (type ID - 1). - uint8_t last_type_idx = 0; + // A map of TypeSpec builders, each associated with an type index. + // We use these to accumulate the set of Types available for a TypeSpec, and later build a single, + // contiguous block of memory that holds all the Types together with the TypeSpec. + std::unordered_map> type_builder_map; ChunkIterator iter(chunk.data_ptr(), chunk.data_size()); while (iter.HasNext()) { @@ -450,28 +446,6 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, case RES_TABLE_TYPE_SPEC_TYPE: { ATRACE_NAME("LoadTableTypeSpec"); - // Starting a new TypeSpec, so finish the old one if there was one. - if (types_builder) { - TypeSpecPtr type_spec_ptr = types_builder->Build(); - if (type_spec_ptr == nullptr) { - LOG(ERROR) << "Too many type configurations, overflow detected."; - return {}; - } - - // We only add the type to the package if there is no IDMAP, or if the type is - // overlaying something. - if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) { - // If this is an overlay, insert it at the target type ID. - if (type_spec_ptr->idmap_entries != nullptr) { - last_type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; - } - loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); - } - - types_builder = {}; - last_type_idx = 0; - } - const ResTable_typeSpec* type_spec = child_chunk.header(); if (type_spec == nullptr) { LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small."; @@ -506,8 +480,6 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, return {}; } - last_type_idx = type_spec->id - 1; - // If this is an overlay, associate the mapping of this type to the target type // from the IDMAP. const IdmapEntry_header* idmap_entry_header = nullptr; @@ -515,7 +487,13 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, idmap_entry_header = loaded_idmap->GetEntryMapForType(type_spec->id); } - types_builder = util::make_unique(type_spec, idmap_entry_header); + std::unique_ptr& builder_ptr = type_builder_map[type_spec->id - 1]; + if (builder_ptr == nullptr) { + builder_ptr = util::make_unique(type_spec, idmap_entry_header); + } else { + LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x", + type_spec->id); + } } break; case RES_TABLE_TYPE_TYPE: { @@ -530,12 +508,15 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } // Type chunks must be preceded by their TypeSpec chunks. - if (!types_builder || type->id - 1 != last_type_idx) { - LOG(ERROR) << "RES_TABLE_TYPE_TYPE found without preceding RES_TABLE_TYPE_SPEC_TYPE."; + std::unique_ptr& builder_ptr = type_builder_map[type->id - 1]; + if (builder_ptr != nullptr) { + builder_ptr->AddType(type); + } else { + LOG(ERROR) << StringPrintf( + "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.", + type->id); return {}; } - - types_builder->AddType(type); } break; case RES_TABLE_LIBRARY_TYPE: { @@ -561,7 +542,7 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, arraysize(entry_iter->packageName), &package_name); if (dtohl(entry_iter->packageId) >= std::numeric_limits::max()) { - LOG(ERROR) << base::StringPrintf( + LOG(ERROR) << StringPrintf( "Package ID %02x in RES_TABLE_LIBRARY_TYPE too large for package '%s'.", dtohl(entry_iter->packageId), package_name.c_str()); return {}; @@ -574,14 +555,20 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } break; default: - LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type()); break; } } - // Finish the last TypeSpec. - if (types_builder) { - TypeSpecPtr type_spec_ptr = types_builder->Build(); + if (iter.HadError()) { + LOG(ERROR) << iter.GetLastError(); + return {}; + } + + // Flatten and construct the TypeSpecs. + for (auto& entry : type_builder_map) { + uint8_t type_idx = static_cast(entry.first); + TypeSpecPtr type_spec_ptr = entry.second->Build(); if (type_spec_ptr == nullptr) { LOG(ERROR) << "Too many type configurations, overflow detected."; return {}; @@ -592,20 +579,15 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) { // If this is an overlay, insert it at the target type ID. if (type_spec_ptr->idmap_entries != nullptr) { - last_type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; + type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; } - loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); + loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr); } } - if (iter.HadError()) { - LOG(ERROR) << iter.GetLastError(); - return {}; - } return std::move(loaded_package); } - bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library) { ATRACE_CALL(); @@ -655,7 +637,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, } break; default: - LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type()); break; } } @@ -687,7 +669,7 @@ std::unique_ptr LoadedArsc::Load(const StringPiece& data, break; default: - LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type()); break; } } diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index bedebd66cb2f..cae632ddea30 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -16,6 +16,7 @@ #include "androidfw/LoadedArsc.h" +#include "android-base/file.h" #include "androidfw/ResourceUtils.h" #include "TestHelpers.h" @@ -29,6 +30,7 @@ namespace basic = com::android::basic; namespace libclient = com::android::libclient; namespace sparse = com::android::sparse; +using ::android::base::ReadFileToString; using ::testing::Eq; using ::testing::Ge; using ::testing::IsNull; @@ -177,6 +179,46 @@ TEST(LoadedArscTest, LoadFeatureSplit) { ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull()); } +// AAPT(2) generates resource tables with chunks in a certain order. The rule is that +// a RES_TABLE_TYPE_TYPE with id `i` must always be preceded by a RES_TABLE_TYPE_SPEC_TYPE with +// id `i`. The RES_TABLE_TYPE_SPEC_TYPE does not need to be directly preceding, however. +// +// AAPT(2) generates something like: +// RES_TABLE_TYPE_SPEC_TYPE id=1 +// RES_TABLE_TYPE_TYPE id=1 +// RES_TABLE_TYPE_SPEC_TYPE id=2 +// RES_TABLE_TYPE_TYPE id=2 +// +// But the following is valid too: +// RES_TABLE_TYPE_SPEC_TYPE id=1 +// RES_TABLE_TYPE_SPEC_TYPE id=2 +// RES_TABLE_TYPE_TYPE id=1 +// RES_TABLE_TYPE_TYPE id=2 +// +TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) { + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/out_of_order_types/out_of_order_types.apk", + "resources.arsc", &contents)); + + std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + ASSERT_THAT(loaded_arsc, NotNull()); + + ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); + const auto& package = loaded_arsc->GetPackages()[0]; + ASSERT_THAT(package, NotNull()); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); + + type_spec = package->GetTypeSpecByTypeIndex(1); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); +} + class MockLoadedIdmap : public LoadedIdmap { public: MockLoadedIdmap() : LoadedIdmap() { diff --git a/libs/androidfw/tests/data/out_of_order_types/AndroidManifest.xml b/libs/androidfw/tests/data/out_of_order_types/AndroidManifest.xml new file mode 100644 index 000000000000..34016db8b808 --- /dev/null +++ b/libs/androidfw/tests/data/out_of_order_types/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + diff --git a/libs/androidfw/tests/data/out_of_order_types/build b/libs/androidfw/tests/data/out_of_order_types/build new file mode 100755 index 000000000000..8496f81038b0 --- /dev/null +++ b/libs/androidfw/tests/data/out_of_order_types/build @@ -0,0 +1,22 @@ +#!/bin/bash +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +aapt2 compile --dir res -o compiled.flata +aapt2 link --manifest AndroidManifest.xml -o out_of_order_types.apk compiled.flata +rm compiled.flata diff --git a/libs/androidfw/tests/data/out_of_order_types/edited_resources.arsc.txt b/libs/androidfw/tests/data/out_of_order_types/edited_resources.arsc.txt new file mode 100644 index 000000000000..eca8f478c501 --- /dev/null +++ b/libs/androidfw/tests/data/out_of_order_types/edited_resources.arsc.txt @@ -0,0 +1,43 @@ +00000000: 0200 0c00 ac02 0000 0100 0000 0100 1c00 ................ +00000010: 1c00 0000 0000 0000 0000 0000 0001 0000 ................ +00000020: 1c00 0000 0000 0000 0002 2001 8402 0000 .......... ..... +00000030: 7f00 0000 6300 6f00 6d00 2e00 6100 6e00 ....c.o.m...a.n. +00000040: 6400 7200 6f00 6900 6400 2e00 6100 7000 d.r.o.i.d...a.p. +00000050: 7000 0000 0000 0000 0000 0000 0000 0000 p............... +00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000090: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +000000a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +000000b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +000000c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +000000d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +000000e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +000000f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000100: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000110: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000120: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000130: 0000 0000 2001 0000 0000 0000 6401 0000 .... .......d... +00000140: 0000 0000 0000 0000 0100 1c00 4400 0000 ............D... +00000150: 0200 0000 0000 0000 0000 0000 2400 0000 ............$... +00000160: 0000 0000 0000 0000 0c00 0000 0400 6200 ..............b. +00000170: 6f00 6f00 6c00 0000 0700 6900 6e00 7400 o.o.l.....i.n.t. +00000180: 6500 6700 6500 7200 0000 0000 0100 1c00 e.g.e.r......... +00000190: 2800 0000 0100 0000 0000 0000 0001 0000 (............... +000001a0: 2000 0000 0000 0000 0000 0000 0404 7465 .............te +000001b0: 7374 0000 0202 1000 1400 0000 0100 0000 st.............. +000001c0: 0100 0000 0000 0000 0202 1000 1400 0000 +000001d0: 0200 0000 0100 0000 0000 0000 0102 5400 +000001e0: 6800 0000 0100 0000 0100 0000 5800 0000 +000001f0: 4000 0000 0000 0000 0000 0000 0000 0000 +00000200: 0000 0000 0000 0000 0000 0000 0000 0000 +00000210: 0000 0000 0000 0000 0000 0000 0000 0000 +00000220: 0000 0000 0000 0000 0000 0000 0000 0000 +00000230: 0000 0000 0800 0000 0000 0000 0800 0012 +00000240: ffff ffff 0102 5400 6800 0000 0200 0000 ......T.h....... +00000250: 0100 0000 5800 0000 4000 0000 0000 0000 ....X...@....... +00000260: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000270: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000280: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000290: 0000 0000 0000 0000 0000 0000 0800 0000 ................ +000002a0: 0000 0000 0800 0010 0100 0000 ............ diff --git a/libs/androidfw/tests/data/out_of_order_types/out_of_order_types.apk b/libs/androidfw/tests/data/out_of_order_types/out_of_order_types.apk new file mode 100644 index 000000000000..75146e0fc476 Binary files /dev/null and b/libs/androidfw/tests/data/out_of_order_types/out_of_order_types.apk differ diff --git a/libs/androidfw/tests/data/out_of_order_types/res/values/values.xml b/libs/androidfw/tests/data/out_of_order_types/res/values/values.xml new file mode 100644 index 000000000000..7c54fbae9f21 --- /dev/null +++ b/libs/androidfw/tests/data/out_of_order_types/res/values/values.xml @@ -0,0 +1,20 @@ + + + + + true + 1 + -- cgit v1.2.3-59-g8ed1b From dfeb7ceb2afc1f630e7d2afdd9f37ef70386e146 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Fri, 9 Feb 2018 11:01:36 -0800 Subject: Revert "AssetManager2: Allow out of order type/type spec" This reverts commit 78695c354342bd95ba5f63937b4e789139b50072. Bug: 73134570 Change-Id: I6acc35372d9071d067d2fb7caa775ee9ba689811 --- libs/androidfw/LoadedArsc.cpp | 84 +++++++++++++-------- libs/androidfw/tests/LoadedArsc_test.cpp | 42 ----------- .../data/out_of_order_types/AndroidManifest.xml | 18 ----- libs/androidfw/tests/data/out_of_order_types/build | 22 ------ .../out_of_order_types/edited_resources.arsc.txt | 43 ----------- .../data/out_of_order_types/out_of_order_types.apk | Bin 1151 -> 0 bytes .../data/out_of_order_types/res/values/values.xml | 20 ----- 7 files changed, 51 insertions(+), 178 deletions(-) delete mode 100644 libs/androidfw/tests/data/out_of_order_types/AndroidManifest.xml delete mode 100755 libs/androidfw/tests/data/out_of_order_types/build delete mode 100644 libs/androidfw/tests/data/out_of_order_types/edited_resources.arsc.txt delete mode 100644 libs/androidfw/tests/data/out_of_order_types/out_of_order_types.apk delete mode 100644 libs/androidfw/tests/data/out_of_order_types/res/values/values.xml (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index a65d49b7e32f..1d2c597c4c8c 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -409,10 +409,14 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, util::ReadUtf16StringFromDevice(header->name, arraysize(header->name), &loaded_package->package_name_); - // A map of TypeSpec builders, each associated with an type index. - // We use these to accumulate the set of Types available for a TypeSpec, and later build a single, - // contiguous block of memory that holds all the Types together with the TypeSpec. - std::unordered_map> type_builder_map; + // A TypeSpec builder. We use this to accumulate the set of Types + // available for a TypeSpec, and later build a single, contiguous block + // of memory that holds all the Types together with the TypeSpec. + std::unique_ptr types_builder; + + // Keep track of the last seen type index. Since type IDs are 1-based, + // this records their index, which is 0-based (type ID - 1). + uint8_t last_type_idx = 0; ChunkIterator iter(chunk.data_ptr(), chunk.data_size()); while (iter.HasNext()) { @@ -446,6 +450,28 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, case RES_TABLE_TYPE_SPEC_TYPE: { ATRACE_NAME("LoadTableTypeSpec"); + // Starting a new TypeSpec, so finish the old one if there was one. + if (types_builder) { + TypeSpecPtr type_spec_ptr = types_builder->Build(); + if (type_spec_ptr == nullptr) { + LOG(ERROR) << "Too many type configurations, overflow detected."; + return {}; + } + + // We only add the type to the package if there is no IDMAP, or if the type is + // overlaying something. + if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) { + // If this is an overlay, insert it at the target type ID. + if (type_spec_ptr->idmap_entries != nullptr) { + last_type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; + } + loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); + } + + types_builder = {}; + last_type_idx = 0; + } + const ResTable_typeSpec* type_spec = child_chunk.header(); if (type_spec == nullptr) { LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small."; @@ -480,6 +506,8 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, return {}; } + last_type_idx = type_spec->id - 1; + // If this is an overlay, associate the mapping of this type to the target type // from the IDMAP. const IdmapEntry_header* idmap_entry_header = nullptr; @@ -487,13 +515,7 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, idmap_entry_header = loaded_idmap->GetEntryMapForType(type_spec->id); } - std::unique_ptr& builder_ptr = type_builder_map[type_spec->id - 1]; - if (builder_ptr == nullptr) { - builder_ptr = util::make_unique(type_spec, idmap_entry_header); - } else { - LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x", - type_spec->id); - } + types_builder = util::make_unique(type_spec, idmap_entry_header); } break; case RES_TABLE_TYPE_TYPE: { @@ -508,15 +530,12 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } // Type chunks must be preceded by their TypeSpec chunks. - std::unique_ptr& builder_ptr = type_builder_map[type->id - 1]; - if (builder_ptr != nullptr) { - builder_ptr->AddType(type); - } else { - LOG(ERROR) << StringPrintf( - "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.", - type->id); + if (!types_builder || type->id - 1 != last_type_idx) { + LOG(ERROR) << "RES_TABLE_TYPE_TYPE found without preceding RES_TABLE_TYPE_SPEC_TYPE."; return {}; } + + types_builder->AddType(type); } break; case RES_TABLE_LIBRARY_TYPE: { @@ -542,7 +561,7 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, arraysize(entry_iter->packageName), &package_name); if (dtohl(entry_iter->packageId) >= std::numeric_limits::max()) { - LOG(ERROR) << StringPrintf( + LOG(ERROR) << base::StringPrintf( "Package ID %02x in RES_TABLE_LIBRARY_TYPE too large for package '%s'.", dtohl(entry_iter->packageId), package_name.c_str()); return {}; @@ -555,20 +574,14 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } break; default: - LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type()); break; } } - if (iter.HadError()) { - LOG(ERROR) << iter.GetLastError(); - return {}; - } - - // Flatten and construct the TypeSpecs. - for (auto& entry : type_builder_map) { - uint8_t type_idx = static_cast(entry.first); - TypeSpecPtr type_spec_ptr = entry.second->Build(); + // Finish the last TypeSpec. + if (types_builder) { + TypeSpecPtr type_spec_ptr = types_builder->Build(); if (type_spec_ptr == nullptr) { LOG(ERROR) << "Too many type configurations, overflow detected."; return {}; @@ -579,15 +592,20 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) { // If this is an overlay, insert it at the target type ID. if (type_spec_ptr->idmap_entries != nullptr) { - type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; + last_type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; } - loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr); + loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); } } + if (iter.HadError()) { + LOG(ERROR) << iter.GetLastError(); + return {}; + } return std::move(loaded_package); } + bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library) { ATRACE_CALL(); @@ -637,7 +655,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, } break; default: - LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type()); break; } } @@ -669,7 +687,7 @@ std::unique_ptr LoadedArsc::Load(const StringPiece& data, break; default: - LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type()); break; } } diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index cae632ddea30..bedebd66cb2f 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -16,7 +16,6 @@ #include "androidfw/LoadedArsc.h" -#include "android-base/file.h" #include "androidfw/ResourceUtils.h" #include "TestHelpers.h" @@ -30,7 +29,6 @@ namespace basic = com::android::basic; namespace libclient = com::android::libclient; namespace sparse = com::android::sparse; -using ::android::base::ReadFileToString; using ::testing::Eq; using ::testing::Ge; using ::testing::IsNull; @@ -179,46 +177,6 @@ TEST(LoadedArscTest, LoadFeatureSplit) { ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull()); } -// AAPT(2) generates resource tables with chunks in a certain order. The rule is that -// a RES_TABLE_TYPE_TYPE with id `i` must always be preceded by a RES_TABLE_TYPE_SPEC_TYPE with -// id `i`. The RES_TABLE_TYPE_SPEC_TYPE does not need to be directly preceding, however. -// -// AAPT(2) generates something like: -// RES_TABLE_TYPE_SPEC_TYPE id=1 -// RES_TABLE_TYPE_TYPE id=1 -// RES_TABLE_TYPE_SPEC_TYPE id=2 -// RES_TABLE_TYPE_TYPE id=2 -// -// But the following is valid too: -// RES_TABLE_TYPE_SPEC_TYPE id=1 -// RES_TABLE_TYPE_SPEC_TYPE id=2 -// RES_TABLE_TYPE_TYPE id=1 -// RES_TABLE_TYPE_TYPE id=2 -// -TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) { - std::string contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/out_of_order_types/out_of_order_types.apk", - "resources.arsc", &contents)); - - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); - - ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); - const auto& package = loaded_arsc->GetPackages()[0]; - ASSERT_THAT(package, NotNull()); - - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); - - type_spec = package->GetTypeSpecByTypeIndex(1); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); -} - class MockLoadedIdmap : public LoadedIdmap { public: MockLoadedIdmap() : LoadedIdmap() { diff --git a/libs/androidfw/tests/data/out_of_order_types/AndroidManifest.xml b/libs/androidfw/tests/data/out_of_order_types/AndroidManifest.xml deleted file mode 100644 index 34016db8b808..000000000000 --- a/libs/androidfw/tests/data/out_of_order_types/AndroidManifest.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - diff --git a/libs/androidfw/tests/data/out_of_order_types/build b/libs/androidfw/tests/data/out_of_order_types/build deleted file mode 100755 index 8496f81038b0..000000000000 --- a/libs/androidfw/tests/data/out_of_order_types/build +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -set -e - -aapt2 compile --dir res -o compiled.flata -aapt2 link --manifest AndroidManifest.xml -o out_of_order_types.apk compiled.flata -rm compiled.flata diff --git a/libs/androidfw/tests/data/out_of_order_types/edited_resources.arsc.txt b/libs/androidfw/tests/data/out_of_order_types/edited_resources.arsc.txt deleted file mode 100644 index eca8f478c501..000000000000 --- a/libs/androidfw/tests/data/out_of_order_types/edited_resources.arsc.txt +++ /dev/null @@ -1,43 +0,0 @@ -00000000: 0200 0c00 ac02 0000 0100 0000 0100 1c00 ................ -00000010: 1c00 0000 0000 0000 0000 0000 0001 0000 ................ -00000020: 1c00 0000 0000 0000 0002 2001 8402 0000 .......... ..... -00000030: 7f00 0000 6300 6f00 6d00 2e00 6100 6e00 ....c.o.m...a.n. -00000040: 6400 7200 6f00 6900 6400 2e00 6100 7000 d.r.o.i.d...a.p. -00000050: 7000 0000 0000 0000 0000 0000 0000 0000 p............... -00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -00000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -00000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -00000090: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -000000a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -000000b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -000000c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -000000d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -000000e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -000000f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -00000100: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -00000110: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -00000120: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -00000130: 0000 0000 2001 0000 0000 0000 6401 0000 .... .......d... -00000140: 0000 0000 0000 0000 0100 1c00 4400 0000 ............D... -00000150: 0200 0000 0000 0000 0000 0000 2400 0000 ............$... -00000160: 0000 0000 0000 0000 0c00 0000 0400 6200 ..............b. -00000170: 6f00 6f00 6c00 0000 0700 6900 6e00 7400 o.o.l.....i.n.t. -00000180: 6500 6700 6500 7200 0000 0000 0100 1c00 e.g.e.r......... -00000190: 2800 0000 0100 0000 0000 0000 0001 0000 (............... -000001a0: 2000 0000 0000 0000 0000 0000 0404 7465 .............te -000001b0: 7374 0000 0202 1000 1400 0000 0100 0000 st.............. -000001c0: 0100 0000 0000 0000 0202 1000 1400 0000 -000001d0: 0200 0000 0100 0000 0000 0000 0102 5400 -000001e0: 6800 0000 0100 0000 0100 0000 5800 0000 -000001f0: 4000 0000 0000 0000 0000 0000 0000 0000 -00000200: 0000 0000 0000 0000 0000 0000 0000 0000 -00000210: 0000 0000 0000 0000 0000 0000 0000 0000 -00000220: 0000 0000 0000 0000 0000 0000 0000 0000 -00000230: 0000 0000 0800 0000 0000 0000 0800 0012 -00000240: ffff ffff 0102 5400 6800 0000 0200 0000 ......T.h....... -00000250: 0100 0000 5800 0000 4000 0000 0000 0000 ....X...@....... -00000260: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -00000270: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -00000280: 0000 0000 0000 0000 0000 0000 0000 0000 ................ -00000290: 0000 0000 0000 0000 0000 0000 0800 0000 ................ -000002a0: 0000 0000 0800 0010 0100 0000 ............ diff --git a/libs/androidfw/tests/data/out_of_order_types/out_of_order_types.apk b/libs/androidfw/tests/data/out_of_order_types/out_of_order_types.apk deleted file mode 100644 index 75146e0fc476..000000000000 Binary files a/libs/androidfw/tests/data/out_of_order_types/out_of_order_types.apk and /dev/null differ diff --git a/libs/androidfw/tests/data/out_of_order_types/res/values/values.xml b/libs/androidfw/tests/data/out_of_order_types/res/values/values.xml deleted file mode 100644 index 7c54fbae9f21..000000000000 --- a/libs/androidfw/tests/data/out_of_order_types/res/values/values.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - true - 1 - -- cgit v1.2.3-59-g8ed1b From b8b3a2619435eb226ce13b1792a6ccb1302b3cb2 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Fri, 9 Feb 2018 11:01:45 -0800 Subject: Revert "libandroidfw: Improve performance of AssetManager2" This reverts commit 88c9959e5b417320bbc2484fab42ab4b12379533. Bug: 73134570 Change-Id: I012643d2b4212cef5aef68feb4146add34f9ecfc --- libs/androidfw/Android.bp | 1 - libs/androidfw/AssetManager2.cpp | 276 ++++++----------------- libs/androidfw/LoadedArsc.cpp | 261 ++++++++++++++------- libs/androidfw/include/androidfw/AssetManager2.h | 66 ++---- libs/androidfw/include/androidfw/LoadedArsc.h | 111 ++++----- libs/androidfw/tests/ApkAssets_test.cpp | 78 ++++--- libs/androidfw/tests/LoadedArsc_test.cpp | 169 +++++++------- libs/androidfw/tests/TestHelpers.h | 1 - 8 files changed, 424 insertions(+), 539 deletions(-) (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 70d52164ff74..7c9078b164a2 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -145,7 +145,6 @@ cc_test { "tests/TypeWrappers_test.cpp", "tests/ZipUtils_test.cpp", ], - static_libs: ["libgmock"], target: { android: { srcs: [ diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index a8c916bbaea6..20dba37fbe66 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -36,31 +36,6 @@ namespace android { -struct FindEntryResult { - // A pointer to the resource table entry for this resource. - // If the size of the entry is > sizeof(ResTable_entry), it can be cast to - // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry; - - // The configuration for which the resulting entry was defined. This is already swapped to host - // endianness. - ResTable_config config; - - // The bitmask of configuration axis with which the resource value varies. - uint32_t type_flags; - - // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table; - - // The string pool reference to the type's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef type_string_ref; - - // The string pool reference to the entry's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef entry_string_ref; -}; - AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } @@ -69,7 +44,6 @@ bool AssetManager2::SetApkAssets(const std::vector& apk_assets bool invalidate_caches) { apk_assets_ = apk_assets; BuildDynamicRefTable(); - RebuildFilterList(); if (invalidate_caches) { InvalidateCaches(static_cast(-1)); } @@ -107,7 +81,7 @@ void AssetManager2::BuildDynamicRefTable() { PackageGroup* package_group = &package_groups_[idx]; // Add the package and to the set of packages with the same ID. - package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); + package_group->packages_.push_back(package.get()); package_group->cookies_.push_back(static_cast(i)); // Add the package name -> build time ID mappings. @@ -122,7 +96,7 @@ void AssetManager2::BuildDynamicRefTable() { // Now assign the runtime IDs so that we have a build-time to runtime ID map. const auto package_groups_end = package_groups_.end(); for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) { - const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName(); + const std::string& package_name = iter->packages_[0]->GetPackageName(); for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) { iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()), iter->dynamic_ref_table.mAssignedPackageId); @@ -144,22 +118,20 @@ void AssetManager2::DumpToLog() const { list = ""; for (size_t i = 0; i < package_ids_.size(); i++) { if (package_ids_[i] != 0xff) { - base::StringAppendF(&list, "%02x -> %d, ", (int)i, package_ids_[i]); + base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]); } } LOG(INFO) << "Package ID map: " << list; for (const auto& package_group: package_groups_) { - list = ""; - for (const auto& package : package_group.packages_) { - const LoadedPackage* loaded_package = package.loaded_package_; - base::StringAppendF(&list, "%s(%02x%s), ", loaded_package->GetPackageName().c_str(), - loaded_package->GetPackageId(), - (loaded_package->IsDynamic() ? " dynamic" : "")); - } - LOG(INFO) << base::StringPrintf("PG (%02x): ", - package_group.dynamic_ref_table.mAssignedPackageId) - << list; + list = ""; + for (const auto& package : package_group.packages_) { + base::StringAppendF(&list, "%s(%02x%s), ", package->GetPackageName().c_str(), + package->GetPackageId(), (package->IsDynamic() ? " dynamic" : "")); + } + LOG(INFO) << base::StringPrintf("PG (%02x): ", + package_group.dynamic_ref_table.mAssignedPackageId) + << list; } } @@ -198,54 +170,52 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { configuration_ = configuration; if (diff) { - RebuildFilterList(); InvalidateCaches(static_cast(diff)); } } std::set AssetManager2::GetResourceConfigurations(bool exclude_system, - bool exclude_mipmap) const { + bool exclude_mipmap) { ATRACE_CALL(); std::set configurations; for (const PackageGroup& package_group : package_groups_) { - for (const ConfiguredPackage& package : package_group.packages_) { - if (exclude_system && package.loaded_package_->IsSystem()) { + for (const LoadedPackage* package : package_group.packages_) { + if (exclude_system && package->IsSystem()) { continue; } - package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations); + package->CollectConfigurations(exclude_mipmap, &configurations); } } return configurations; } std::set AssetManager2::GetResourceLocales(bool exclude_system, - bool merge_equivalent_languages) const { + bool merge_equivalent_languages) { ATRACE_CALL(); std::set locales; for (const PackageGroup& package_group : package_groups_) { - for (const ConfiguredPackage& package : package_group.packages_) { - if (exclude_system && package.loaded_package_->IsSystem()) { + for (const LoadedPackage* package : package_group.packages_) { + if (exclude_system && package->IsSystem()) { continue; } - package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales); + package->CollectLocales(merge_equivalent_languages, &locales); } } return locales; } -std::unique_ptr AssetManager2::Open(const std::string& filename, - Asset::AccessMode mode) const { +std::unique_ptr AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, mode); } std::unique_ptr AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) const { + Asset::AccessMode mode) { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, cookie, mode); } -std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) const { +std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) { ATRACE_CALL(); std::string full_path = "assets/" + dirname; @@ -279,7 +249,7 @@ std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) con // is inconsistent for split APKs. std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie) const { + ApkAssetsCookie* out_cookie) { ATRACE_CALL(); for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) { std::unique_ptr asset = apk_assets_[i]->Open(filename, mode); @@ -298,8 +268,7 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, - ApkAssetsCookie cookie, - Asset::AccessMode mode) const { + ApkAssetsCookie cookie, Asset::AccessMode mode) { ATRACE_CALL(); if (cookie < 0 || static_cast(cookie) >= apk_assets_.size()) { return {}; @@ -308,13 +277,12 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, - bool /*stop_at_first_match*/, - FindEntryResult* out_entry) const { + bool stop_at_first_match, FindEntryResult* out_entry) { // Might use this if density_override != 0. ResTable_config density_override_config; // Select our configuration or generate a density override configuration. - const ResTable_config* desired_config = &configuration_; + ResTable_config* desired_config = &configuration_; if (density_override != 0 && density_override != configuration_.density) { density_override_config = configuration_; density_override_config.density = density_override; @@ -328,135 +296,53 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri const uint32_t package_id = get_package_id(resid); const uint8_t type_idx = get_type_id(resid) - 1; - const uint16_t entry_idx = get_entry_id(resid); + const uint16_t entry_id = get_entry_id(resid); - const uint8_t package_idx = package_ids_[package_id]; - if (package_idx == 0xff) { + const uint8_t idx = package_ids_[package_id]; + if (idx == 0xff) { LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); return kInvalidCookie; } - const PackageGroup& package_group = package_groups_[package_idx]; - const size_t package_count = package_group.packages_.size(); - + FindEntryResult best_entry; ApkAssetsCookie best_cookie = kInvalidCookie; - const LoadedPackage* best_package = nullptr; - const ResTable_type* best_type = nullptr; - const ResTable_config* best_config = nullptr; - ResTable_config best_config_copy; - uint32_t best_offset = 0u; - uint32_t type_flags = 0u; - - // If desired_config is the same as the set configuration, then we can use our filtered list - // and we don't need to match the configurations, since they already matched. - const bool use_fast_path = desired_config == &configuration_; - - for (size_t pi = 0; pi < package_count; pi++) { - const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; - const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_; - ApkAssetsCookie cookie = package_group.cookies_[pi]; - - // If the type IDs are offset in this package, we need to take that into account when searching - // for a type. - const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx); - if (UNLIKELY(type_spec == nullptr)) { - continue; - } - - uint16_t local_entry_idx = entry_idx; + uint32_t cumulated_flags = 0u; - // If there is an IDMAP supplied with this package, translate the entry ID. - if (type_spec->idmap_entries != nullptr) { - if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) { - // There is no mapping, so the resource is not meant to be in this overlay package. - continue; - } + const PackageGroup& package_group = package_groups_[idx]; + const size_t package_count = package_group.packages_.size(); + FindEntryResult current_entry; + for (size_t i = 0; i < package_count; i++) { + const LoadedPackage* loaded_package = package_group.packages_[i]; + if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry)) { + continue; } - type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx); - - // If the package is an overlay, then even configurations that are the same MUST be chosen. - const bool package_is_overlay = loaded_package->IsOverlay(); - - const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; - if (use_fast_path) { - const std::vector& candidate_configs = filtered_group.configurations; - const size_t type_count = candidate_configs.size(); - for (uint32_t i = 0; i < type_count; i++) { - const ResTable_config& this_config = candidate_configs[i]; - - // We can skip calling ResTable_config::match() because we know that all candidate - // configurations that do NOT match have been filtered-out. - if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || - (package_is_overlay && this_config.compare(*best_config) == 0)) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const ResTable_type* type_chunk = filtered_group.types[i]; - const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } - - best_cookie = cookie; - best_package = loaded_package; - best_type = type_chunk; - best_config = &this_config; - best_offset = offset; - } - } - } else { - // This is the slower path, which doesn't use the filtered list of configurations. - // Here we must read the ResTable_config from the mmapped APK, convert it to host endianness - // and fill in any new fields that did not exist when the APK was compiled. - // Furthermore when selecting configurations we can't just record the pointer to the - // ResTable_config, we must copy it. - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config this_config; - this_config.copyFromDtoH((*iter)->config); - - if (this_config.match(*desired_config)) { - if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || - (package_is_overlay && this_config.compare(*best_config) == 0)) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } + cumulated_flags |= current_entry.type_flags; - best_cookie = cookie; - best_package = loaded_package; - best_type = *iter; - best_config_copy = this_config; - best_config = &best_config_copy; - best_offset = offset; - } - } + const ResTable_config* current_config = current_entry.config; + const ResTable_config* best_config = best_entry.config; + if (best_cookie == kInvalidCookie || + current_config->isBetterThan(*best_config, desired_config) || + (loaded_package->IsOverlay() && current_config->compare(*best_config) == 0)) { + best_entry = current_entry; + best_cookie = package_group.cookies_[i]; + if (stop_at_first_match) { + break; } } } - if (UNLIKELY(best_cookie == kInvalidCookie)) { + if (best_cookie == kInvalidCookie) { return kInvalidCookie; } - const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset); - if (UNLIKELY(best_entry == nullptr)) { - return kInvalidCookie; - } - - out_entry->entry = best_entry; - out_entry->config = *best_config; - out_entry->type_flags = type_flags; - out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); - out_entry->entry_string_ref = - StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); + *out_entry = best_entry; out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; + out_entry->type_flags = cumulated_flags; return best_cookie; } -bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const { +bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { ATRACE_CALL(); FindEntryResult entry; @@ -466,8 +352,7 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons return false; } - const LoadedPackage* package = - apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); + const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid); if (package == nullptr) { return false; } @@ -495,7 +380,7 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons return true; } -bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const { +bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { FindEntryResult entry; ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); @@ -509,7 +394,7 @@ bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) const { + uint32_t* out_flags) { ATRACE_CALL(); FindEntryResult entry; @@ -528,7 +413,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Create a reference since we can't represent this complex type as a Res_value. out_value->dataType = Res_value::TYPE_REFERENCE; out_value->data = resid; - *out_selected_config = entry.config; + *out_selected_config = *entry.config; *out_flags = entry.type_flags; return cookie; } @@ -540,7 +425,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Convert the package ID to the runtime assigned package ID. entry.dynamic_ref_table->lookupResourceValue(out_value); - *out_selected_config = entry.config; + *out_selected_config = *entry.config; *out_flags = entry.type_flags; return cookie; } @@ -548,7 +433,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) const { + uint32_t* out_last_reference) { ATRACE_CALL(); constexpr const int kMaxIterations = 20; @@ -616,8 +501,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { // Attributes, arrays, etc don't have a resource id as the name. They specify // other data, which would be wrong to change via a lookup. if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, - resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); return nullptr; } } @@ -649,8 +533,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { const ResolvedBag* parent_bag = GetBag(parent_resid); if (parent_bag == nullptr) { // Failed to get the parent that should exist. - LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, - resid); + LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid); return nullptr; } @@ -669,8 +552,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t child_key = dtohl(map_entry->name.ident); if (!is_internal_resid(child_key)) { if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, - resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid); return nullptr; } } @@ -709,8 +591,7 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t new_key = dtohl(map_entry->name.ident); if (!is_internal_resid(new_key)) { if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, - resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); return nullptr; } } @@ -766,7 +647,7 @@ static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) { uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const std::string& fallback_type, - const std::string& fallback_package) const { + const std::string& fallback_package) { StringPiece package_name, type, entry; if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) { return 0u; @@ -798,8 +679,7 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const static std::u16string kAttrPrivate16 = u"^attr-private"; for (const PackageGroup& package_group : package_groups_) { - for (const ConfiguredPackage& package_impl : package_group.packages_) { - const LoadedPackage* package = package_impl.loaded_package_; + for (const LoadedPackage* package : package_group.packages_) { if (package_name != package->GetPackageName()) { // All packages in the same group are expected to have the same package name. break; @@ -821,32 +701,6 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, return 0u; } -void AssetManager2::RebuildFilterList() { - for (PackageGroup& group : package_groups_) { - for (ConfiguredPackage& impl : group.packages_) { - // Destroy it. - impl.filtered_configs_.~ByteBucketArray(); - - // Re-create it. - new (&impl.filtered_configs_) ByteBucketArray(); - - // Create the filters here. - impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec* spec, uint8_t type_index) { - FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_index); - const auto iter_end = spec->types + spec->type_count; - for (auto iter = spec->types; iter != iter_end; ++iter) { - ResTable_config this_config; - this_config.copyFromDtoH((*iter)->config); - if (this_config.match(configuration_)) { - group.configurations.push_back(this_config); - group.types.push_back(*iter); - } - } - }); - } - } -} - void AssetManager2::InvalidateCaches(uint32_t diff) { if (diff == 0xffffffffu) { // Everything must go. @@ -1027,7 +881,7 @@ ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_type_spec_flags, - uint32_t* out_last_ref) const { + uint32_t* out_last_ref) { if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) { uint32_t new_flags; cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags); diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 1d2c597c4c8c..e08848f891f6 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -44,6 +44,44 @@ namespace android { constexpr const static int kAppPackageId = 0x7f; +// Element of a TypeSpec array. See TypeSpec. +struct Type { + // The configuration for which this type defines entries. + // This is already converted to host endianness. + ResTable_config configuration; + + // Pointer to the mmapped data where entry definitions are kept. + const ResTable_type* type; +}; + +// TypeSpec is going to be immediately proceeded by +// an array of Type structs, all in the same block of memory. +struct TypeSpec { + // Pointer to the mmapped data where flags are kept. + // Flags denote whether the resource entry is public + // and under which configurations it varies. + const ResTable_typeSpec* type_spec; + + // Pointer to the mmapped data where the IDMAP mappings for this type + // exist. May be nullptr if no IDMAP exists. + const IdmapEntry_header* idmap_entries; + + // The number of types that follow this struct. + // There is a type for each configuration + // that entries are defined for. + size_t type_count; + + // Trick to easily access a variable number of Type structs + // proceeding this struct, and to ensure their alignment. + const Type types[0]; +}; + +// TypeSpecPtr points to the block of memory that holds +// a TypeSpec struct, followed by an array of Type structs. +// TypeSpecPtr is a managed pointer that knows how to delete +// itself. +using TypeSpecPtr = util::unique_cptr; + namespace { // Builder that helps accumulate Type structs and then create a single @@ -57,22 +95,21 @@ class TypeSpecPtrBuilder { } void AddType(const ResTable_type* type) { - types_.push_back(type); + ResTable_config config; + config.copyFromDtoH(type->config); + types_.push_back(Type{config, type}); } TypeSpecPtr Build() { // Check for overflow. - using ElementType = const ResTable_type*; - if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(ElementType) < - types_.size()) { + if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) { return {}; } - TypeSpec* type_spec = - (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType))); + TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type))); type_spec->type_spec = header_; type_spec->idmap_entries = idmap_header_; type_spec->type_count = types_.size(); - memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType)); + memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type)); return TypeSpecPtr(type_spec); } @@ -81,7 +118,7 @@ class TypeSpecPtrBuilder { const ResTable_typeSpec* header_; const IdmapEntry_header* idmap_header_; - std::vector types_; + std::vector types_; }; } // namespace @@ -125,17 +162,18 @@ static bool VerifyResTableType(const ResTable_type* header) { return true; } -static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) { +static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset, + size_t entry_idx) { // Check that the offset is aligned. if (entry_offset & 0x03) { - LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned."; + LOG(ERROR) << "Entry offset at index " << entry_idx << " is not 4-byte aligned."; return false; } // Check that the offset doesn't overflow. if (entry_offset > std::numeric_limits::max() - dtohl(type->entriesStart)) { // Overflow in offset. - LOG(ERROR) << "Entry at offset " << entry_offset << " is too large."; + LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large."; return false; } @@ -143,7 +181,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset entry_offset += dtohl(type->entriesStart); if (entry_offset > chunk_size - sizeof(ResTable_entry)) { - LOG(ERROR) << "Entry at offset " << entry_offset + LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large. No room for ResTable_entry."; return false; } @@ -153,13 +191,13 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t entry_size = dtohs(entry->size); if (entry_size < sizeof(*entry)) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset + LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx << " is too small."; return false; } if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset + LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx << " is too large."; return false; } @@ -167,7 +205,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset if (entry_size < sizeof(ResTable_map_entry)) { // There needs to be room for one Res_value struct. if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) { - LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset + LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << entry_idx << " for type " << (int)type->id << "."; return false; } @@ -176,12 +214,12 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset reinterpret_cast(reinterpret_cast(entry) + entry_size); const size_t value_size = dtohs(value->size); if (value_size < sizeof(Res_value)) { - LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small."; + LOG(ERROR) << "Res_value at index " << entry_idx << " is too small."; return false; } if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) { - LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset + LOG(ERROR) << "Res_value size " << value_size << " at index " << entry_idx << " is too large."; return false; } @@ -190,76 +228,117 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t map_entry_count = dtohl(map->count); size_t map_entries_start = entry_offset + entry_size; if (map_entries_start & 0x03) { - LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset."; + LOG(ERROR) << "Map entries at index " << entry_idx << " start at unaligned offset."; return false; } // Each entry is sizeof(ResTable_map) big. if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) { - LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << "."; + LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << entry_idx << "."; return false; } } return true; } -const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk, - uint16_t entry_index) { - uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index); - if (entry_offset == ResTable_type::NO_ENTRY) { - return nullptr; - } - return GetEntryFromOffset(type_chunk, entry_offset); -} - -uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const size_t entry_count = dtohl(type_chunk->entryCount); - const size_t offsets_offset = dtohs(type_chunk->header.headerSize); +bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_idx, + const ResTable_config& config, FindEntryResult* out_entry) const { + const ResTable_config* best_config = nullptr; + const ResTable_type* best_type = nullptr; + uint32_t best_offset = 0; + + for (uint32_t i = 0; i < type_spec_ptr->type_count; i++) { + const Type* type = &type_spec_ptr->types[i]; + const ResTable_type* type_chunk = type->type; + + if (type->configuration.match(config) && + (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const size_t entry_count = dtohl(type_chunk->entryCount); + const size_t offsets_offset = dtohs(type_chunk->header.headerSize); + + // Check if there is the desired entry in this type. + + if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { + // This is encoded as a sparse map, so perform a binary search. + const ResTable_sparseTypeEntry* sparse_indices = + reinterpret_cast( + reinterpret_cast(type_chunk) + offsets_offset); + const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; + const ResTable_sparseTypeEntry* result = + std::lower_bound(sparse_indices, sparse_indices_end, entry_idx, + [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { + return dtohs(entry.idx) < entry_idx; + }); + + if (result == sparse_indices_end || dtohs(result->idx) != entry_idx) { + // No entry found. + continue; + } - // Check if there is the desired entry in this type. + // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as + // the real offset divided by 4. + best_offset = uint32_t{dtohs(result->offset)} * 4u; + } else { + if (entry_idx >= entry_count) { + // This entry cannot be here. + continue; + } - if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { - // This is encoded as a sparse map, so perform a binary search. - const ResTable_sparseTypeEntry* sparse_indices = - reinterpret_cast( + const uint32_t* entry_offsets = reinterpret_cast( reinterpret_cast(type_chunk) + offsets_offset); - const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; - const ResTable_sparseTypeEntry* result = - std::lower_bound(sparse_indices, sparse_indices_end, entry_index, - [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { - return dtohs(entry.idx) < entry_idx; - }); - - if (result == sparse_indices_end || dtohs(result->idx) != entry_index) { - // No entry found. - return ResTable_type::NO_ENTRY; + const uint32_t offset = dtohl(entry_offsets[entry_idx]); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + // There is an entry for this resource, record it. + best_offset = offset; + } + + best_config = &type->configuration; + best_type = type_chunk; } + } - // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as - // the real offset divided by 4. - return uint32_t{dtohs(result->offset)} * 4u; + if (best_type == nullptr) { + return false; } - // This type is encoded as a dense array. - if (entry_index >= entry_count) { - // This entry cannot be here. - return ResTable_type::NO_ENTRY; + if (UNLIKELY(!VerifyResTableEntry(best_type, best_offset, entry_idx))) { + return false; } - const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type_chunk) + offsets_offset); - return dtohl(entry_offsets[entry_index]); + const ResTable_entry* best_entry = reinterpret_cast( + reinterpret_cast(best_type) + best_offset + dtohl(best_type->entriesStart)); + + const uint32_t* flags = reinterpret_cast(type_spec_ptr->type_spec + 1); + out_entry->type_flags = dtohl(flags[entry_idx]); + out_entry->entry = best_entry; + out_entry->config = best_config; + out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1); + out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index)); + return true; } -const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk, - uint32_t offset) { - if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) { - return nullptr; +bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, + FindEntryResult* out_entry) const { + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; + if (UNLIKELY(ptr == nullptr)) { + return false; } - return reinterpret_cast(reinterpret_cast(type_chunk) + - offset + dtohl(type_chunk->entriesStart)); + + // If there is an IDMAP supplied with this package, translate the entry ID. + if (ptr->idmap_entries != nullptr) { + if (!LoadedIdmap::Lookup(ptr->idmap_entries, entry_idx, &entry_idx)) { + // There is no mapping, so the resource is not meant to be in this overlay package. + return false; + } + } + return FindEntry(ptr, entry_idx, config, out_entry); } void LoadedPackage::CollectConfigurations(bool exclude_mipmap, @@ -267,7 +346,7 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, const static std::u16string kMipMap = u"mipmap"; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const TypeSpecPtr& type_spec = type_specs_[i]; + const util::unique_cptr& type_spec = type_specs_[i]; if (type_spec != nullptr) { if (exclude_mipmap) { const int type_idx = type_spec->type_spec->id - 1; @@ -288,11 +367,8 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, } } - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config config; - config.copyFromDtoH((*iter)->config); - out_configs->insert(config); + for (size_t j = 0; j < type_spec->type_count; j++) { + out_configs->insert(type_spec->types[j].configuration); } } } @@ -302,12 +378,10 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set* out char temp_locale[RESTABLE_MAX_LOCALE_LEN]; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const TypeSpecPtr& type_spec = type_specs_[i]; + const util::unique_cptr& type_spec = type_specs_[i]; if (type_spec != nullptr) { - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config configuration; - configuration.copyFromDtoH((*iter)->config); + for (size_t j = 0; j < type_spec->type_count; j++) { + const ResTable_config& configuration = type_spec->types[j].configuration; if (configuration.locale != 0) { configuration.getBcp47Locale(temp_locale, canonicalize); std::string locale(temp_locale); @@ -335,17 +409,17 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - const ResTable_type* type = *iter; - size_t entry_count = dtohl(type->entryCount); + for (size_t ti = 0; ti < type_spec->type_count; ti++) { + const Type* type = &type_spec->types[ti]; + size_t entry_count = dtohl(type->type->entryCount); for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) { const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type) + dtohs(type->header.headerSize)); + reinterpret_cast(type->type) + dtohs(type->type->header.headerSize)); const uint32_t offset = dtohl(entry_offsets[entry_idx]); if (offset != ResTable_type::NO_ENTRY) { - const ResTable_entry* entry = reinterpret_cast( - reinterpret_cast(type) + dtohl(type->entriesStart) + offset); + const ResTable_entry* entry = + reinterpret_cast(reinterpret_cast(type->type) + + dtohl(type->type->entriesStart) + offset); if (dtohl(entry->key.index) == static_cast(key_idx)) { // The package ID will be overridden by the caller (due to runtime assignment of package // IDs for shared libraries). @@ -357,7 +431,8 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } -const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { +const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const { + const uint8_t package_id = get_package_id(resid); for (const auto& loaded_package : packages_) { if (loaded_package->GetPackageId() == package_id) { return loaded_package.get(); @@ -605,6 +680,26 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, return std::move(loaded_package); } +bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, + FindEntryResult* out_entry) const { + ATRACE_CALL(); + + const uint8_t package_id = get_package_id(resid); + const uint8_t type_id = get_type_id(resid); + const uint16_t entry_id = get_entry_id(resid); + + if (UNLIKELY(type_id == 0)) { + LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); + return false; + } + + for (const auto& loaded_package : packages_) { + if (loaded_package->GetPackageId() == package_id) { + return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry); + } + } + return false; +} bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library) { diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index ef08897d997a..b033137b4764 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -69,8 +69,6 @@ struct ResolvedBag { Entry entries[0]; }; -struct FindEntryResult; - // AssetManager2 is the main entry point for accessing assets and resources. // AssetManager2 provides caching of resources retrieved via the underlying ApkAssets. class AssetManager2 { @@ -129,7 +127,7 @@ class AssetManager2 { // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap' // will be excluded from the list. std::set GetResourceConfigurations(bool exclude_system = false, - bool exclude_mipmap = false) const; + bool exclude_mipmap = false); // Returns all the locales for which there are resources defined. This includes resource // locales in all the ApkAssets set for this AssetManager. @@ -138,24 +136,24 @@ class AssetManager2 { // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized // and de-duped in the resulting list. std::set GetResourceLocales(bool exclude_system = false, - bool merge_equivalent_languages = false) const; + bool merge_equivalent_languages = false); // Searches the set of APKs loaded by this AssetManager and opens the first one found located // in the assets/ directory. // `mode` controls how the file is opened. // // NOTE: The loaded APKs are searched in reverse order. - std::unique_ptr Open(const std::string& filename, Asset::AccessMode mode) const; + std::unique_ptr Open(const std::string& filename, Asset::AccessMode mode); // Opens a file within the assets/ directory of the APK specified by `cookie`. // `mode` controls how the file is opened. std::unique_ptr Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) const; + Asset::AccessMode mode); // Opens the directory specified by `dirname`. The result is an AssetDir that is the combination // of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded. // The entries are sorted by their ASCII name. - std::unique_ptr OpenDir(const std::string& dirname) const; + std::unique_ptr OpenDir(const std::string& dirname); // Searches the set of APKs loaded by this AssetManager and opens the first one found. // `mode` controls how the file is opened. @@ -163,24 +161,24 @@ class AssetManager2 { // // NOTE: The loaded APKs are searched in reverse order. std::unique_ptr OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie = nullptr) const; + ApkAssetsCookie* out_cookie = nullptr); // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened. // This is typically used to open a specific AndroidManifest.xml, or a binary XML file // referenced by a resource lookup with GetResource(). std::unique_ptr OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) const; + Asset::AccessMode mode); // Populates the `out_name` parameter with resource name information. // Utf8 strings are preferred, and only if they are unavailable are // the Utf16 variants populated. // Returns false if the resource was not found or the name was missing/corrupt. - bool GetResourceName(uint32_t resid, ResourceName* out_name) const; + bool GetResourceName(uint32_t resid, ResourceName* out_name); // Populates `out_flags` with the bitmask of configuration axis that this resource varies with. // See ResTable_config for the list of configuration axis. // Returns false if the resource was not found. - bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const; + bool GetResourceFlags(uint32_t resid, uint32_t* out_flags); // Finds the resource ID assigned to `resource_name`. // `resource_name` must be of the form '[package:][type/]entry'. @@ -188,7 +186,7 @@ class AssetManager2 { // If no type is specified in `resource_name`, then `fallback_type` is used as the type. // Returns 0x0 if no resource by that name was found. uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {}, - const std::string& fallback_package = {}) const; + const std::string& fallback_package = {}); // Retrieves the best matching resource with ID `resid`. The resource value is filled into // `out_value` and the configuration for the selected value is populated in `out_selected_config`. @@ -201,7 +199,7 @@ class AssetManager2 { // this function logs if the resource was a map/bag type before returning kInvalidCookie. ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) const; + uint32_t* out_flags); // Resolves the resource reference in `in_out_value` if the data type is // Res_value::TYPE_REFERENCE. @@ -217,7 +215,7 @@ class AssetManager2 { // it was not found. ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) const; + uint32_t* out_last_reference); // Retrieves the best matching bag/map resource with ID `resid`. // This method will resolve all parent references for this bag and merge keys with the child. @@ -235,9 +233,9 @@ class AssetManager2 { std::unique_ptr NewTheme(); template - void ForEachPackage(Func func) const { + void ForEachPackage(Func func) { for (const PackageGroup& package_group : package_groups_) { - func(package_group.packages_.front().loaded_package_->GetPackageName(), + func(package_group.packages_.front()->GetPackageName(), package_group.dynamic_ref_table.mAssignedPackageId); } } @@ -262,7 +260,7 @@ class AssetManager2 { // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds. ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, - FindEntryResult* out_entry) const; + FindEntryResult* out_entry); // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. @@ -272,43 +270,13 @@ class AssetManager2 { // bitmask `diff`. void InvalidateCaches(uint32_t diff); - // Triggers the re-construction of lists of types that match the set configuration. - // This should always be called when mutating the AssetManager's configuration or ApkAssets set. - void RebuildFilterList(); - // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector apk_assets_; - // A collection of configurations and their associated ResTable_type that match the current - // AssetManager configuration. - struct FilteredConfigGroup { - std::vector configurations; - std::vector types; - }; - - // Represents an single package. - struct ConfiguredPackage { - // A pointer to the immutable, loaded package info. - const LoadedPackage* loaded_package_; - - // A mutable AssetManager-specific list of configurations that match the AssetManager's - // current configuration. This is used as an optimization to avoid checking every single - // candidate configuration when looking up resources. - ByteBucketArray filtered_configs_; - }; - - // Represents a logical package, which can be made up of many individual packages. Each package - // in a PackageGroup shares the same package name and package ID. struct PackageGroup { - // The set of packages that make-up this group. - std::vector packages_; - - // The cookies associated with each package in the group. They share the same order as - // packages_. + std::vector packages_; std::vector cookies_; - - // A library reference table that contains build-package ID to runtime-package ID mappings. DynamicRefTable dynamic_ref_table; }; @@ -382,7 +350,7 @@ class Theme { ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config = nullptr, uint32_t* in_out_type_spec_flags = nullptr, - uint32_t* out_last_ref = nullptr) const; + uint32_t* out_last_ref = nullptr); private: DISALLOW_COPY_AND_ASSIGN(Theme); diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 35ae5fcd9e7b..1775f5070f4e 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -41,40 +41,33 @@ class DynamicPackageEntry { int package_id = 0; }; -// TypeSpec is going to be immediately proceeded by -// an array of Type structs, all in the same block of memory. -struct TypeSpec { - // Pointer to the mmapped data where flags are kept. - // Flags denote whether the resource entry is public - // and under which configurations it varies. - const ResTable_typeSpec* type_spec; - - // Pointer to the mmapped data where the IDMAP mappings for this type - // exist. May be nullptr if no IDMAP exists. - const IdmapEntry_header* idmap_entries; - - // The number of types that follow this struct. - // There is a type for each configuration that entries are defined for. - size_t type_count; - - // Trick to easily access a variable number of Type structs - // proceeding this struct, and to ensure their alignment. - const ResTable_type* types[0]; - - inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const { - if (entry_index >= dtohl(type_spec->entryCount)) { - return 0u; - } - - const uint32_t* flags = reinterpret_cast(type_spec + 1); - return flags[entry_index]; - } +struct FindEntryResult { + // A pointer to the resource table entry for this resource. + // If the size of the entry is > sizeof(ResTable_entry), it can be cast to + // a ResTable_map_entry and processed as a bag/map. + const ResTable_entry* entry; + + // The configuration for which the resulting entry was defined. This points to a structure that + // is already swapped to host endianness. + const ResTable_config* config; + + // The bitmask of configuration axis with which the resource value varies. + uint32_t type_flags; + + // The dynamic package ID map for the package from which this resource came from. + const DynamicRefTable* dynamic_ref_table; + + // The string pool reference to the type's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef type_string_ref; + + // The string pool reference to the entry's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef entry_string_ref; }; -// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of -// ResTable_type pointers. -// TypeSpecPtr is a managed pointer that knows how to delete itself. -using TypeSpecPtr = util::unique_cptr; +struct TypeSpec; +class LoadedArsc; class LoadedPackage { public: @@ -84,6 +77,9 @@ class LoadedPackage { ~LoadedPackage(); + bool FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, + FindEntryResult* out_entry) const; + // Finds the entry with the specified type name and entry name. The names are in UTF-16 because // the underlying ResStringPool API expects this. For now this is acceptable, but since // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. @@ -91,12 +87,6 @@ class LoadedPackage { // for patching the correct package ID to the resource ID. uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; - static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index); - - static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index); - - static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset); - // Returns the string pool where type names are stored. inline const ResStringPool* GetTypeStringPool() const { return &type_string_pool_; @@ -146,32 +136,14 @@ class LoadedPackage { // before being inserted into the set. This may cause some equivalent locales to de-dupe. void CollectLocales(bool canonicalize, std::set* out_locales) const; - // type_idx is TT - 1 from 0xPPTTEEEE. - inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const { - // If the type IDs are offset in this package, we need to take that into account when searching - // for a type. - return type_specs_[type_index - type_id_offset_].get(); - } - - template - void ForEachTypeSpec(Func f) const { - for (size_t i = 0; i < type_specs_.size(); i++) { - const TypeSpecPtr& ptr = type_specs_[i]; - if (ptr != nullptr) { - uint8_t type_id = ptr->type_spec->id; - if (ptr->idmap_entries != nullptr) { - type_id = ptr->idmap_entries->target_type_id; - } - f(ptr.get(), type_id - 1); - } - } - } - private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); LoadedPackage(); + bool FindEntry(const util::unique_cptr& type_spec_ptr, uint16_t entry_idx, + const ResTable_config& config, FindEntryResult* out_entry) const; + ResStringPool type_string_pool_; ResStringPool key_string_pool_; std::string package_name_; @@ -181,7 +153,7 @@ class LoadedPackage { bool system_ = false; bool overlay_ = false; - ByteBucketArray type_specs_; + ByteBucketArray> type_specs_; std::vector dynamic_package_map_; }; @@ -209,20 +181,25 @@ class LoadedArsc { return &global_string_pool_; } - // Gets a pointer to the package with the specified package ID, or nullptr if no such package - // exists. - const LoadedPackage* GetPackageById(uint8_t package_id) const; + // Finds the resource with ID `resid` with the best value for configuration `config`. + // The parameter `out_entry` will be filled with the resulting resource entry. + // The resource entry can be a simple entry (ResTable_entry) or a complex bag + // (ResTable_entry_map). + bool FindEntry(uint32_t resid, const ResTable_config& config, FindEntryResult* out_entry) const; - // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. - inline const std::vector>& GetPackages() const { - return packages_; - } + // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist. + const LoadedPackage* GetPackageForId(uint32_t resid) const; // Returns true if this is a system provided resource. inline bool IsSystem() const { return system_; } + // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. + inline const std::vector>& GetPackages() const { + return packages_; + } + private: DISALLOW_COPY_AND_ASSIGN(LoadedArsc); diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index e2b9f0040989..6c43a67e602f 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -26,56 +26,58 @@ using ::android::base::unique_fd; using ::com::android::basic::R; -using ::testing::Eq; -using ::testing::Ge; -using ::testing::NotNull; -using ::testing::SizeIs; -using ::testing::StrEq; namespace android { TEST(ApkAssetsTest, LoadApk) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); - ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); + ASSERT_NE(nullptr, loaded_arsc); + + const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); + ASSERT_NE(nullptr, loaded_package); + + std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); + ASSERT_NE(nullptr, asset); } TEST(ApkAssetsTest, LoadApkFromFd) { const std::string path = GetTestDataPath() + "/basic/basic.apk"; unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY)); - ASSERT_THAT(fd.get(), Ge(0)); + ASSERT_GE(fd.get(), 0); std::unique_ptr loaded_apk = ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); - ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); + ASSERT_NE(nullptr, loaded_arsc); + + const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); + ASSERT_NE(nullptr, loaded_package); + + std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); + ASSERT_NE(nullptr, asset); } TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_THAT(loaded_apk, NotNull()); - + ASSERT_NE(nullptr, loaded_apk); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); + ASSERT_NE(nullptr, loaded_arsc); + ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic()); loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_THAT(loaded_arsc, NotNull()); - ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); + ASSERT_NE(nullptr, loaded_arsc); + ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic()); } @@ -84,22 +86,19 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { ResTable target_table; const std::string target_path = GetTestDataPath() + "/basic/basic.apk"; ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents)); - ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/), - Eq(NO_ERROR)); + ASSERT_EQ(NO_ERROR, target_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); ResTable overlay_table; const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk"; ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents)); - ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/), - Eq(NO_ERROR)); + ASSERT_EQ(NO_ERROR, overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); util::unique_cptr idmap_data; void* temp_data; size_t idmap_len; - ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), - overlay_path.c_str(), &temp_data, &idmap_len), - Eq(NO_ERROR)); + ASSERT_EQ(NO_ERROR, target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), + overlay_path.c_str(), &temp_data, &idmap_len)); idmap_data.reset(temp_data); TemporaryFile tf; @@ -109,30 +108,37 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { // Open something so that the destructor of TemporaryFile closes a valid fd. tf.fd = open("/dev/null", O_WRONLY); - ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull()); + std::unique_ptr loaded_overlay_apk = ApkAssets::LoadOverlay(tf.path); + ASSERT_NE(nullptr, loaded_overlay_apk); } TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); - { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } + { + std::unique_ptr assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); + ASSERT_NE(nullptr, assets); + } - { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } + { + std::unique_ptr assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); + ASSERT_NE(nullptr, assets); + } } TEST(ApkAssetsTest, OpenUncompressedAssetFd) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_THAT(loaded_apk, NotNull()); + ASSERT_NE(nullptr, loaded_apk); auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN); - ASSERT_THAT(asset, NotNull()); + ASSERT_NE(nullptr, asset); off64_t start, length; unique_fd fd(asset->openFileDescriptor(&start, &length)); - ASSERT_THAT(fd.get(), Ge(0)); + EXPECT_GE(fd.get(), 0); lseek64(fd.get(), start, SEEK_SET); @@ -140,7 +146,7 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) { buffer.resize(length); ASSERT_TRUE(base::ReadFully(fd.get(), &*buffer.begin(), length)); - EXPECT_THAT(buffer, StrEq("This should be uncompressed.\n\n")); + EXPECT_EQ("This should be uncompressed.\n\n", buffer); } } // namespace android diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index bedebd66cb2f..37ddafb14fd3 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -16,8 +16,6 @@ #include "androidfw/LoadedArsc.h" -#include "androidfw/ResourceUtils.h" - #include "TestHelpers.h" #include "data/basic/R.h" #include "data/libclient/R.h" @@ -29,13 +27,6 @@ namespace basic = com::android::basic; namespace libclient = com::android::libclient; namespace sparse = com::android::sparse; -using ::testing::Eq; -using ::testing::Ge; -using ::testing::IsNull; -using ::testing::NotNull; -using ::testing::SizeIs; -using ::testing::StrEq; - namespace android { TEST(LoadedArscTest, LoadSinglePackageArsc) { @@ -44,24 +35,39 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); + + const std::vector>& packages = loaded_arsc->GetPackages(); + ASSERT_EQ(1u, packages.size()); + EXPECT_EQ(std::string("com.android.app"), packages[0]->GetPackageName()); + EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.sdkVersion = 24; + + FindEntryResult entry; - const LoadedPackage* package = - loaded_arsc->GetPackageById(get_package_id(app::R::string::string_one)); - ASSERT_THAT(package, NotNull()); - EXPECT_THAT(package->GetPackageName(), StrEq("com.android.app")); - EXPECT_THAT(package->GetPackageId(), Eq(0x7f)); + ASSERT_TRUE(loaded_arsc->FindEntry(app::R::string::string_one, config, &entry)); + ASSERT_NE(nullptr, entry.entry); +} - const uint8_t type_index = get_type_id(app::R::string::string_one) - 1; - const uint16_t entry_index = get_entry_id(app::R::string::string_one); +TEST(LoadedArscTest, FindDefaultEntry) { + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); + std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + ASSERT_NE(nullptr, loaded_arsc); - const ResTable_type* type = type_spec->types[0]; - ASSERT_THAT(type, NotNull()); - ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + desired_config.language[0] = 'd'; + desired_config.language[1] = 'e'; + + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry)); + ASSERT_NE(nullptr, entry.entry); } TEST(LoadedArscTest, LoadSparseEntryApp) { @@ -70,22 +76,15 @@ TEST(LoadedArscTest, LoadSparseEntryApp) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); - - const LoadedPackage* package = - loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9)); - ASSERT_THAT(package, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); - const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1; - const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9); + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.sdkVersion = 26; - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - - const ResTable_type* type = type_spec->types[0]; - ASSERT_THAT(type, NotNull()); - ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(sparse::R::integer::foo_9, config, &entry)); + ASSERT_NE(nullptr, entry.entry); } TEST(LoadedArscTest, LoadSharedLibrary) { @@ -94,13 +93,14 @@ TEST(LoadedArscTest, LoadSharedLibrary) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_THAT(packages, SizeIs(1u)); + ASSERT_EQ(1u, packages.size()); + EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.lib_one")); - EXPECT_THAT(packages[0]->GetPackageId(), Eq(0)); + EXPECT_EQ(std::string("com.android.lib_one"), packages[0]->GetPackageName()); + EXPECT_EQ(0, packages[0]->GetPackageId()); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); @@ -114,23 +114,25 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) { "resources.arsc", &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_THAT(packages, SizeIs(1u)); + ASSERT_EQ(1u, packages.size()); + EXPECT_FALSE(packages[0]->IsDynamic()); - EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.libclient")); - EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); + EXPECT_EQ(std::string("com.android.libclient"), packages[0]->GetPackageName()); + EXPECT_EQ(0x7f, packages[0]->GetPackageId()); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); // The library has two dependencies. - ASSERT_THAT(dynamic_pkg_map, SizeIs(2u)); - EXPECT_THAT(dynamic_pkg_map[0].package_name, StrEq("com.android.lib_one")); - EXPECT_THAT(dynamic_pkg_map[0].package_id, Eq(0x02)); + ASSERT_EQ(2u, dynamic_pkg_map.size()); - EXPECT_THAT(dynamic_pkg_map[1].package_name, StrEq("com.android.lib_two")); - EXPECT_THAT(dynamic_pkg_map[1].package_id, Eq(0x03)); + EXPECT_EQ(std::string("com.android.lib_one"), dynamic_pkg_map[0].package_name); + EXPECT_EQ(0x02, dynamic_pkg_map[0].package_id); + + EXPECT_EQ(std::string("com.android.lib_two"), dynamic_pkg_map[1].package_name); + EXPECT_EQ(0x03, dynamic_pkg_map[1].package_id); } TEST(LoadedArscTest, LoadAppAsSharedLibrary) { @@ -141,12 +143,13 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, true /*load_as_shared_library*/); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_THAT(packages, SizeIs(1u)); + ASSERT_EQ(1u, packages.size()); + EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); + EXPECT_EQ(0x7f, packages[0]->GetPackageId()); } TEST(LoadedArscTest, LoadFeatureSplit) { @@ -154,27 +157,21 @@ TEST(LoadedArscTest, LoadFeatureSplit) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc", &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_NE(nullptr, loaded_arsc); - const LoadedPackage* package = - loaded_arsc->GetPackageById(get_package_id(basic::R::string::test3)); - ASSERT_THAT(package, NotNull()); + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); - uint8_t type_index = get_type_id(basic::R::string::test3) - 1; - uint8_t entry_index = get_entry_id(basic::R::string::test3); - - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry)); size_t len; - const char16_t* type_name16 = - package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len); - ASSERT_THAT(type_name16, NotNull()); - EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string")); + const char16_t* type_name16 = entry.type_string_ref.string16(&len); + ASSERT_NE(nullptr, type_name16); + ASSERT_NE(0u, len); - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull()); + std::string type_name = util::Utf16ToUtf8(StringPiece16(type_name16, len)); + EXPECT_EQ(std::string("string"), type_name); } class MockLoadedIdmap : public LoadedIdmap { @@ -202,33 +199,23 @@ class MockLoadedIdmap : public LoadedIdmap { }; TEST(LoadedArscTest, LoadOverlay) { - std::string contents; + std::string contents, overlay_contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc", - &contents)); + &overlay_contents)); MockLoadedIdmap loaded_idmap; std::unique_ptr loaded_arsc = - LoadedArsc::Load(StringPiece(contents), &loaded_idmap); - ASSERT_THAT(loaded_arsc, NotNull()); - - const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u); - ASSERT_THAT(package, NotNull()); - - const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1); - ASSERT_THAT(type_spec, NotNull()); - ASSERT_THAT(type_spec->type_count, Ge(1u)); - ASSERT_THAT(type_spec->types[0], NotNull()); - - // The entry being overlaid doesn't exist at the original entry index. - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull()); - - // Since this is an overlay, the actual entry ID must be mapped. - ASSERT_THAT(type_spec->idmap_entries, NotNull()); - uint16_t target_entry_id = 0u; - ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id)); - ASSERT_THAT(target_entry_id, Eq(0x0u)); - ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull()); + LoadedArsc::Load(StringPiece(overlay_contents), &loaded_idmap); + ASSERT_NE(nullptr, loaded_arsc); + + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + + FindEntryResult entry; + ASSERT_TRUE(loaded_arsc->FindEntry(0x08030001u, desired_config, &entry)); } // structs with size fields (like Res_value, ResTable_entry) should be diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h index df0c642f4565..43a995536d89 100644 --- a/libs/androidfw/tests/TestHelpers.h +++ b/libs/androidfw/tests/TestHelpers.h @@ -20,7 +20,6 @@ #include #include "androidfw/ResourceTypes.h" -#include "gmock/gmock.h" #include "gtest/gtest.h" #include "CommonHelpers.h" -- cgit v1.2.3-59-g8ed1b From bde1df21adf264d3398b9f3274f353faa6399008 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Fri, 9 Feb 2018 11:12:22 -0800 Subject: Revert "Replace AssetManager with AssetManager2 implementation" This reverts commit 1187590da38457809dd368d4901c9c47ac5a6958. Bug: 73134570 Change-Id: I59b4d714e447478ea124f086356f127f42251fb7 --- config/hiddenapi-light-greylist.txt | 16 + core/java/android/content/pm/PackageParser.java | 19 +- core/java/android/content/res/ApkAssets.java | 221 -- core/java/android/content/res/AssetManager.java | 1341 ++++----- core/java/android/content/res/Resources.java | 9 +- core/java/android/content/res/ResourcesImpl.java | 22 +- core/java/android/content/res/TypedArray.java | 185 +- core/java/android/content/res/XmlBlock.java | 8 +- core/jni/Android.bp | 3 +- core/jni/AndroidRuntime.cpp | 2 - core/jni/android/graphics/FontFamily.cpp | 29 +- core/jni/android_app_NativeActivity.cpp | 2 +- core/jni/android_content_res_ApkAssets.cpp | 150 - core/jni/android_util_AssetManager.cpp | 2892 +++++++++++--------- .../android_runtime/android_util_AssetManager.h | 15 +- libs/androidfw/AssetManager2.cpp | 6 +- libs/androidfw/AttributeResolution.cpp | 263 +- libs/androidfw/LoadedArsc.cpp | 2 + libs/androidfw/include/androidfw/AttributeFinder.h | 6 - .../include/androidfw/AttributeResolution.h | 11 +- libs/androidfw/include/androidfw/LoadedArsc.h | 13 +- libs/androidfw/include/androidfw/MutexGuard.h | 101 - libs/androidfw/tests/AttributeResolution_test.cpp | 39 +- libs/androidfw/tests/BenchmarkHelpers.cpp | 4 +- native/android/asset_manager.cpp | 28 +- rs/jni/android_renderscript_RenderScript.cpp | 30 +- 26 files changed, 2515 insertions(+), 2902 deletions(-) delete mode 100644 core/java/android/content/res/ApkAssets.java delete mode 100644 core/jni/android_content_res_ApkAssets.cpp delete mode 100644 libs/androidfw/include/androidfw/MutexGuard.h (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 71de479b5530..279bc1194ef4 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -282,13 +282,28 @@ Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;IZ)Landroid/conte Landroid/content/pm/UserInfo;->id:I Landroid/content/pm/UserInfo;->isPrimary()Z Landroid/content/res/AssetManager;->addAssetPath(Ljava/lang/String;)I +Landroid/content/res/AssetManager;->addAssetPaths([Ljava/lang/String;)[I +Landroid/content/res/AssetManager;->applyStyle(JIIJ[IIJJ)V +Landroid/content/res/AssetManager;->getArraySize(I)I Landroid/content/res/AssetManager;->getAssignedPackageIdentifiers()Landroid/util/SparseArray; +Landroid/content/res/AssetManager;->getCookieName(I)Ljava/lang/String; Landroid/content/res/AssetManager;->getResourceBagText(II)Ljava/lang/CharSequence; +Landroid/content/res/AssetManager;->loadResourceBagValue(IILandroid/util/TypedValue;Z)I +Landroid/content/res/AssetManager;->loadResourceValue(ISLandroid/util/TypedValue;Z)I +Landroid/content/res/AssetManager;->loadThemeAttributeValue(JILandroid/util/TypedValue;Z)I Landroid/content/res/AssetManager;->mObject:J +Landroid/content/res/AssetManager;->openNonAssetFdNative(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor; Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;I)Ljava/io/InputStream; Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;)Ljava/io/InputStream; Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;I)Ljava/io/InputStream; Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;)Ljava/io/InputStream; +Landroid/content/res/AssetManager;->openNonAssetNative(ILjava/lang/String;I)J +Landroid/content/res/AssetManager;->openXmlAssetNative(ILjava/lang/String;)J +Landroid/content/res/AssetManager;->resolveAttrs(JII[I[I[I[I)Z +Landroid/content/res/AssetManager;->retrieveArray(I[I)I +Landroid/content/res/AssetManager;->retrieveAttributes(J[I[I[I)Z +Landroid/content/res/AssetManager;->STYLE_NUM_ENTRIES:I +Landroid/content/res/AssetManager;->STYLE_RESOURCE_ID:I Landroid/content/res/DrawableCache;->getInstance(JLandroid/content/res/Resources;Landroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable; Landroid/content/res/Resources;->getCompatibilityInfo()Landroid/content/res/CompatibilityInfo; Landroid/content/res/ResourcesImpl;->mAccessLock:Ljava/lang/Object; @@ -318,6 +333,7 @@ Landroid/content/res/TypedArray;->mResources:Landroid/content/res/Resources; Landroid/content/res/TypedArray;->mTheme:Landroid/content/res/Resources$Theme; Landroid/content/res/TypedArray;->mValue:Landroid/util/TypedValue; Landroid/content/res/TypedArray;->mXml:Landroid/content/res/XmlBlock$Parser; +Landroid/content/res/XmlBlock;->close()V Landroid/content/res/XmlBlock;->newParser()Landroid/content/res/XmlResourceParser; Landroid/content/res/XmlBlock$Parser;->mParseState:J Landroid/content/SyncStatusInfo;->lastSuccessTime:J diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index eff12a924fe0..dda4167d3c3b 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -54,7 +54,6 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper; import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.SplitAssetDependencyLoader; import android.content.pm.split.SplitAssetLoader; -import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -1593,19 +1592,21 @@ public class PackageParser { int flags) throws PackageParserException { final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); - ApkAssets apkAssets = null; + AssetManager assets = null; XmlResourceParser parser = null; try { - try { - apkAssets = fd != null - ? ApkAssets.loadFromFd(fd, debugPathName, false, false) - : ApkAssets.loadFromPath(apkPath); - } catch (IOException e) { + assets = newConfiguredAssetManager(); + int cookie = fd != null + ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath); + if (cookie == 0) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse " + apkPath); } - parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME); + final DisplayMetrics metrics = new DisplayMetrics(); + metrics.setToDefaults(); + + parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); final SigningDetails signingDetails; if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) { @@ -1632,7 +1633,7 @@ public class PackageParser { "Failed to parse " + apkPath, e); } finally { IoUtils.closeQuietly(parser); - IoUtils.closeQuietly(apkAssets); + IoUtils.closeQuietly(assets); } } diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java deleted file mode 100644 index b087c48d8d4c..000000000000 --- a/core/java/android/content/res/ApkAssets.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.content.res; - -import android.annotation.NonNull; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.Preconditions; - -import java.io.FileDescriptor; -import java.io.IOException; - -/** - * The loaded, immutable, in-memory representation of an APK. - * - * The main implementation is native C++ and there is very little API surface exposed here. The APK - * is mainly accessed via {@link AssetManager}. - * - * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers, - * making the creation of AssetManagers very cheap. - * @hide - */ -public final class ApkAssets implements AutoCloseable { - @GuardedBy("this") private long mNativePtr; - @GuardedBy("this") private StringBlock mStringBlock; - - /** - * Creates a new ApkAssets instance from the given path on disk. - * - * @param path The path to an APK on disk. - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException { - return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/); - } - - /** - * Creates a new ApkAssets instance from the given path on disk. - * - * @param path The path to an APK on disk. - * @param system When true, the APK is loaded as a system APK (framework). - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system) - throws IOException { - return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/); - } - - /** - * Creates a new ApkAssets instance from the given path on disk. - * - * @param path The path to an APK on disk. - * @param system When true, the APK is loaded as a system APK (framework). - * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are - * loaded as a shared library. - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system, - boolean forceSharedLibrary) throws IOException { - return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/); - } - - /** - * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications. - * - * Performs a dup of the underlying fd, so you must take care of still closing - * the FileDescriptor yourself (and can do that whenever you want). - * - * @param fd The FileDescriptor of an open, readable APK. - * @param friendlyName The friendly name used to identify this ApkAssets when logging. - * @param system When true, the APK is loaded as a system APK (framework). - * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are - * loaded as a shared library. - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd, - @NonNull String friendlyName, boolean system, boolean forceSharedLibrary) - throws IOException { - return new ApkAssets(fd, friendlyName, system, forceSharedLibrary); - } - - /** - * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path - * is encoded within the IDMAP. - * - * @param idmapPath Path to the IDMAP of an overlay APK. - * @param system When true, the APK is loaded as a system APK (framework). - * @return a new instance of ApkAssets. - * @throws IOException if a disk I/O error or parsing error occurred. - */ - public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system) - throws IOException { - return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/); - } - - private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) - throws IOException { - Preconditions.checkNotNull(path, "path"); - mNativePtr = nativeLoad(path, system, forceSharedLib, overlay); - mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); - } - - private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system, - boolean forceSharedLib) throws IOException { - Preconditions.checkNotNull(fd, "fd"); - Preconditions.checkNotNull(friendlyName, "friendlyName"); - mNativePtr = nativeLoadFromFd(fd, friendlyName, system, forceSharedLib); - mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); - } - - @NonNull String getAssetPath() { - synchronized (this) { - ensureValidLocked(); - return nativeGetAssetPath(mNativePtr); - } - } - - CharSequence getStringFromPool(int idx) { - synchronized (this) { - ensureValidLocked(); - return mStringBlock.get(idx); - } - } - - /** - * Retrieve a parser for a compiled XML file. This is associated with a single APK and - * NOT a full AssetManager. This means that shared-library references will not be - * dynamically assigned runtime package IDs. - * - * @param fileName The path to the file within the APK. - * @return An XmlResourceParser. - * @throws IOException if the file was not found or an error occurred retrieving it. - */ - public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException { - Preconditions.checkNotNull(fileName, "fileName"); - synchronized (this) { - ensureValidLocked(); - long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName); - try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) { - XmlResourceParser parser = block.newParser(); - // If nativeOpenXml doesn't throw, it will always return a valid native pointer, - // which makes newParser always return non-null. But let's be paranoid. - if (parser == null) { - throw new AssertionError("block.newParser() returned a null parser"); - } - return parser; - } - } - } - - /** - * Returns false if the underlying APK was changed since this ApkAssets was loaded. - */ - public boolean isUpToDate() { - synchronized (this) { - ensureValidLocked(); - return nativeIsUpToDate(mNativePtr); - } - } - - /** - * Closes the ApkAssets and destroys the underlying native implementation. Further use of the - * ApkAssets object will cause exceptions to be thrown. - * - * Calling close on an already closed ApkAssets does nothing. - */ - @Override - public void close() { - synchronized (this) { - if (mNativePtr == 0) { - return; - } - - mStringBlock = null; - nativeDestroy(mNativePtr); - mNativePtr = 0; - } - } - - @Override - protected void finalize() throws Throwable { - if (mNativePtr != 0) { - nativeDestroy(mNativePtr); - } - } - - private void ensureValidLocked() { - if (mNativePtr == 0) { - throw new RuntimeException("ApkAssets is closed"); - } - } - - private static native long nativeLoad( - @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) - throws IOException; - private static native long nativeLoadFromFd(@NonNull FileDescriptor fd, - @NonNull String friendlyName, boolean system, boolean forceSharedLib) - throws IOException; - private static native void nativeDestroy(long ptr); - private static native @NonNull String nativeGetAssetPath(long ptr); - private static native long nativeGetStringBlock(long ptr); - private static native boolean nativeIsUpToDate(long ptr); - private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException; -} diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 4bd6edc43541..5f8a34d46ecd 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -18,11 +18,9 @@ package android.content.res; import android.annotation.AnyRes; import android.annotation.ArrayRes; -import android.annotation.AttrRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; -import android.annotation.StyleRes; import android.content.pm.ActivityInfo; import android.content.res.Configuration.NativeConfig; import android.os.ParcelFileDescriptor; @@ -31,19 +29,11 @@ import android.util.SparseArray; import android.util.TypedValue; import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.Preconditions; -import libcore.io.IoUtils; - -import java.io.BufferedReader; -import java.io.FileInputStream; +import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.channels.FileLock; -import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; /** @@ -54,17 +44,7 @@ import java.util.HashMap; * bytes. */ public final class AssetManager implements AutoCloseable { - private static final String TAG = "AssetManager"; - private static final boolean DEBUG_REFS = false; - - private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk"; - - private static final Object sSync = new Object(); - - // Not private for LayoutLib's BridgeAssetManager. - @GuardedBy("sSync") static AssetManager sSystem = null; - - @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0]; + /* modes used when opening an asset */ /** * Mode for {@link #open(String, int)}: no specific information about how @@ -87,303 +67,87 @@ public final class AssetManager implements AutoCloseable { */ public static final int ACCESS_BUFFER = 3; - @GuardedBy("this") private final TypedValue mValue = new TypedValue(); - @GuardedBy("this") private final long[] mOffsets = new long[2]; - - // Pointer to native implementation, stuffed inside a long. - @GuardedBy("this") private long mObject; - - // The loaded asset paths. - @GuardedBy("this") private ApkAssets[] mApkAssets; + private static final String TAG = "AssetManager"; + private static final boolean localLOGV = false || false; + + private static final boolean DEBUG_REFS = false; + + private static final Object sSync = new Object(); + /*package*/ static AssetManager sSystem = null; - // Debug/reference counting implementation. - @GuardedBy("this") private boolean mOpen = true; - @GuardedBy("this") private int mNumRefs = 1; - @GuardedBy("this") private HashMap mRefStacks; + private final TypedValue mValue = new TypedValue(); + private final long[] mOffsets = new long[2]; + + // For communication with native code. + private long mObject; + private StringBlock mStringBlocks[] = null; + + private int mNumRefs = 1; + private boolean mOpen = true; + private HashMap mRefStacks; + /** * Create a new AssetManager containing only the basic system assets. * Applications will not generally use this method, instead retrieving the * appropriate asset manager with {@link Resources#getAssets}. Not for * use by applications. - * @hide + * {@hide} */ public AssetManager() { - final ApkAssets[] assets; - synchronized (sSync) { - createSystemAssetsInZygoteLocked(); - assets = sSystemApkAssets; - } - - mObject = nativeCreate(); - if (DEBUG_REFS) { - mNumRefs = 0; - incRefsLocked(hashCode()); - } - - // Always set the framework resources. - setApkAssets(assets, false /*invalidateCaches*/); - } - - /** - * Private constructor that doesn't call ensureSystemAssets. - * Used for the creation of system assets. - */ - @SuppressWarnings("unused") - private AssetManager(boolean sentinel) { - mObject = nativeCreate(); - if (DEBUG_REFS) { - mNumRefs = 0; - incRefsLocked(hashCode()); + synchronized (this) { + if (DEBUG_REFS) { + mNumRefs = 0; + incRefsLocked(this.hashCode()); + } + init(false); + if (localLOGV) Log.v(TAG, "New asset manager: " + this); + ensureSystemAssets(); } } - /** - * This must be called from Zygote so that system assets are shared by all applications. - */ - @GuardedBy("sSync") - private static void createSystemAssetsInZygoteLocked() { - if (sSystem != null) { - return; - } - - // Make sure that all IDMAPs are up to date. - nativeVerifySystemIdmaps(); - - try { - ArrayList apkAssets = new ArrayList<>(); - apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/)); - loadStaticRuntimeOverlays(apkAssets); - - sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]); - sSystem = new AssetManager(true /*sentinel*/); - sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/); - } catch (IOException e) { - throw new IllegalStateException("Failed to create system AssetManager", e); + private static void ensureSystemAssets() { + synchronized (sSync) { + if (sSystem == null) { + AssetManager system = new AssetManager(true); + system.makeStringBlocks(null); + sSystem = system; + } } } - - /** - * Loads the static runtime overlays declared in /data/resource-cache/overlays.list. - * Throws an exception if the file is corrupt or if loading the APKs referenced by the file - * fails. Returns quietly if the overlays.list file doesn't exist. - * @param outApkAssets The list to fill with the loaded ApkAssets. - */ - private static void loadStaticRuntimeOverlays(ArrayList outApkAssets) - throws IOException { - final FileInputStream fis; - try { - fis = new FileInputStream("/data/resource-cache/overlays.list"); - } catch (FileNotFoundException e) { - // We might not have any overlays, this is fine. We catch here since ApkAssets - // loading can also fail with the same exception, which we would want to propagate. - Log.i(TAG, "no overlays.list file found"); - return; - } - - try { - // Acquire a lock so that any idmap scanning doesn't impact the current set. - // The order of this try-with-resources block matters. We must release the lock, and - // then close the file streams when exiting the block. - try (final BufferedReader br = new BufferedReader(new InputStreamReader(fis)); - final FileLock flock = fis.getChannel().lock(0, Long.MAX_VALUE, true /*shared*/)) { - for (String line; (line = br.readLine()) != null; ) { - final String idmapPath = line.split(" ")[1]; - outApkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/)); - } + + private AssetManager(boolean isSystem) { + if (DEBUG_REFS) { + synchronized (this) { + mNumRefs = 0; + incRefsLocked(this.hashCode()); } - } finally { - // When BufferedReader is closed above, FileInputStream is closed as well. But let's be - // paranoid. - IoUtils.closeQuietly(fis); } + init(true); + if (localLOGV) Log.v(TAG, "New asset manager: " + this); } /** * Return a global shared asset manager that provides access to only * system assets (no application assets). - * @hide + * {@hide} */ public static AssetManager getSystem() { - synchronized (sSync) { - createSystemAssetsInZygoteLocked(); - return sSystem; - } + ensureSystemAssets(); + return sSystem; } /** * Close this asset manager. */ - @Override public void close() { - synchronized (this) { - if (!mOpen) { - return; + synchronized(this) { + //System.out.println("Release: num=" + mNumRefs + // + ", released=" + mReleased); + if (mOpen) { + mOpen = false; + decRefsLocked(this.hashCode()); } - - mOpen = false; - decRefsLocked(hashCode()); - } - } - - /** - * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)} - * family of methods. - * - * @param apkAssets The new set of paths. - * @param invalidateCaches Whether to invalidate any caches. This should almost always be true. - * Set this to false if you are appending new resources - * (not new configurations). - * @hide - */ - public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) { - Preconditions.checkNotNull(apkAssets, "apkAssets"); - synchronized (this) { - ensureValidLocked(); - mApkAssets = apkAssets; - nativeSetApkAssets(mObject, apkAssets, invalidateCaches); - if (invalidateCaches) { - // Invalidate all caches. - invalidateCachesLocked(-1); - } - } - } - - /** - * Invalidates the caches in this AssetManager according to the bitmask `diff`. - * - * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}. - * @see ActivityInfo.Config - */ - private void invalidateCachesLocked(int diff) { - // TODO(adamlesinski): Currently there are no caches to invalidate in Java code. - } - - /** - * @hide - */ - public @NonNull ApkAssets[] getApkAssets() { - synchronized (this) { - ensureValidLocked(); - return mApkAssets; - } - } - - /** - * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} - * @hide - */ - @Deprecated - public int addAssetPath(String path) { - return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/); - } - - /** - * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} - * @hide - */ - @Deprecated - public int addAssetPathAsSharedLibrary(String path) { - return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/); - } - - /** - * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} - * @hide - */ - @Deprecated - public int addOverlayPath(String path) { - return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/); - } - - private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) { - Preconditions.checkNotNull(path, "path"); - synchronized (this) { - ensureOpenLocked(); - final int count = mApkAssets.length; - for (int i = 0; i < count; i++) { - if (mApkAssets[i].getAssetPath().equals(path)) { - return i + 1; - } - } - - final ApkAssets assets; - try { - if (overlay) { - // TODO(b/70343104): This hardcoded path will be removed once - // addAssetPathInternal is deleted. - final String idmapPath = "/data/resource-cache/" - + path.substring(1).replace('/', '@') - + "@idmap"; - assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/); - } else { - assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib); - } - } catch (IOException e) { - return 0; - } - - final ApkAssets[] newApkAssets = Arrays.copyOf(mApkAssets, count + 1); - newApkAssets[count] = assets; - setApkAssets(newApkAssets, true); - return count + 1; - } - } - - /** - * Ensures that the native implementation has not been destroyed. - * The AssetManager may have been closed, but references to it still exist - * and therefore the native implementation is not destroyed. - */ - @GuardedBy("this") - private void ensureValidLocked() { - if (mObject == 0) { - throw new RuntimeException("AssetManager has been destroyed"); - } - } - - /** - * Ensures that the AssetManager has not been explicitly closed. If this method passes, - * then this implies that ensureValidLocked() also passes. - */ - @GuardedBy("this") - private void ensureOpenLocked() { - if (!mOpen) { - throw new RuntimeException("AssetManager has been closed"); - } - } - - /** - * Populates {@code outValue} with the data associated a particular - * resource identifier for the current configuration. - * - * @param resId the resource identifier to load - * @param densityDpi the density bucket for which to load the resource - * @param outValue the typed value in which to put the data - * @param resolveRefs {@code true} to resolve references, {@code false} - * to leave them unresolved - * @return {@code true} if the data was loaded into {@code outValue}, - * {@code false} otherwise - */ - boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, - boolean resolveRefs) { - Preconditions.checkNotNull(outValue, "outValue"); - synchronized (this) { - ensureValidLocked(); - final int cookie = nativeGetResourceValue( - mObject, resId, (short) densityDpi, outValue, resolveRefs); - if (cookie <= 0) { - return false; - } - - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); - - if (outValue.type == TypedValue.TYPE_STRING) { - outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); - } - return true; } } @@ -394,7 +158,8 @@ public final class AssetManager implements AutoCloseable { * @param resId the resource identifier to load * @return the string value, or {@code null} */ - @Nullable CharSequence getResourceText(@StringRes int resId) { + @Nullable + final CharSequence getResourceText(@StringRes int resId) { synchronized (this) { final TypedValue outValue = mValue; if (getResourceValue(resId, 0, outValue, true)) { @@ -409,15 +174,15 @@ public final class AssetManager implements AutoCloseable { * identifier for the current configuration. * * @param resId the resource identifier to load - * @param bagEntryId the index into the bag to load + * @param bagEntryId * @return the string value, or {@code null} */ - @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { + @Nullable + final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { synchronized (this) { - ensureValidLocked(); final TypedValue outValue = mValue; - final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue); - if (cookie <= 0) { + final int block = loadResourceBagValue(resId, bagEntryId, outValue, true); + if (block < 0) { return null; } @@ -426,60 +191,52 @@ public final class AssetManager implements AutoCloseable { outValue.changingConfigurations); if (outValue.type == TypedValue.TYPE_STRING) { - return mApkAssets[cookie - 1].getStringFromPool(outValue.data); + return mStringBlocks[block].get(outValue.data); } return outValue.coerceToString(); } } - int getResourceArraySize(@ArrayRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceArraySize(mObject, resId); - } - } - /** - * Populates `outData` with array elements of `resId`. `outData` is normally - * used with - * {@link TypedArray}. - * - * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES} - * long, - * with the indices of the data representing the type, value, asset cookie, - * resource ID, - * configuration change mask, and density of the element. - * - * @param resId The resource ID of an array resource. - * @param outData The array to populate with data. - * @return The length of the array. + * Retrieves the string array associated with a particular resource + * identifier for the current configuration. * - * @see TypedArray#STYLE_TYPE - * @see TypedArray#STYLE_DATA - * @see TypedArray#STYLE_ASSET_COOKIE - * @see TypedArray#STYLE_RESOURCE_ID - * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS - * @see TypedArray#STYLE_DENSITY + * @param resId the resource identifier of the string array + * @return the string array, or {@code null} */ - int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) { - Preconditions.checkNotNull(outData, "outData"); - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceArray(mObject, resId, outData); - } + @Nullable + final String[] getResourceStringArray(@ArrayRes int resId) { + return getArrayStringResource(resId); } /** - * Retrieves the string array associated with a particular resource - * identifier for the current configuration. + * Populates {@code outValue} with the data associated a particular + * resource identifier for the current configuration. * - * @param resId the resource identifier of the string array - * @return the string array, or {@code null} + * @param resId the resource identifier to load + * @param densityDpi the density bucket for which to load the resource + * @param outValue the typed value in which to put the data + * @param resolveRefs {@code true} to resolve references, {@code false} + * to leave them unresolved + * @return {@code true} if the data was loaded into {@code outValue}, + * {@code false} otherwise */ - @Nullable String[] getResourceStringArray(@ArrayRes int resId) { + final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, + boolean resolveRefs) { synchronized (this) { - ensureValidLocked(); - return nativeGetResourceStringArray(mObject, resId); + final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs); + if (block < 0) { + return false; + } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mStringBlocks[block].get(outValue.data); + } + return true; } } @@ -489,48 +246,26 @@ public final class AssetManager implements AutoCloseable { * * @param resId the resource id of the string array */ - @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { + final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { synchronized (this) { - ensureValidLocked(); - final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId); + final int[] rawInfoArray = getArrayStringInfo(resId); if (rawInfoArray == null) { return null; } - final int rawInfoArrayLen = rawInfoArray.length; final int infoArrayLen = rawInfoArrayLen / 2; + int block; + int index; final CharSequence[] retArray = new CharSequence[infoArrayLen]; for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) { - int cookie = rawInfoArray[i]; - int index = rawInfoArray[i + 1]; - retArray[j] = (index >= 0 && cookie > 0) - ? mApkAssets[cookie - 1].getStringFromPool(index) : null; + block = rawInfoArray[i]; + index = rawInfoArray[i + 1]; + retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null; } return retArray; } } - @Nullable int[] getResourceIntArray(@ArrayRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceIntArray(mObject, resId); - } - } - - /** - * Get the attributes for a style resource. These are the <item> - * elements in - * a <style> resource. - * @param resId The resource ID of the style - * @return An array of attribute IDs. - */ - @AttrRes int[] getStyleAttributes(@StyleRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetStyleAttributes(mObject, resId); - } - } - /** * Populates {@code outValue} with the data associated with a particular * resource identifier for the current configuration. Resolves theme @@ -544,88 +279,73 @@ public final class AssetManager implements AutoCloseable { * @return {@code true} if the data was loaded into {@code outValue}, * {@code false} otherwise */ - boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, + final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, boolean resolveRefs) { - Preconditions.checkNotNull(outValue, "outValue"); - synchronized (this) { - ensureValidLocked(); - final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue, - resolveRefs); - if (cookie <= 0) { - return false; - } - - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); - - if (outValue.type == TypedValue.TYPE_STRING) { - outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); - } - return true; - } - } - - void dumpTheme(long theme, int priority, String tag, String prefix) { - synchronized (this) { - ensureValidLocked(); - nativeThemeDump(mObject, theme, priority, tag, prefix); + final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs); + if (block < 0) { + return false; } - } - @Nullable String getResourceName(@AnyRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceName(mObject, resId); - } - } + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); - @Nullable String getResourcePackageName(@AnyRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourcePackageName(mObject, resId); + if (outValue.type == TypedValue.TYPE_STRING) { + final StringBlock[] blocks = ensureStringBlocks(); + outValue.string = blocks[block].get(outValue.data); } + return true; } - @Nullable String getResourceTypeName(@AnyRes int resId) { + /** + * Ensures the string blocks are loaded. + * + * @return the string blocks + */ + @NonNull + final StringBlock[] ensureStringBlocks() { synchronized (this) { - ensureValidLocked(); - return nativeGetResourceTypeName(mObject, resId); + if (mStringBlocks == null) { + makeStringBlocks(sSystem.mStringBlocks); + } + return mStringBlocks; } } - @Nullable String getResourceEntryName(@AnyRes int resId) { - synchronized (this) { - ensureValidLocked(); - return nativeGetResourceEntryName(mObject, resId); + /*package*/ final void makeStringBlocks(StringBlock[] seed) { + final int seedNum = (seed != null) ? seed.length : 0; + final int num = getStringBlockCount(); + mStringBlocks = new StringBlock[num]; + if (localLOGV) Log.v(TAG, "Making string blocks for " + this + + ": " + num); + for (int i=0; i Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len; + } + public final void close() throws IOException { + synchronized (AssetManager.this) { + if (mAsset != 0) { + destroyAsset(mAsset); + mAsset = 0; + decRefsLocked(hashCode()); + } + } + } + public final void mark(int readlimit) { + mMarkPos = seekAsset(mAsset, 0, 0); + } + public final void reset() throws IOException { + seekAsset(mAsset, mMarkPos, -1); + } + public final int read(byte[] b) throws IOException { + return readAsset(mAsset, b, 0, b.length); + } + public final int read(byte[] b, int off, int len) throws IOException { + return readAsset(mAsset, b, off, len); } - - @Override public final long skip(long n) throws IOException { - ensureOpen(); - long pos = nativeAssetSeek(mAssetNativePtr, 0, 0); - if ((pos + n) > mLength) { - n = mLength - pos; + long pos = seekAsset(mAsset, 0, 0); + if ((pos+n) > mLength) { + n = mLength-pos; } if (n > 0) { - nativeAssetSeek(mAssetNativePtr, n, 0); + seekAsset(mAsset, n, 0); } return n; } - @Override - public final int available() throws IOException { - ensureOpen(); - final long len = nativeAssetGetRemainingLength(mAssetNativePtr); - return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len; + protected void finalize() throws Throwable + { + close(); } - @Override - public final boolean markSupported() { - return true; - } + private long mAsset; + private long mLength; + private long mMarkPos; + } - @Override - public final void mark(int readlimit) { - ensureOpen(); - mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0); + /** + * Add an additional set of assets to the asset manager. This can be + * either a directory or ZIP file. Not for use by applications. Returns + * the cookie of the added asset, or 0 on failure. + * {@hide} + */ + public final int addAssetPath(String path) { + return addAssetPathInternal(path, false); + } + + /** + * Add an application assets to the asset manager and loading it as shared library. + * This can be either a directory or ZIP file. Not for use by applications. Returns + * the cookie of the added asset, or 0 on failure. + * {@hide} + */ + public final int addAssetPathAsSharedLibrary(String path) { + return addAssetPathInternal(path, true); + } + + private final int addAssetPathInternal(String path, boolean appAsLib) { + synchronized (this) { + int res = addAssetPathNative(path, appAsLib); + makeStringBlocks(mStringBlocks); + return res; } + } - @Override - public final void reset() throws IOException { - ensureOpen(); - nativeAssetSeek(mAssetNativePtr, mMarkPos, -1); + private native final int addAssetPathNative(String path, boolean appAsLib); + + /** + * Add an additional set of assets to the asset manager from an already open + * FileDescriptor. Not for use by applications. + * This does not give full AssetManager functionality for these assets, + * since the origin of the file is not known for purposes of sharing, + * overlay resolution, and other features. However it does allow you + * to do simple access to the contents of the given fd as an apk file. + * Performs a dup of the underlying fd, so you must take care of still closing + * the FileDescriptor yourself (and can do that whenever you want). + * Returns the cookie of the added asset, or 0 on failure. + * {@hide} + */ + public int addAssetFd(FileDescriptor fd, String debugPathName) { + return addAssetFdInternal(fd, debugPathName, false); + } + + private int addAssetFdInternal(FileDescriptor fd, String debugPathName, + boolean appAsLib) { + synchronized (this) { + int res = addAssetFdNative(fd, debugPathName, appAsLib); + makeStringBlocks(mStringBlocks); + return res; } + } - @Override - public final void close() throws IOException { - if (mAssetNativePtr != 0) { - nativeAssetDestroy(mAssetNativePtr); - mAssetNativePtr = 0; + private native int addAssetFdNative(FileDescriptor fd, String debugPathName, + boolean appAsLib); - synchronized (AssetManager.this) { - decRefsLocked(hashCode()); - } - } + /** + * Add a set of assets to overlay an already added set of assets. + * + * This is only intended for application resources. System wide resources + * are handled before any Java code is executed. + * + * {@hide} + */ + + public final int addOverlayPath(String idmapPath) { + synchronized (this) { + int res = addOverlayPathNative(idmapPath); + makeStringBlocks(mStringBlocks); + return res; } + } - @Override - protected void finalize() throws Throwable { - close(); + /** + * See addOverlayPath. + * + * {@hide} + */ + public native final int addOverlayPathNative(String idmapPath); + + /** + * Add multiple sets of assets to the asset manager at once. See + * {@link #addAssetPath(String)} for more information. Returns array of + * cookies for each added asset with 0 indicating failure, or null if + * the input array of paths is null. + * {@hide} + */ + public final int[] addAssetPaths(String[] paths) { + if (paths == null) { + return null; } - private void ensureOpen() { - if (mAssetNativePtr == 0) { - throw new IllegalStateException("AssetInputStream is closed"); - } + int[] cookies = new int[paths.length]; + for (int i = 0; i < paths.length; i++) { + cookies[i] = addAssetPath(paths[i]); } + + return cookies; } /** * Determine whether the state in this asset manager is up-to-date with * the files on the filesystem. If false is returned, you need to * instantiate a new AssetManager class to see the new data. - * @hide + * {@hide} */ - public boolean isUpToDate() { - for (ApkAssets apkAssets : getApkAssets()) { - if (!apkAssets.isUpToDate()) { - return false; - } - } - return true; - } + public native final boolean isUpToDate(); /** * Get the locales that this asset manager contains data for. @@ -1097,12 +786,7 @@ public final class AssetManager implements AutoCloseable { * are of the form {@code ll_CC} where {@code ll} is a two letter language code, * and {@code CC} is a two letter country code. */ - public String[] getLocales() { - synchronized (this) { - ensureValidLocked(); - return nativeGetLocales(mObject, false /*excludeSystem*/); - } - } + public native final String[] getLocales(); /** * Same as getLocales(), except that locales that are only provided by the system (i.e. those @@ -1112,58 +796,132 @@ public final class AssetManager implements AutoCloseable { * assets support Cherokee and French, getLocales() would return * [Cherokee, English, French, German], while getNonSystemLocales() would return * [Cherokee, French]. - * @hide + * {@hide} */ - public String[] getNonSystemLocales() { - synchronized (this) { - ensureValidLocked(); - return nativeGetLocales(mObject, true /*excludeSystem*/); - } - } + public native final String[] getNonSystemLocales(); + + /** {@hide} */ + public native final Configuration[] getSizeConfigurations(); /** - * @hide + * Change the configuation used when retrieving resources. Not for use by + * applications. + * {@hide} */ - Configuration[] getSizeConfigurations() { - synchronized (this) { - ensureValidLocked(); - return nativeGetSizeConfigurations(mObject); - } - } + public native final void setConfiguration(int mcc, int mnc, String locale, + int orientation, int touchscreen, int density, int keyboard, + int keyboardHidden, int navigation, int screenWidth, int screenHeight, + int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, + int screenLayout, int uiMode, int colorMode, int majorVersion); /** - * Change the configuration used when retrieving resources. Not for use by - * applications. - * @hide + * Retrieve the resource identifier for the given resource name. */ - public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation, - int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, - int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, - int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) { - synchronized (this) { - ensureValidLocked(); - nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density, - keyboard, keyboardHidden, navigation, screenWidth, screenHeight, - smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode, - colorMode, majorVersion); - } - } + /*package*/ native final int getResourceIdentifier(String name, + String defType, + String defPackage); + /*package*/ native final String getResourceName(int resid); + /*package*/ native final String getResourcePackageName(int resid); + /*package*/ native final String getResourceTypeName(int resid); + /*package*/ native final String getResourceEntryName(int resid); + + private native final long openAsset(String fileName, int accessMode); + private final native ParcelFileDescriptor openAssetFd(String fileName, + long[] outOffsets) throws IOException; + private native final long openNonAssetNative(int cookie, String fileName, + int accessMode); + private native ParcelFileDescriptor openNonAssetFdNative(int cookie, + String fileName, long[] outOffsets) throws IOException; + private native final void destroyAsset(long asset); + private native final int readAssetChar(long asset); + private native final int readAsset(long asset, byte[] b, int off, int len); + private native final long seekAsset(long asset, long offset, int whence); + private native final long getAssetLength(long asset); + private native final long getAssetRemainingLength(long asset); + + /** Returns true if the resource was found, filling in mRetStringBlock and + * mRetData. */ + private native final int loadResourceValue(int ident, short density, TypedValue outValue, + boolean resolve); + /** Returns true if the resource was found, filling in mRetStringBlock and + * mRetData. */ + private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue, + boolean resolve); + /*package*/ static final int STYLE_NUM_ENTRIES = 6; + /*package*/ static final int STYLE_TYPE = 0; + /*package*/ static final int STYLE_DATA = 1; + /*package*/ static final int STYLE_ASSET_COOKIE = 2; + /*package*/ static final int STYLE_RESOURCE_ID = 3; + + /* Offset within typed data array for native changingConfigurations. */ + static final int STYLE_CHANGING_CONFIGURATIONS = 4; + + /*package*/ static final int STYLE_DENSITY = 5; + /*package*/ native static final void applyStyle(long theme, + int defStyleAttr, int defStyleRes, long xmlParser, + int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress); + /*package*/ native static final boolean resolveAttrs(long theme, + int defStyleAttr, int defStyleRes, int[] inValues, + int[] inAttrs, int[] outValues, int[] outIndices); + /*package*/ native final boolean retrieveAttributes( + long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); + /*package*/ native final int getArraySize(int resource); + /*package*/ native final int retrieveArray(int resource, int[] outValues); + private native final int getStringBlockCount(); + private native final long getNativeStringBlock(int block); + + /** + * {@hide} + */ + public native final String getCookieName(int cookie); + + /** + * {@hide} + */ + public native final SparseArray getAssignedPackageIdentifiers(); + + /** + * {@hide} + */ + public native static final int getGlobalAssetCount(); + /** - * @hide + * {@hide} */ - public SparseArray getAssignedPackageIdentifiers() { - synchronized (this) { - ensureValidLocked(); - return nativeGetAssignedPackageIdentifiers(mObject); - } - } + public native static final String getAssetAllocations(); + + /** + * {@hide} + */ + public native static final int getGlobalAssetManagerCount(); + + private native final long newTheme(); + private native final void deleteTheme(long theme); + /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force); + /*package*/ native static final void copyTheme(long dest, long source); + /*package*/ native static final void clearTheme(long theme); + /*package*/ native static final int loadThemeAttributeValue(long theme, int ident, + TypedValue outValue, + boolean resolve); + /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix); + /*package*/ native static final @NativeConfig int getThemeChangingConfigurations(long theme); + + private native final long openXmlAssetNative(int cookie, String fileName); + + private native final String[] getArrayStringResource(int arrayRes); + private native final int[] getArrayStringInfo(int arrayRes); + /*package*/ native final int[] getArrayIntResource(int arrayRes); + /*package*/ native final int[] getStyleAttributes(int themeRes); + + private native final void init(boolean isSystem); + private native final void destroy(); @GuardedBy("this") - private void incRefsLocked(long id) { + private final void incRefsLocked(long id) { if (DEBUG_REFS) { if (mRefStacks == null) { - mRefStacks = new HashMap<>(); + mRefStacks = new HashMap(); } RuntimeException ex = new RuntimeException(); ex.fillInStackTrace(); @@ -1173,116 +931,15 @@ public final class AssetManager implements AutoCloseable { } @GuardedBy("this") - private void decRefsLocked(long id) { + private final void decRefsLocked(long id) { if (DEBUG_REFS && mRefStacks != null) { mRefStacks.remove(id); } mNumRefs--; - if (mNumRefs == 0 && mObject != 0) { - nativeDestroy(mObject); - mObject = 0; + //System.out.println("Dec streams: mNumRefs=" + mNumRefs + // + " mReleased=" + mReleased); + if (mNumRefs == 0) { + destroy(); } } - - // AssetManager setup native methods. - private static native long nativeCreate(); - private static native void nativeDestroy(long ptr); - private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, - boolean invalidateCaches); - private static native void nativeSetConfiguration(long ptr, int mcc, int mnc, - @Nullable String locale, int orientation, int touchscreen, int density, int keyboard, - int keyboardHidden, int navigation, int screenWidth, int screenHeight, - int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, - int uiMode, int colorMode, int majorVersion); - private static native @NonNull SparseArray nativeGetAssignedPackageIdentifiers( - long ptr); - - // File native methods. - private static native @Nullable String[] nativeList(long ptr, @NonNull String path) - throws IOException; - private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode); - private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr, - @NonNull String fileName, long[] outOffsets) throws IOException; - private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName, - int accessMode); - private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie, - @NonNull String fileName, @NonNull long[] outOffsets) throws IOException; - private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName); - - // Primitive resource native methods. - private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density, - @NonNull TypedValue outValue, boolean resolveReferences); - private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId, - @NonNull TypedValue outValue); - - private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr, - @StyleRes int resId); - private static native @Nullable String[] nativeGetResourceStringArray(long ptr, - @ArrayRes int resId); - private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr, - @ArrayRes int resId); - private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId); - private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId); - private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId, - @NonNull int[] outValues); - - // Resource name/ID native methods. - private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name, - @Nullable String defType, @Nullable String defPackage); - private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid); - private static native @Nullable String nativeGetResourcePackageName(long ptr, - @AnyRes int resid); - private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid); - private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid); - private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem); - private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr); - - // Style attribute retrieval native methods. - private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, - @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs, - long outValuesAddress, long outIndicesAddress); - private static native boolean nativeResolveAttrs(long ptr, long themePtr, - @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, - @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); - private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr, - @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); - - // Theme related native methods - private static native long nativeThemeCreate(long ptr); - private static native void nativeThemeDestroy(long themePtr); - private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, - boolean force); - static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr); - static native void nativeThemeClear(long themePtr); - private static native int nativeThemeGetAttributeValue(long ptr, long themePtr, - @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve); - private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag, - String prefix); - static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr); - - // AssetInputStream related native methods. - private static native void nativeAssetDestroy(long assetPtr); - private static native int nativeAssetReadChar(long assetPtr); - private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len); - private static native long nativeAssetSeek(long assetPtr, long offset, int whence); - private static native long nativeAssetGetLength(long assetPtr); - private static native long nativeAssetGetRemainingLength(long assetPtr); - - private static native void nativeVerifySystemIdmaps(); - - // Global debug native methods. - /** - * @hide - */ - public static native int getGlobalAssetCount(); - - /** - * @hide - */ - public static native String getAssetAllocations(); - - /** - * @hide - */ - public static native int getGlobalAssetManagerCount(); } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index d8133824f757..ad85e71b86f9 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -590,7 +590,7 @@ public class Resources { */ @NonNull public int[] getIntArray(@ArrayRes int id) throws NotFoundException { - int[] res = mResourcesImpl.getAssets().getResourceIntArray(id); + int[] res = mResourcesImpl.getAssets().getArrayIntResource(id); if (res != null) { return res; } @@ -613,13 +613,13 @@ public class Resources { @NonNull public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException { final ResourcesImpl impl = mResourcesImpl; - int len = impl.getAssets().getResourceArraySize(id); + int len = impl.getAssets().getArraySize(id); if (len < 0) { throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id)); } TypedArray array = TypedArray.obtain(this, len); - array.mLength = impl.getAssets().getResourceArray(id, array.mData); + array.mLength = impl.getAssets().retrieveArray(id, array.mData); array.mIndices[0] = 0; return array; @@ -1794,7 +1794,8 @@ public class Resources { // out the attributes from the XML file (applying type information // contained in the resources and such). XmlBlock.Parser parser = (XmlBlock.Parser)set; - mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices); + mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, attrs, + array.mData, array.mIndices); array.mXml = parser; diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 08a161347e59..91dd7ee14d1b 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -168,6 +168,7 @@ public class ResourcesImpl { mDisplayAdjustments = displayAdjustments; mConfiguration.setToDefaults(); updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo()); + mAssets.ensureStringBlocks(); } public DisplayAdjustments getDisplayAdjustments() { @@ -1274,7 +1275,8 @@ public class ResourcesImpl { void applyStyle(int resId, boolean force) { synchronized (mKey) { - mAssets.applyStyleToTheme(mTheme, resId, force); + AssetManager.applyThemeStyle(mTheme, resId, force); + mThemeResId = resId; mKey.append(resId, force); } @@ -1283,7 +1285,7 @@ public class ResourcesImpl { void setTo(ThemeImpl other) { synchronized (mKey) { synchronized (other.mKey) { - AssetManager.nativeThemeCopy(mTheme, other.mTheme); + AssetManager.copyTheme(mTheme, other.mTheme); mThemeResId = other.mThemeResId; mKey.setTo(other.getKey()); @@ -1306,10 +1308,12 @@ public class ResourcesImpl { // out the attributes from the XML file (applying type information // contained in the resources and such). final XmlBlock.Parser parser = (XmlBlock.Parser) set; - mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs, - array.mDataAddress, array.mIndicesAddress); + AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes, + parser != null ? parser.mParseState : 0, + attrs, attrs.length, array.mDataAddress, array.mIndicesAddress); array.mTheme = wrapper; array.mXml = parser; + return array; } } @@ -1326,7 +1330,7 @@ public class ResourcesImpl { } final TypedArray array = TypedArray.obtain(wrapper.getResources(), len); - mAssets.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); + AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); array.mTheme = wrapper; array.mXml = null; return array; @@ -1346,14 +1350,14 @@ public class ResourcesImpl { @Config int getChangingConfigurations() { synchronized (mKey) { final @NativeConfig int nativeChangingConfig = - AssetManager.nativeThemeGetChangingConfigurations(mTheme); + AssetManager.getThemeChangingConfigurations(mTheme); return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig); } } public void dump(int priority, String tag, String prefix) { synchronized (mKey) { - mAssets.dumpTheme(mTheme, priority, tag, prefix); + AssetManager.dumpTheme(mTheme, priority, tag, prefix); } } @@ -1382,13 +1386,13 @@ public class ResourcesImpl { */ void rebase() { synchronized (mKey) { - AssetManager.nativeThemeClear(mTheme); + AssetManager.clearTheme(mTheme); // Reapply the same styles in the same order. for (int i = 0; i < mKey.mCount; i++) { final int resId = mKey.mResId[i]; final boolean force = mKey.mForce[i]; - mAssets.applyStyleToTheme(mTheme, resId, force); + AssetManager.applyThemeStyle(mTheme, resId, force); } } } diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index cbb3c6df0558..f33c75168a5f 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -61,15 +61,6 @@ public class TypedArray { return attrs; } - // STYLE_ prefixed constants are offsets within the typed data array. - static final int STYLE_NUM_ENTRIES = 6; - static final int STYLE_TYPE = 0; - static final int STYLE_DATA = 1; - static final int STYLE_ASSET_COOKIE = 2; - static final int STYLE_RESOURCE_ID = 3; - static final int STYLE_CHANGING_CONFIGURATIONS = 4; - static final int STYLE_DENSITY = 5; - private final Resources mResources; private DisplayMetrics mMetrics; private AssetManager mAssets; @@ -87,7 +78,7 @@ public class TypedArray { private void resize(int len) { mLength = len; - final int dataLen = len * STYLE_NUM_ENTRIES; + final int dataLen = len * AssetManager.STYLE_NUM_ENTRIES; final int indicesLen = len + 1; final VMRuntime runtime = VMRuntime.getRuntime(); if (mDataAddress == 0 || mData.length < dataLen) { @@ -175,9 +166,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -212,9 +203,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -251,13 +242,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_STRING) { - final int cookie = data[index + STYLE_ASSET_COOKIE]; + final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; if (cookie < 0) { - return mXml.getPooledString(data[index + STYLE_DATA]).toString(); + return mXml.getPooledString( + data[index+AssetManager.STYLE_DATA]).toString(); } } return null; @@ -282,11 +274,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava( - data[index + STYLE_CHANGING_CONFIGURATIONS]); + data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); if ((changingConfigs & ~allowedChangingConfigs) != 0) { return null; } @@ -328,14 +320,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA] != 0; + return data[index+AssetManager.STYLE_DATA] != 0; } final TypedValue v = mValue; @@ -367,14 +359,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } final TypedValue v = mValue; @@ -404,16 +396,16 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FLOAT) { - return Float.intBitsToFloat(data[index + STYLE_DATA]); + return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]); } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } final TypedValue v = mValue; @@ -454,15 +446,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_STRING) { final TypedValue value = mValue; if (getValueAt(index, value)) { @@ -506,7 +498,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -541,7 +533,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -572,15 +564,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -620,14 +612,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimension( + data[index + AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -668,14 +661,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelOffset( + data[index + AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -717,14 +711,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize( + data[index+AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -760,15 +755,16 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize( + data[index+AssetManager.STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -799,14 +795,15 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index + STYLE_DATA]; + return data[index+AssetManager.STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize( + data[index + AssetManager.STYLE_DATA], mMetrics); } return defValue; @@ -836,14 +833,15 @@ public class TypedArray { } final int attrIndex = index; - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FRACTION) { - return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase); + return TypedValue.complexToFraction( + data[index+AssetManager.STYLE_DATA], base, pbase); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -876,10 +874,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) { - final int resid = data[index + STYLE_RESOURCE_ID]; + if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) { + final int resid = data[index+AssetManager.STYLE_RESOURCE_ID]; if (resid != 0) { return resid; } @@ -904,10 +902,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { - return data[index + STYLE_DATA]; + if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { + return data[index + AssetManager.STYLE_DATA]; } return defValue; } @@ -941,7 +939,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -977,7 +975,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -1008,7 +1006,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { return mResources.getTextArray(value.resourceId); } return null; @@ -1029,7 +1027,7 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - return getValueAt(index * STYLE_NUM_ENTRIES, outValue); + return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue); } /** @@ -1045,8 +1043,8 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; - return mData[index + STYLE_TYPE]; + index *= AssetManager.STYLE_NUM_ENTRIES; + return mData[index + AssetManager.STYLE_TYPE]; } /** @@ -1065,9 +1063,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; return type != TypedValue.TYPE_NULL; } @@ -1086,11 +1084,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= STYLE_NUM_ENTRIES; + index *= AssetManager.STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; return type != TypedValue.TYPE_NULL - || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; + || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; } /** @@ -1111,7 +1109,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { return value; } return null; @@ -1183,16 +1181,16 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * STYLE_NUM_ENTRIES; - if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { + final int index = i * AssetManager.STYLE_NUM_ENTRIES; + if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { // Not an attribute, ignore. continue; } // Null the entry so that we can safely call getZzz(). - data[index + STYLE_TYPE] = TypedValue.TYPE_NULL; + data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL; - final int attr = data[index + STYLE_DATA]; + final int attr = data[index + AssetManager.STYLE_DATA]; if (attr == 0) { // Useless data, ignore. continue; @@ -1233,44 +1231,45 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * STYLE_NUM_ENTRIES; - final int type = data[index + STYLE_TYPE]; + final int index = i * AssetManager.STYLE_NUM_ENTRIES; + final int type = data[index + AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { continue; } changingConfig |= ActivityInfo.activityInfoConfigNativeToJava( - data[index + STYLE_CHANGING_CONFIGURATIONS]); + data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); } return changingConfig; } private boolean getValueAt(int index, TypedValue outValue) { final int[] data = mData; - final int type = data[index + STYLE_TYPE]; + final int type = data[index+AssetManager.STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return false; } outValue.type = type; - outValue.data = data[index + STYLE_DATA]; - outValue.assetCookie = data[index + STYLE_ASSET_COOKIE]; - outValue.resourceId = data[index + STYLE_RESOURCE_ID]; + outValue.data = data[index+AssetManager.STYLE_DATA]; + outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; + outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID]; outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - data[index + STYLE_CHANGING_CONFIGURATIONS]); - outValue.density = data[index + STYLE_DENSITY]; + data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); + outValue.density = data[index+AssetManager.STYLE_DENSITY]; outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null; return true; } private CharSequence loadStringValueAt(int index) { final int[] data = mData; - final int cookie = data[index + STYLE_ASSET_COOKIE]; + final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; if (cookie < 0) { if (mXml != null) { - return mXml.getPooledString(data[index + STYLE_DATA]); + return mXml.getPooledString( + data[index+AssetManager.STYLE_DATA]); } return null; } - return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]); + return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]); } /** @hide */ diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java index d4ccffb83ca5..e6b957414ea8 100644 --- a/core/java/android/content/res/XmlBlock.java +++ b/core/java/android/content/res/XmlBlock.java @@ -16,7 +16,6 @@ package android.content.res; -import android.annotation.Nullable; import android.util.TypedValue; import com.android.internal.util.XmlUtils; @@ -34,7 +33,7 @@ import java.io.Reader; * * {@hide} */ -final class XmlBlock implements AutoCloseable { +final class XmlBlock { private static final boolean DEBUG=false; public XmlBlock(byte[] data) { @@ -49,7 +48,6 @@ final class XmlBlock implements AutoCloseable { mStrings = new StringBlock(nativeGetStringBlock(mNative), false); } - @Override public void close() { synchronized (this) { if (mOpen) { @@ -480,13 +478,13 @@ final class XmlBlock implements AutoCloseable { * are doing! The given native object must exist for the entire lifetime * of this newly creating XmlBlock. */ - XmlBlock(@Nullable AssetManager assets, long xmlBlock) { + XmlBlock(AssetManager assets, long xmlBlock) { mAssets = assets; mNative = xmlBlock; mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false); } - private @Nullable final AssetManager mAssets; + private final AssetManager mAssets; private final long mNative; /*package*/ final StringBlock mStrings; private boolean mOpen = true; diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 78a3e137ccb0..33f80ce8ffb0 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -110,8 +110,8 @@ cc_library_shared { "android_util_AssetManager.cpp", "android_util_Binder.cpp", "android_util_EventLog.cpp", - "android_util_Log.cpp", "android_util_MemoryIntArray.cpp", + "android_util_Log.cpp", "android_util_PathParser.cpp", "android_util_Process.cpp", "android_util_StringBlock.cpp", @@ -191,7 +191,6 @@ cc_library_shared { "android_backup_FileBackupHelperBase.cpp", "android_backup_BackupHelperDispatcher.cpp", "android_app_backup_FullBackup.cpp", - "android_content_res_ApkAssets.cpp", "android_content_res_ObbScanner.cpp", "android_content_res_Configuration.cpp", "android_animation_PropertyValuesHolder.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 4a032c4bbae4..d20217386b1e 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -123,7 +123,6 @@ extern int register_android_util_MemoryIntArray(JNIEnv* env); extern int register_android_util_PathParser(JNIEnv* env); extern int register_android_content_StringBlock(JNIEnv* env); extern int register_android_content_XmlBlock(JNIEnv* env); -extern int register_android_content_res_ApkAssets(JNIEnv* env); extern int register_android_graphics_Canvas(JNIEnv* env); extern int register_android_graphics_CanvasProperty(JNIEnv* env); extern int register_android_graphics_ColorFilter(JNIEnv* env); @@ -1347,7 +1346,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_AssetManager), REG_JNI(register_android_content_StringBlock), REG_JNI(register_android_content_XmlBlock), - REG_JNI(register_android_content_res_ApkAssets), REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_Hyphenator), REG_JNI(register_android_text_MeasuredParagraph), diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index ed032c78f6c7..48aef4a8b320 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include "Utils.h" #include "FontUtils.h" @@ -205,8 +205,7 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b NPE_CHECK_RETURN_ZERO(env, jpath); NativeFamilyBuilder* builder = reinterpret_cast(builderPtr); - - Guarded* mgr = AssetManagerForJavaObject(env, jassetMgr); + AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr); if (NULL == mgr) { builder->axes.clear(); return false; @@ -218,33 +217,27 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b return false; } - std::unique_ptr asset; - { - ScopedLock locked_mgr(*mgr); - if (isAsset) { - asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); - } else if (cookie > 0) { - // Valid java cookies are 1-based, but AssetManager cookies are 0-based. - asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast(cookie - 1), - Asset::ACCESS_BUFFER); - } else { - asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER); - } + Asset* asset; + if (isAsset) { + asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); + } else { + asset = cookie ? mgr->openNonAsset(static_cast(cookie), str.c_str(), + Asset::ACCESS_BUFFER) : mgr->openNonAsset(str.c_str(), Asset::ACCESS_BUFFER); } - if (nullptr == asset) { + if (NULL == asset) { builder->axes.clear(); return false; } const void* buf = asset->getBuffer(false); if (NULL == buf) { + delete asset; builder->axes.clear(); return false; } - sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, - asset.release())); + sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset)); return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); } diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 49a24a30f77e..09e37e1a3de6 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -361,7 +361,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName code->sdkVersion = sdkVersion; code->javaAssetManager = env->NewGlobalRef(jAssetMgr); - code->assetManager = NdkAssetManagerForJavaObject(env, jAssetMgr); + code->assetManager = assetManagerForJavaObject(env, jAssetMgr); if (obbDir != NULL) { dirStr = env->GetStringUTFChars(obbDir, NULL); diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp deleted file mode 100644 index c0f151b71c93..000000000000 --- a/core/jni/android_content_res_ApkAssets.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "android-base/macros.h" -#include "android-base/stringprintf.h" -#include "android-base/unique_fd.h" -#include "androidfw/ApkAssets.h" -#include "utils/misc.h" - -#include "core_jni_helpers.h" -#include "jni.h" -#include "nativehelper/ScopedUtfChars.h" - -using ::android::base::unique_fd; - -namespace android { - -static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboolean system, - jboolean force_shared_lib, jboolean overlay) { - ScopedUtfChars path(env, java_path); - if (path.c_str() == nullptr) { - return 0; - } - - std::unique_ptr apk_assets; - if (overlay) { - apk_assets = ApkAssets::LoadOverlay(path.c_str(), system); - } else if (force_shared_lib) { - apk_assets = ApkAssets::LoadAsSharedLibrary(path.c_str(), system); - } else { - apk_assets = ApkAssets::Load(path.c_str(), system); - } - - if (apk_assets == nullptr) { - std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str()); - jniThrowException(env, "java/io/IOException", error_msg.c_str()); - return 0; - } - return reinterpret_cast(apk_assets.release()); -} - -static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor, - jstring friendly_name, jboolean system, jboolean force_shared_lib) { - ScopedUtfChars friendly_name_utf8(env, friendly_name); - if (friendly_name_utf8.c_str() == nullptr) { - return 0; - } - - int fd = jniGetFDFromFileDescriptor(env, file_descriptor); - if (fd < 0) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); - return 0; - } - - unique_fd dup_fd(::dup(fd)); - if (dup_fd < 0) { - jniThrowIOException(env, errno); - return 0; - } - - std::unique_ptr apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), - friendly_name_utf8.c_str(), - system, force_shared_lib); - if (apk_assets == nullptr) { - std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d", - friendly_name_utf8.c_str(), dup_fd.get()); - jniThrowException(env, "java/io/IOException", error_msg.c_str()); - return 0; - } - return reinterpret_cast(apk_assets.release()); -} - -static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - delete reinterpret_cast(ptr); -} - -static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) { - const ApkAssets* apk_assets = reinterpret_cast(ptr); - return env->NewStringUTF(apk_assets->GetPath().c_str()); -} - -static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - const ApkAssets* apk_assets = reinterpret_cast(ptr); - return reinterpret_cast(apk_assets->GetLoadedArsc()->GetStringPool()); -} - -static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - const ApkAssets* apk_assets = reinterpret_cast(ptr); - (void)apk_assets; - return JNI_TRUE; -} - -static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) { - ScopedUtfChars path_utf8(env, file_name); - if (path_utf8.c_str() == nullptr) { - return 0; - } - - const ApkAssets* apk_assets = reinterpret_cast(ptr); - std::unique_ptr asset = apk_assets->Open(path_utf8.c_str(), - Asset::AccessMode::ACCESS_RANDOM); - if (asset == nullptr) { - jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str()); - return 0; - } - - // DynamicRefTable is only needed when looking up resource references. Opening an XML file - // directly from an ApkAssets has no notion of proper resource references. - std::unique_ptr xml_tree = util::make_unique(nullptr /*dynamicRefTable*/); - status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); - asset.reset(); - - if (err != NO_ERROR) { - jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); - return 0; - } - return reinterpret_cast(xml_tree.release()); -} - -// JNI registration. -static const JNINativeMethod gApkAssetsMethods[] = { - {"nativeLoad", "(Ljava/lang/String;ZZZ)J", (void*)NativeLoad}, - {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZ)J", - (void*)NativeLoadFromFd}, - {"nativeDestroy", "(J)V", (void*)NativeDestroy}, - {"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath}, - {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock}, - {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate}, - {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml}, -}; - -int register_android_content_res_ApkAssets(JNIEnv* env) { - return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods, - arraysize(gApkAssetsMethods)); -} - -} // namespace android diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index c623ca621292..683b4c490ec3 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -1,1441 +1,1851 @@ -/* - * Copyright 2006, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/* //device/libs/android_runtime/android_util_AssetManager.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ #define LOG_TAG "asset" +#include + #include #include #include -#include -#include #include #include +#include +#include #include // for AID_SYSTEM -#include "android-base/logging.h" -#include "android-base/properties.h" -#include "android-base/stringprintf.h" -#include "android_runtime/android_util_AssetManager.h" -#include "android_runtime/AndroidRuntime.h" -#include "android_util_Binder.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" -#include "androidfw/AssetManager2.h" #include "androidfw/AttributeResolution.h" -#include "androidfw/MutexGuard.h" #include "androidfw/ResourceTypes.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_util_Binder.h" #include "core_jni_helpers.h" #include "jni.h" -#include "nativehelper/JNIHelp.h" -#include "nativehelper/ScopedPrimitiveArray.h" -#include "nativehelper/ScopedStringChars.h" -#include "nativehelper/ScopedUtfChars.h" +#include +#include +#include #include "utils/Log.h" -#include "utils/String8.h" #include "utils/misc.h" +#include "utils/String8.h" extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap); extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap); -using ::android::base::StringPrintf; namespace android { +static const bool kThrowOnBadId = false; + // ---------------------------------------------------------------------------- -static struct typedvalue_offsets_t { - jfieldID mType; - jfieldID mData; - jfieldID mString; - jfieldID mAssetCookie; - jfieldID mResourceId; - jfieldID mChangingConfigurations; - jfieldID mDensity; +static struct typedvalue_offsets_t +{ + jfieldID mType; + jfieldID mData; + jfieldID mString; + jfieldID mAssetCookie; + jfieldID mResourceId; + jfieldID mChangingConfigurations; + jfieldID mDensity; } gTypedValueOffsets; -static struct assetfiledescriptor_offsets_t { - jfieldID mFd; - jfieldID mStartOffset; - jfieldID mLength; +static struct assetfiledescriptor_offsets_t +{ + jfieldID mFd; + jfieldID mStartOffset; + jfieldID mLength; } gAssetFileDescriptorOffsets; -static struct assetmanager_offsets_t { - jfieldID mObject; +static struct assetmanager_offsets_t +{ + jfieldID mObject; } gAssetManagerOffsets; -static struct { - jfieldID native_ptr; -} gApkAssetsFields; - -static struct sparsearray_offsets_t { - jclass classObject; - jmethodID constructor; - jmethodID put; +static struct sparsearray_offsets_t +{ + jclass classObject; + jmethodID constructor; + jmethodID put; } gSparseArrayOffsets; -static struct configuration_offsets_t { - jclass classObject; - jmethodID constructor; - jfieldID mSmallestScreenWidthDpOffset; - jfieldID mScreenWidthDpOffset; - jfieldID mScreenHeightDpOffset; +static struct configuration_offsets_t +{ + jclass classObject; + jmethodID constructor; + jfieldID mSmallestScreenWidthDpOffset; + jfieldID mScreenWidthDpOffset; + jfieldID mScreenHeightDpOffset; } gConfigurationOffsets; -jclass g_stringClass = nullptr; +jclass g_stringClass = NULL; // ---------------------------------------------------------------------------- -// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. -constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { - return cookie != kInvalidCookie ? static_cast(cookie + 1) : -1; -} - -constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) { - return cookie > 0 ? static_cast(cookie - 1) : kInvalidCookie; +static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, + const Res_value& value, uint32_t ref, ssize_t block, + uint32_t typeSpecFlags, ResTable_config* config = NULL); + +jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, + const Res_value& value, uint32_t ref, ssize_t block, + uint32_t typeSpecFlags, ResTable_config* config) +{ + env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType); + env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie, + static_cast(table->getTableCookie(block))); + env->SetIntField(outValue, gTypedValueOffsets.mData, value.data); + env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL); + env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref); + env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations, + typeSpecFlags); + if (config != NULL) { + env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density); + } + return block; } // This is called by zygote (running as user root) as part of preloadResources. -static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { - switch (pid_t pid = fork()) { - case -1: - PLOG(ERROR) << "failed to fork for idmap"; - break; - - // child - case 0: { - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata; - - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - - capheader.version = _LINUX_CAPABILITY_VERSION; - capheader.pid = 0; - - if (capget(&capheader, &capdata) != 0) { - PLOG(ERROR) << "capget"; - exit(1); - } - - capdata.effective = capdata.permitted; - if (capset(&capheader, &capdata) != 0) { - PLOG(ERROR) << "capset"; - exit(1); - } - - if (setgid(AID_SYSTEM) != 0) { - PLOG(ERROR) << "setgid"; - exit(1); - } - - if (setuid(AID_SYSTEM) != 0) { - PLOG(ERROR) << "setuid"; - exit(1); - } - - // Generic idmap parameters - const char* argv[8]; - int argc = 0; - struct stat st; - - memset(argv, 0, sizeof(argv)); - argv[argc++] = AssetManager::IDMAP_BIN; - argv[argc++] = "--scan"; - argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; - argv[argc++] = AssetManager::TARGET_APK_PATH; - argv[argc++] = AssetManager::IDMAP_DIR; - - // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, - // use OVERLAY_DIR/ in addition to OVERLAY_DIR. - std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY, - ""); - if (!overlay_theme_path.empty()) { - overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path; - if (stat(overlay_theme_path.c_str(), &st) == 0) { - argv[argc++] = overlay_theme_path.c_str(); - } - } - - if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::OVERLAY_DIR; - } - - if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; - } - - // Finally, invoke idmap (if any overlay directory exists) - if (argc > 5) { - execv(AssetManager::IDMAP_BIN, (char* const*)argv); - PLOG(ERROR) << "failed to execv for idmap"; - exit(1); // should never get here - } else { - exit(0); - } - } break; - - // parent - default: - waitpid(pid, nullptr, 0); - break; - } +static void verifySystemIdmaps() +{ + pid_t pid; + char system_id[10]; + + snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM); + + switch (pid = fork()) { + case -1: + ALOGE("failed to fork for idmap: %s", strerror(errno)); + break; + case 0: // child + { + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata; + + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + + capheader.version = _LINUX_CAPABILITY_VERSION; + capheader.pid = 0; + + if (capget(&capheader, &capdata) != 0) { + ALOGE("capget: %s\n", strerror(errno)); + exit(1); + } + + capdata.effective = capdata.permitted; + if (capset(&capheader, &capdata) != 0) { + ALOGE("capset: %s\n", strerror(errno)); + exit(1); + } + + if (setgid(AID_SYSTEM) != 0) { + ALOGE("setgid: %s\n", strerror(errno)); + exit(1); + } + + if (setuid(AID_SYSTEM) != 0) { + ALOGE("setuid: %s\n", strerror(errno)); + exit(1); + } + + // Generic idmap parameters + const char* argv[8]; + int argc = 0; + struct stat st; + + memset(argv, NULL, sizeof(argv)); + argv[argc++] = AssetManager::IDMAP_BIN; + argv[argc++] = "--scan"; + argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; + argv[argc++] = AssetManager::TARGET_APK_PATH; + argv[argc++] = AssetManager::IDMAP_DIR; + + // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, + // use OVERLAY_DIR/ in addition to OVERLAY_DIR. + char subdir[PROP_VALUE_MAX]; + int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir); + if (len > 0) { + String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir; + if (stat(overlayPath.string(), &st) == 0) { + argv[argc++] = overlayPath.string(); + } + } + if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::OVERLAY_DIR; + } + + if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; + } + + // Finally, invoke idmap (if any overlay directory exists) + if (argc > 5) { + execv(AssetManager::IDMAP_BIN, (char* const*)argv); + ALOGE("failed to execv for idmap: %s", strerror(errno)); + exit(1); // should never get here + } else { + exit(0); + } + } + break; + default: // parent + waitpid(pid, NULL, 0); + break; + } } -static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref, - uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) { - env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType); - env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie, - ApkAssetsCookieToJavaCookie(cookie)); - env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data); - env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr); - env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref); - env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags); - if (config != nullptr) { - env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density); - } - return static_cast(ApkAssetsCookieToJavaCookie(cookie)); -} // ---------------------------------------------------------------------------- -// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance. -struct GuardedAssetManager : public ::AAssetManager { - Guarded guarded_assetmanager; -}; - -::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { - jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject); - ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle); - if (am == nullptr) { +// this guy is exported to other jni routines +AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj) +{ + jlong amHandle = env->GetLongField(obj, gAssetManagerOffsets.mObject); + AssetManager* am = reinterpret_cast(amHandle); + if (am != NULL) { + return am; + } jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); - return nullptr; - } - return am; + return NULL; } -Guarded* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) { - if (assetmanager == nullptr) { - return nullptr; - } - return &reinterpret_cast(assetmanager)->guarded_assetmanager; -} +static jlong android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz, + jstring fileName, jint mode) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + ALOGV("openAsset in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Empty file name"); + return -1; + } + + if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM + && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return -1; + } + + Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return -1; + } + + //printf("Created Asset Stream: %p\n", a); -Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { - return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager)); + return reinterpret_cast(a); } -static Guarded& AssetManagerFromLong(jlong ptr) { - return *AssetManagerForNdkAssetManager(reinterpret_cast(ptr)); +static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets) +{ + off64_t startOffset, length; + int fd = a->openFileDescriptor(&startOffset, &length); + delete a; + + if (fd < 0) { + jniThrowException(env, "java/io/FileNotFoundException", + "This file can not be opened as a file descriptor; it is probably compressed"); + return NULL; + } + + jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0); + if (offsets == NULL) { + close(fd); + return NULL; + } + + offsets[0] = startOffset; + offsets[1] = length; + + env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0); + + jobject fileDesc = jniCreateFileDescriptor(env, fd); + if (fileDesc == NULL) { + close(fd); + return NULL; + } + + return newParcelFileDescriptor(env, fileDesc); } -static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr asset, - jlongArray out_offsets) { - off64_t start_offset, length; - int fd = asset->openFileDescriptor(&start_offset, &length); - asset.reset(); - - if (fd < 0) { - jniThrowException(env, "java/io/FileNotFoundException", - "This file can not be opened as a file descriptor; it is probably " - "compressed"); - return nullptr; - } - - jlong* offsets = reinterpret_cast(env->GetPrimitiveArrayCritical(out_offsets, 0)); - if (offsets == nullptr) { - close(fd); - return nullptr; - } - - offsets[0] = start_offset; - offsets[1] = length; - - env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0); - - jobject file_desc = jniCreateFileDescriptor(env, fd); - if (file_desc == nullptr) { - close(fd); - return nullptr; - } - return newParcelFileDescriptor(env, file_desc); +static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz, + jstring fileName, jlongArray outOffsets) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ALOGV("openAssetFd in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return NULL; + } + + Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return NULL; + } + + //printf("Created Asset Stream: %p\n", a); + + return returnParcelFileDescriptor(env, a, outOffsets); } -static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) { - return Asset::getGlobalCount(); +static jlong android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz, + jint cookie, + jstring fileName, + jint mode) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return -1; + } + + if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM + && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return -1; + } + + Asset* a = cookie + ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), + (Asset::AccessMode)mode) + : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return -1; + } + + //printf("Created Asset Stream: %p\n", a); + + return reinterpret_cast(a); } -static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) { - String8 alloc = Asset::getAssetAllocations(); - if (alloc.length() <= 0) { - return nullptr; - } - return env->NewStringUTF(alloc.string()); +static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz, + jint cookie, + jstring fileName, + jlongArray outOffsets) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ALOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return NULL; + } + + Asset* a = cookie + ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), Asset::ACCESS_RANDOM) + : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return NULL; + } + + //printf("Created Asset Stream: %p\n", a); + + return returnParcelFileDescriptor(env, a, outOffsets); } -static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) { - // TODO(adamlesinski): Switch to AssetManager2. - return AssetManager::getGlobalCount(); +static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz, + jstring fileName) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return NULL; + } + + AssetDir* dir = am->openDir(fileName8.c_str()); + + if (dir == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return NULL; + } + + size_t N = dir->getFileCount(); + + jobjectArray array = env->NewObjectArray(dir->getFileCount(), + g_stringClass, NULL); + if (array == NULL) { + delete dir; + return NULL; + } + + for (size_t i=0; igetFileName(i); + jstring str = env->NewStringUTF(name.string()); + if (str == NULL) { + delete dir; + return NULL; + } + env->SetObjectArrayElement(array, i, str); + env->DeleteLocalRef(str); + } + + delete dir; + + return array; } -static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) { - // AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and - // AssetManager2 in a contiguous block (GuardedAssetManager). - return reinterpret_cast(new GuardedAssetManager()); +static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz, + jlong assetHandle) +{ + Asset* a = reinterpret_cast(assetHandle); + + //printf("Destroying Asset Stream: %p\n", a); + + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return; + } + + delete a; } -static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - delete reinterpret_cast(ptr); +static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz, + jlong assetHandle) +{ + Asset* a = reinterpret_cast(assetHandle); + + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; + } + + uint8_t b; + ssize_t res = a->read(&b, 1); + return res == 1 ? b : -1; } -static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr, - jobjectArray apk_assets_array, jboolean invalidate_caches) { - const jsize apk_assets_len = env->GetArrayLength(apk_assets_array); - std::vector apk_assets; - apk_assets.reserve(apk_assets_len); - for (jsize i = 0; i < apk_assets_len; i++) { - jobject obj = env->GetObjectArrayElement(apk_assets_array, i); - if (obj == nullptr) { - std::string msg = StringPrintf("ApkAssets at index %d is null", i); - jniThrowNullPointerException(env, msg.c_str()); - return; +static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz, + jlong assetHandle, jbyteArray bArray, + jint off, jint len) +{ + Asset* a = reinterpret_cast(assetHandle); + + if (a == NULL || bArray == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; } - jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr); - if (env->ExceptionCheck()) { - return; + if (len == 0) { + return 0; + } + + jsize bLen = env->GetArrayLength(bArray); + if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); + return -1; } - apk_assets.push_back(reinterpret_cast(apk_assets_native_ptr)); - } - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - assetmanager->SetApkAssets(apk_assets, invalidate_caches); + jbyte* b = env->GetByteArrayElements(bArray, NULL); + ssize_t res = a->read(b+off, len); + env->ReleaseByteArrayElements(bArray, b, 0); + + if (res > 0) return static_cast(res); + + if (res < 0) { + jniThrowException(env, "java/io/IOException", ""); + } + return -1; } -static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc, - jstring locale, jint orientation, jint touchscreen, jint density, - jint keyboard, jint keyboard_hidden, jint navigation, - jint screen_width, jint screen_height, - jint smallest_screen_width_dp, jint screen_width_dp, - jint screen_height_dp, jint screen_layout, jint ui_mode, - jint color_mode, jint major_version) { - ResTable_config configuration; - memset(&configuration, 0, sizeof(configuration)); - configuration.mcc = static_cast(mcc); - configuration.mnc = static_cast(mnc); - configuration.orientation = static_cast(orientation); - configuration.touchscreen = static_cast(touchscreen); - configuration.density = static_cast(density); - configuration.keyboard = static_cast(keyboard); - configuration.inputFlags = static_cast(keyboard_hidden); - configuration.navigation = static_cast(navigation); - configuration.screenWidth = static_cast(screen_width); - configuration.screenHeight = static_cast(screen_height); - configuration.smallestScreenWidthDp = static_cast(smallest_screen_width_dp); - configuration.screenWidthDp = static_cast(screen_width_dp); - configuration.screenHeightDp = static_cast(screen_height_dp); - configuration.screenLayout = static_cast(screen_layout); - configuration.uiMode = static_cast(ui_mode); - configuration.colorMode = static_cast(color_mode); - configuration.sdkVersion = static_cast(major_version); - - if (locale != nullptr) { - ScopedUtfChars locale_utf8(env, locale); - CHECK(locale_utf8.c_str() != nullptr); - configuration.setBcp47Locale(locale_utf8.c_str()); - } - - // Constants duplicated from Java class android.content.res.Configuration. - static const jint kScreenLayoutRoundMask = 0x300; - static const jint kScreenLayoutRoundShift = 8; - - // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer - // in C++. We must extract the round qualifier out of the Java screenLayout and put it - // into screenLayout2. - configuration.screenLayout2 = - static_cast((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - assetmanager->SetConfiguration(configuration); +static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz, + jlong assetHandle, + jlong offset, jint whence) +{ + Asset* a = reinterpret_cast(assetHandle); + + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; + } + + return a->seek( + offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)); } -static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); +static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz, + jlong assetHandle) +{ + Asset* a = reinterpret_cast(assetHandle); - jobject sparse_array = - env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor); + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; + } - if (sparse_array == nullptr) { - // An exception is pending. - return nullptr; - } + return a->getLength(); +} - assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) { - jstring jpackage_name = env->NewStringUTF(package_name.c_str()); - if (jpackage_name == nullptr) { - // An exception is pending. - return; +static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz, + jlong assetHandle) +{ + Asset* a = reinterpret_cast(assetHandle); + + if (a == NULL) { + jniThrowNullPointerException(env, "asset"); + return -1; } - env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast(package_id), - jpackage_name); - }); - return sparse_array; + return a->getRemainingLength(); } -static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) { - ScopedUtfChars path_utf8(env, path); - if (path_utf8.c_str() == nullptr) { - // This will throw NPE. - return nullptr; - } - - std::vector all_file_paths; - { - StringPiece normalized_path = path_utf8.c_str(); - if (normalized_path.data()[0] == '/') { - normalized_path = normalized_path.substr(1); - } - std::string root_path = StringPrintf("assets/%s", normalized_path.data()); - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - for (const ApkAssets* assets : assetmanager->GetApkAssets()) { - assets->ForEachFile(root_path, [&](const StringPiece& file_path, FileType type) { - if (type == FileType::kFileTypeRegular) { - all_file_paths.push_back(file_path.to_string()); - } - }); +static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz, + jstring path, jboolean appAsLib) +{ + ScopedUtfChars path8(env, path); + if (path8.c_str() == NULL) { + return 0; } - } - jobjectArray array = env->NewObjectArray(all_file_paths.size(), g_stringClass, nullptr); - if (array == nullptr) { - return nullptr; - } + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } - jsize index = 0; - for (const std::string& file_path : all_file_paths) { - jstring java_string = env->NewStringUTF(file_path.c_str()); + int32_t cookie; + bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib); - // Check for errors creating the strings (if malformed or no memory). - if (env->ExceptionCheck()) { - return nullptr; + return (res) ? static_cast(cookie) : 0; +} + +static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz, + jstring idmapPath) +{ + ScopedUtfChars idmapPath8(env, idmapPath); + if (idmapPath8.c_str() == NULL) { + return 0; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; } - env->SetObjectArrayElement(array, index++, java_string); + int32_t cookie; + bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie); - // If we have a large amount of string in our array, we might overflow the - // local reference table of the VM. - env->DeleteLocalRef(java_string); - } - return array; + return (res) ? (jint)cookie : 0; } -static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, - jint access_mode) { - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return 0; - } - - if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && - access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return 0; - } - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr asset = - assetmanager->Open(asset_path_utf8.c_str(), static_cast(access_mode)); - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return 0; - } - return reinterpret_cast(asset.release()); +static jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz, + jobject fileDescriptor, jstring debugPathName, + jboolean appAsLib) +{ + ScopedUtfChars debugPathName8(env, debugPathName); + + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (fd < 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); + return 0; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + int dupfd = ::dup(fd); + if (dupfd < 0) { + jniThrowIOException(env, errno); + return 0; + } + + int32_t cookie; + bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib); + + return (res) ? static_cast(cookie) : 0; } -static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, - jlongArray out_offsets) { - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return nullptr; - } - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return nullptr; - } - return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); +static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_TRUE; + } + return am->isUpToDate() ? JNI_TRUE : JNI_FALSE; } -static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, - jstring asset_path, jint access_mode) { - ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return 0; - } - - if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && - access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return 0; - } - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr asset; - if (cookie != kInvalidCookie) { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, - static_cast(access_mode)); - } else { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), - static_cast(access_mode)); - } - - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return 0; - } - return reinterpret_cast(asset.release()); +static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales) +{ + Vector locales; + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + am->getLocales(&locales, includeSystemLocales); + + const int N = locales.size(); + + jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL); + if (result == NULL) { + return NULL; + } + + for (int i=0; iNewStringUTF(locales[i].string()); + if (str == NULL) { + return NULL; + } + env->SetObjectArrayElement(result, i, str); + env->DeleteLocalRef(str); + } + + return result; } -static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, - jstring asset_path, jlongArray out_offsets) { - ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return nullptr; - } - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr asset; - if (cookie != kInvalidCookie) { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); - } else { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); - } - - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return nullptr; - } - return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); +static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz) +{ + return getLocales(env, clazz, true /* include system locales */); } -static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie, - jstring asset_path) { - ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); - ScopedUtfChars asset_path_utf8(env, asset_path); - if (asset_path_utf8.c_str() == nullptr) { - // This will throw NPE. - return 0; - } - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::unique_ptr asset; - if (cookie != kInvalidCookie) { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); - } else { - asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie); - } - - if (!asset) { - jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); - return 0; - } - - // May be nullptr. - const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie); - - std::unique_ptr xml_tree = util::make_unique(dynamic_ref_table); - status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); - asset.reset(); - - if (err != NO_ERROR) { - jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); - return 0; - } - return reinterpret_cast(xml_tree.release()); +static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz) +{ + return getLocales(env, clazz, false /* don't include system locales */); } -static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, - jshort density, jobject typed_value, - jboolean resolve_references) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Res_value value; - ResTable_config selected_config; - uint32_t flags; - ApkAssetsCookie cookie = - assetmanager->GetResource(static_cast(resid), false /*may_be_bag*/, - static_cast(density), &value, &selected_config, &flags); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - - uint32_t ref = static_cast(resid); - if (resolve_references) { - cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - } - return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value); +static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) { + jobject result = env->NewObject(gConfigurationOffsets.classObject, + gConfigurationOffsets.constructor); + if (result == NULL) { + return NULL; + } + + env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, + config.smallestScreenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); + + return result; } -static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, - jint bag_entry_id, jobject typed_value) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - - uint32_t type_spec_flags = bag->type_spec_flags; - ApkAssetsCookie cookie = kInvalidCookie; - const Res_value* bag_value = nullptr; - for (const ResolvedBag::Entry& entry : bag) { - if (entry.key == static_cast(bag_entry_id)) { - cookie = entry.cookie; - bag_value = &entry.value; - - // Keep searching (the old implementation did that). - } - } - - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - - Res_value value = *bag_value; - uint32_t ref = static_cast(resid); - ResTable_config selected_config; - cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value); +static jobjectArray getSizeConfigurationsInternal(JNIEnv* env, + const Vector& configs) { + const int N = configs.size(); + jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL); + if (result == NULL) { + return NULL; + } + + for (int i=0; iDeleteLocalRef(result); + return NULL; + } + + env->SetObjectArrayElement(result, i, config); + env->DeleteLocalRef(config); + } + + return result; } -static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return nullptr; - } - - jintArray array = env->NewIntArray(bag->entry_count); - if (env->ExceptionCheck()) { - return nullptr; - } - - for (uint32_t i = 0; i < bag->entry_count; i++) { - jint attr_resid = bag->entries[i].key; - env->SetIntArrayRegion(array, i, 1, &attr_resid); - } - return array; +static jobjectArray android_content_AssetManager_getSizeConfigurations(JNIEnv* env, jobject clazz) { + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + const ResTable& res(am->getResources()); + Vector configs; + res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */); + + return getSizeConfigurationsInternal(env, configs); } -static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, - jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return nullptr; - } - - jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr); - if (array == nullptr) { - return nullptr; - } - - for (uint32_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - - // Resolve any references to their final value. - Res_value value = entry.value; - ResTable_config selected_config; - uint32_t flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - return nullptr; - } - - if (value.dataType == Res_value::TYPE_STRING) { - const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie]; - const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool(); - - jstring java_string = nullptr; - size_t str_len; - const char* str_utf8 = pool->string8At(value.data, &str_len); - if (str_utf8 != nullptr) { - java_string = env->NewStringUTF(str_utf8); - } else { - const char16_t* str_utf16 = pool->stringAt(value.data, &str_len); - java_string = env->NewString(reinterpret_cast(str_utf16), str_len); - } - - // Check for errors creating the strings (if malformed or no memory). - if (env->ExceptionCheck()) { - return nullptr; - } - - env->SetObjectArrayElement(array, i, java_string); - - // If we have a large amount of string in our array, we might overflow the - // local reference table of the VM. - env->DeleteLocalRef(java_string); - } - } - return array; +static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz, + jint mcc, jint mnc, + jstring locale, jint orientation, + jint touchscreen, jint density, + jint keyboard, jint keyboardHidden, + jint navigation, + jint screenWidth, jint screenHeight, + jint smallestScreenWidthDp, + jint screenWidthDp, jint screenHeightDp, + jint screenLayout, jint uiMode, + jint colorMode, jint sdkVersion) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return; + } + + ResTable_config config; + memset(&config, 0, sizeof(config)); + + const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL; + + // Constants duplicated from Java class android.content.res.Configuration. + static const jint kScreenLayoutRoundMask = 0x300; + static const jint kScreenLayoutRoundShift = 8; + + config.mcc = (uint16_t)mcc; + config.mnc = (uint16_t)mnc; + config.orientation = (uint8_t)orientation; + config.touchscreen = (uint8_t)touchscreen; + config.density = (uint16_t)density; + config.keyboard = (uint8_t)keyboard; + config.inputFlags = (uint8_t)keyboardHidden; + config.navigation = (uint8_t)navigation; + config.screenWidth = (uint16_t)screenWidth; + config.screenHeight = (uint16_t)screenHeight; + config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp; + config.screenWidthDp = (uint16_t)screenWidthDp; + config.screenHeightDp = (uint16_t)screenHeightDp; + config.screenLayout = (uint8_t)screenLayout; + config.uiMode = (uint8_t)uiMode; + config.colorMode = (uint8_t)colorMode; + config.sdkVersion = (uint16_t)sdkVersion; + config.minorVersion = 0; + + // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer + // in C++. We must extract the round qualifier out of the Java screenLayout and put it + // into screenLayout2. + config.screenLayout2 = + (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); + + am->setConfiguration(config, locale8); + + if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8); } -static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr, - jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return nullptr; - } - - jintArray array = env->NewIntArray(bag->entry_count * 2); - if (array == nullptr) { - return nullptr; - } - - jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); - if (buffer == nullptr) { - return nullptr; - } - - for (size_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - Res_value value = entry.value; - ResTable_config selected_config; - uint32_t flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); - return nullptr; - } - - jint string_index = -1; - if (value.dataType == Res_value::TYPE_STRING) { - string_index = static_cast(value.data); - } - - buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie); - buffer[(i * 2) + 1] = string_index; - } - env->ReleasePrimitiveArrayCritical(array, buffer, 0); - return array; +static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz, + jstring name, + jstring defType, + jstring defPackage) +{ + ScopedStringChars name16(env, name); + if (name16.get() == NULL) { + return 0; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + const char16_t* defType16 = reinterpret_cast(defType) + ? reinterpret_cast(env->GetStringChars(defType, NULL)) + : NULL; + jsize defTypeLen = defType + ? env->GetStringLength(defType) : 0; + const char16_t* defPackage16 = reinterpret_cast(defPackage) + ? reinterpret_cast(env->GetStringChars(defPackage, + NULL)) + : NULL; + jsize defPackageLen = defPackage + ? env->GetStringLength(defPackage) : 0; + + jint ident = am->getResources().identifierForName( + reinterpret_cast(name16.get()), name16.size(), + defType16, defTypeLen, defPackage16, defPackageLen); + + if (defPackage16) { + env->ReleaseStringChars(defPackage, + reinterpret_cast(defPackage16)); + } + if (defType16) { + env->ReleaseStringChars(defType, + reinterpret_cast(defType16)); + } + + return ident; } -static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return nullptr; - } - - jintArray array = env->NewIntArray(bag->entry_count); - if (array == nullptr) { - return nullptr; - } - - jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); - if (buffer == nullptr) { - return nullptr; - } - - for (size_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - Res_value value = entry.value; - ResTable_config selected_config; - uint32_t flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); - return nullptr; - } - - if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) { - buffer[i] = static_cast(value.data); - } - } - env->ReleasePrimitiveArrayCritical(array, buffer, 0); - return array; +static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, true, &name)) { + return NULL; + } + + String16 str; + if (name.package != NULL) { + str.setTo(name.package, name.packageLen); + } + if (name.type8 != NULL || name.type != NULL) { + if (str.size() > 0) { + char16_t div = ':'; + str.append(&div, 1); + } + if (name.type8 != NULL) { + str.append(String16(name.type8, name.typeLen)); + } else { + str.append(name.type, name.typeLen); + } + } + if (name.name8 != NULL || name.name != NULL) { + if (str.size() > 0) { + char16_t div = '/'; + str.append(&div, 1); + } + if (name.name8 != NULL) { + str.append(String16(name.name8, name.nameLen)); + } else { + str.append(name.name, name.nameLen); + } + } + + return env->NewString((const jchar*)str.string(), str.size()); } -static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return -1; - } - return static_cast(bag->entry_count); +static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, true, &name)) { + return NULL; + } + + if (name.package != NULL) { + return env->NewString((const jchar*)name.package, name.packageLen); + } + + return NULL; } -static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, - jintArray out_data) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); - if (bag == nullptr) { - return -1; - } +static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } - const jsize out_data_length = env->GetArrayLength(out_data); - if (env->ExceptionCheck()) { - return -1; - } + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, true, &name)) { + return NULL; + } - if (static_cast(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough"); - return -1; - } + if (name.type8 != NULL) { + return env->NewStringUTF(name.type8); + } - jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(out_data, nullptr)); - if (buffer == nullptr) { - return -1; - } - - jint* cursor = buffer; - for (size_t i = 0; i < bag->entry_count; i++) { - const ResolvedBag::Entry& entry = bag->entries[i]; - Res_value value = entry.value; - ResTable_config selected_config; - selected_config.density = 0; - uint32_t flags = bag->type_spec_flags; - uint32_t ref; - ApkAssetsCookie cookie = - assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT); - return -1; - } - - // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; - } - - cursor[STYLE_TYPE] = static_cast(value.dataType); - cursor[STYLE_DATA] = static_cast(value.data); - cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); - cursor[STYLE_RESOURCE_ID] = static_cast(ref); - cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast(flags); - cursor[STYLE_DENSITY] = static_cast(selected_config.density); - cursor += STYLE_NUM_ENTRIES; - } - env->ReleasePrimitiveArrayCritical(out_data, buffer, 0); - return static_cast(bag->entry_count); + if (name.type != NULL) { + return env->NewString((const jchar*)name.type, name.typeLen); + } + + return NULL; } -static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name, - jstring def_type, jstring def_package) { - ScopedUtfChars name_utf8(env, name); - if (name_utf8.c_str() == nullptr) { - // This will throw NPE. - return 0; - } - - std::string type; - if (def_type != nullptr) { - ScopedUtfChars type_utf8(env, def_type); - CHECK(type_utf8.c_str() != nullptr); - type = type_utf8.c_str(); - } - - std::string package; - if (def_package != nullptr) { - ScopedUtfChars package_utf8(env, def_package); - CHECK(package_utf8.c_str() != nullptr); - package = package_utf8.c_str(); - } - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - return static_cast(assetmanager->GetResourceId(name_utf8.c_str(), type, package)); +static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz, + jint resid) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + + ResTable::resource_name name; + if (!am->getResources().getResourceName(resid, true, &name)) { + return NULL; + } + + if (name.name8 != NULL) { + return env->NewStringUTF(name.name8); + } + + if (name.name != NULL) { + return env->NewString((const jchar*)name.name, name.nameLen); + } + + return NULL; } -static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast(resid), &name)) { - return nullptr; - } +static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz, + jint ident, + jshort density, + jobject outValue, + jboolean resolve) +{ + if (outValue == NULL) { + jniThrowNullPointerException(env, "outValue"); + return 0; + } + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + const ResTable& res(am->getResources()); + + Res_value value; + ResTable_config config; + uint32_t typeSpecFlags; + ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return 0; + } + } + uint32_t ref = ident; + if (resolve) { + block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return 0; + } + } + } + if (block >= 0) { + return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config); + } - std::string result; - if (name.package != nullptr) { - result.append(name.package, name.package_len); - } + return static_cast(block); +} - if (name.type != nullptr || name.type16 != nullptr) { - if (!result.empty()) { - result += ":"; +static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz, + jint ident, jint bagEntryId, + jobject outValue, jboolean resolve) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; } + const ResTable& res(am->getResources()); + + // Now lock down the resource object and start pulling stuff from it. + res.lock(); + + ssize_t block = -1; + Res_value value; - if (name.type != nullptr) { - result.append(name.type, name.type_len); - } else { - result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len)); + const ResTable::bag_entry* entry = NULL; + uint32_t typeSpecFlags; + ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags); + + for (ssize_t i=0; imap.name.ident) { + block = entry->stringBlock; + value = entry->map.value; + } + entry++; } - } - if (name.entry != nullptr || name.entry16 != nullptr) { - if (!result.empty()) { - result += "/"; + res.unlock(); + + if (block < 0) { + return static_cast(block); } - if (name.entry != nullptr) { - result.append(name.entry, name.entry_len); - } else { - result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); + uint32_t ref = ident; + if (resolve) { + block = res.resolveReference(&value, block, &ref, &typeSpecFlags); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return 0; + } + } + } + if (block >= 0) { + return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags); } - } - return env->NewStringUTF(result.c_str()); + + return static_cast(block); } -static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast(resid), &name)) { - return nullptr; - } - - if (name.package != nullptr) { - return env->NewStringUTF(name.package); - } - return nullptr; +static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + return am->getResources().getTableCount(); } -static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast(resid), &name)) { - return nullptr; - } - - if (name.type != nullptr) { - return env->NewStringUTF(name.type); - } else if (name.type16 != nullptr) { - return env->NewString(reinterpret_cast(name.type16), name.type_len); - } - return nullptr; +static jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz, + jint block) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + return reinterpret_cast(am->getResources().getTableStringBlock(block)); } -static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - AssetManager2::ResourceName name; - if (!assetmanager->GetResourceName(static_cast(resid), &name)) { - return nullptr; - } - - if (name.entry != nullptr) { - return env->NewStringUTF(name.entry); - } else if (name.entry16 != nullptr) { - return env->NewString(reinterpret_cast(name.entry16), name.entry_len); - } - return nullptr; +static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz, + jint cookie) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + String8 name(am->getAssetPath(static_cast(cookie))); + if (name.length() == 0) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name"); + return NULL; + } + jstring str = env->NewStringUTF(name.string()); + return str; } -static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr, - jboolean exclude_system) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::set locales = - assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/); - - jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr); - if (array == nullptr) { - return nullptr; - } - - size_t idx = 0; - for (const std::string& locale : locales) { - jstring java_string = env->NewStringUTF(locale.c_str()); - if (java_string == nullptr) { - return nullptr; - } - env->SetObjectArrayElement(array, idx++, java_string); - env->DeleteLocalRef(java_string); - } - return array; +static jobject android_content_AssetManager_getAssignedPackageIdentifiers(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + const ResTable& res = am->getResources(); + + jobject sparseArray = env->NewObject(gSparseArrayOffsets.classObject, + gSparseArrayOffsets.constructor); + const size_t N = res.getBasePackageCount(); + for (size_t i = 0; i < N; i++) { + const String16 name = res.getBasePackageName(i); + env->CallVoidMethod( + sparseArray, gSparseArrayOffsets.put, + static_cast(res.getBasePackageId(i)), + env->NewString(reinterpret_cast(name.string()), + name.size())); + } + return sparseArray; } -static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) { - jobject result = - env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor); - if (result == nullptr) { - return nullptr; - } - - env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, - config.smallestScreenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); - return result; +static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + return reinterpret_cast(new ResTable::Theme(am->getResources())); } -static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - std::set configurations = - assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/); +static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz, + jlong themeHandle) +{ + ResTable::Theme* theme = reinterpret_cast(themeHandle); + delete theme; +} - jobjectArray array = - env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr); - if (array == nullptr) { - return nullptr; - } +static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz, + jlong themeHandle, + jint styleRes, + jboolean force) +{ + ResTable::Theme* theme = reinterpret_cast(themeHandle); + theme->applyStyle(styleRes, force ? true : false); +} - size_t idx = 0; - for (const ResTable_config& configuration : configurations) { - jobject java_configuration = ConstructConfigurationObject(env, configuration); - if (java_configuration == nullptr) { - return nullptr; +static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz, + jlong destHandle, jlong srcHandle) +{ + ResTable::Theme* dest = reinterpret_cast(destHandle); + ResTable::Theme* src = reinterpret_cast(srcHandle); + dest->setTo(*src); +} + +static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle) +{ + ResTable::Theme* theme = reinterpret_cast(themeHandle); + theme->clear(); +} + +static jint android_content_AssetManager_loadThemeAttributeValue( + JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve) +{ + ResTable::Theme* theme = reinterpret_cast(themeHandle); + const ResTable& res(theme->getResTable()); + + Res_value value; + // XXX value could be different in different configs! + uint32_t typeSpecFlags = 0; + ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags); + uint32_t ref = 0; + if (resolve) { + block = res.resolveReference(&value, block, &ref, &typeSpecFlags); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return 0; + } + } } + return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block; +} - env->SetObjectArrayElement(array, idx++, java_configuration); - env->DeleteLocalRef(java_configuration); - } - return array; +static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz, + jlong themeHandle) +{ + ResTable::Theme* theme = reinterpret_cast(themeHandle); + return theme->getChangingConfigurations(); } -static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr, - jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - - ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); - uint32_t* out_values = reinterpret_cast(out_values_ptr); - uint32_t* out_indices = reinterpret_cast(out_indices_ptr); - - jsize attrs_len = env->GetArrayLength(java_attrs); - jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); - if (attrs == nullptr) { - return; - } - - ApplyStyle(theme, xml_parser, static_cast(def_style_attr), - static_cast(def_style_resid), reinterpret_cast(attrs), attrs_len, - out_values, out_indices); - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); +static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz, + jlong themeHandle, jint pri, + jstring tag, jstring prefix) +{ + ResTable::Theme* theme = reinterpret_cast(themeHandle); + const ResTable& res(theme->getResTable()); + (void)res; + + // XXX Need to use params. + theme->dumpToLog(); } -static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint def_style_attr, jint def_style_resid, jintArray java_values, - jintArray java_attrs, jintArray out_java_values, - jintArray out_java_indices) { - const jsize attrs_len = env->GetArrayLength(java_attrs); - const jsize out_values_len = env->GetArrayLength(out_java_values); - if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); - return JNI_FALSE; - } - - jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); - if (attrs == nullptr) { - return JNI_FALSE; - } - - jint* values = nullptr; - jsize values_len = 0; - if (java_values != nullptr) { - values_len = env->GetArrayLength(java_values); - values = reinterpret_cast(env->GetPrimitiveArrayCritical(java_values, nullptr)); - if (values == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - return JNI_FALSE; - } - } - - jint* out_values = - reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); - if (out_values == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - if (values != nullptr) { - env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); - } - return JNI_FALSE; - } - - jint* out_indices = nullptr; - if (out_java_indices != nullptr) { - jsize out_indices_len = env->GetArrayLength(out_java_indices); - if (out_indices_len > attrs_len) { - out_indices = - reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); - if (out_indices == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - if (values != nullptr) { - env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); - } - env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); +static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz, + jlong themeToken, + jint defStyleAttr, + jint defStyleRes, + jintArray inValues, + jintArray attrs, + jintArray outValues, + jintArray outIndices) +{ + if (themeToken == 0) { + jniThrowNullPointerException(env, "theme token"); return JNI_FALSE; - } - } - } - - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - - bool result = ResolveAttrs( - theme, static_cast(def_style_attr), static_cast(def_style_resid), - reinterpret_cast(values), values_len, reinterpret_cast(attrs), - attrs_len, reinterpret_cast(out_values), reinterpret_cast(out_indices)); - if (out_indices != nullptr) { - env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); - } - - env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); - if (values != nullptr) { - env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); - } - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - return result ? JNI_TRUE : JNI_FALSE; -} + } + if (attrs == NULL) { + jniThrowNullPointerException(env, "attrs"); + return JNI_FALSE; + } + if (outValues == NULL) { + jniThrowNullPointerException(env, "out values"); + return JNI_FALSE; + } -static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, - jlong xml_parser_ptr, jintArray java_attrs, - jintArray out_java_values, jintArray out_java_indices) { - const jsize attrs_len = env->GetArrayLength(java_attrs); - const jsize out_values_len = env->GetArrayLength(out_java_values); - if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); - return JNI_FALSE; - } - - jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); - if (attrs == nullptr) { - return JNI_FALSE; - } - - jint* out_values = - reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); - if (out_values == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - return JNI_FALSE; - } - - jint* out_indices = nullptr; - if (out_java_indices != nullptr) { - jsize out_indices_len = env->GetArrayLength(out_java_indices); - if (out_indices_len > attrs_len) { - out_indices = - reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); - if (out_indices == nullptr) { - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); + const jsize NI = env->GetArrayLength(attrs); + const jsize NV = env->GetArrayLength(outValues); + if (NV < (NI*STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); return JNI_FALSE; - } } - } - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); + jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); + if (src == NULL) { + return JNI_FALSE; + } - bool result = RetrieveAttributes(assetmanager.get(), xml_parser, - reinterpret_cast(attrs), attrs_len, - reinterpret_cast(out_values), - reinterpret_cast(out_indices)); + jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0); + const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues); - if (out_indices != nullptr) { - env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); - } - env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - return result ? JNI_TRUE : JNI_FALSE; -} + jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + if (baseDest == NULL) { + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + return JNI_FALSE; + } + + jint* indices = NULL; + if (outIndices != NULL) { + if (env->GetArrayLength(outIndices) > NI) { + indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); + } + } -static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - return reinterpret_cast(assetmanager->NewTheme().release()); + ResTable::Theme* theme = reinterpret_cast(themeToken); + bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes, + (uint32_t*) srcValues, NSV, + (uint32_t*) src, NI, + (uint32_t*) baseDest, + (uint32_t*) indices); + + if (indices != NULL) { + env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); + } + env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); + env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0); + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + return result ? JNI_TRUE : JNI_FALSE; } -static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { - delete reinterpret_cast(theme_ptr); +static void android_content_AssetManager_applyStyle(JNIEnv* env, jobject, jlong themeToken, + jint defStyleAttr, jint defStyleRes, jlong xmlParserToken, jintArray attrsObj, jint length, + jlong outValuesAddress, jlong outIndicesAddress) { + jint* attrs = env->GetIntArrayElements(attrsObj, 0); + ResTable::Theme* theme = reinterpret_cast(themeToken); + ResXMLParser* xmlParser = reinterpret_cast(xmlParserToken); + uint32_t* outValues = reinterpret_cast(static_cast(outValuesAddress)); + uint32_t* outIndices = reinterpret_cast(static_cast(outIndicesAddress)); + ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes, + reinterpret_cast(attrs), length, outValues, outIndices); + env->ReleaseIntArrayElements(attrsObj, attrs, JNI_ABORT); } -static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint resid, jboolean force) { - // AssetManager is accessed via the theme, so grab an explicit lock here. - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - theme->ApplyStyle(static_cast(resid), force); - - // TODO(adamlesinski): Consider surfacing exception when result is failure. - // CTS currently expects no exceptions from this method. - // std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid); - // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str()); +static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz, + jlong xmlParserToken, + jintArray attrs, + jintArray outValues, + jintArray outIndices) +{ + if (xmlParserToken == 0) { + jniThrowNullPointerException(env, "xmlParserToken"); + return JNI_FALSE; + } + if (attrs == NULL) { + jniThrowNullPointerException(env, "attrs"); + return JNI_FALSE; + } + if (outValues == NULL) { + jniThrowNullPointerException(env, "out values"); + return JNI_FALSE; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_FALSE; + } + const ResTable& res(am->getResources()); + ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken; + + const jsize NI = env->GetArrayLength(attrs); + const jsize NV = env->GetArrayLength(outValues); + if (NV < (NI*STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); + return JNI_FALSE; + } + + jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); + if (src == NULL) { + return JNI_FALSE; + } + + jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + if (baseDest == NULL) { + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + return JNI_FALSE; + } + + jint* indices = NULL; + if (outIndices != NULL) { + if (env->GetArrayLength(outIndices) > NI) { + indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); + } + } + + bool result = RetrieveAttributes(&res, xmlParser, + (uint32_t*) src, NI, + (uint32_t*) baseDest, + (uint32_t*) indices); + + if (indices != NULL) { + env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); + } + env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); + env->ReleasePrimitiveArrayCritical(attrs, src, 0); + return result ? JNI_TRUE : JNI_FALSE; } -static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr, - jlong src_theme_ptr) { - Theme* dst_theme = reinterpret_cast(dst_theme_ptr); - Theme* src_theme = reinterpret_cast(src_theme_ptr); - if (!dst_theme->SetTo(*src_theme)) { - jniThrowException(env, "java/lang/IllegalArgumentException", - "Themes are from different AssetManagers"); - } +static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz, + jint id) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + const ResTable& res(am->getResources()); + + res.lock(); + const ResTable::bag_entry* defStyleEnt = NULL; + ssize_t bagOff = res.getBagLocked(id, &defStyleEnt); + res.unlock(); + + return static_cast(bagOff); } -static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { - reinterpret_cast(theme_ptr)->Clear(); +static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz, + jint id, + jintArray outValues) +{ + if (outValues == NULL) { + jniThrowNullPointerException(env, "out values"); + return JNI_FALSE; + } + + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_FALSE; + } + const ResTable& res(am->getResources()); + ResTable_config config; + Res_value value; + ssize_t block; + + const jsize NV = env->GetArrayLength(outValues); + + jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); + jint* dest = baseDest; + if (dest == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", ""); + return JNI_FALSE; + } + + // Now lock down the resource object and start pulling stuff from it. + res.lock(); + + const ResTable::bag_entry* arrayEnt = NULL; + uint32_t arrayTypeSetFlags = 0; + ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags); + const ResTable::bag_entry* endArrayEnt = arrayEnt + + (bagOff >= 0 ? bagOff : 0); + + int i = 0; + uint32_t typeSetFlags; + while (i < NV && arrayEnt < endArrayEnt) { + block = arrayEnt->stringBlock; + typeSetFlags = arrayTypeSetFlags; + config.density = 0; + value = arrayEnt->map.value; + + uint32_t resid = 0; + if (value.dataType != Res_value::TYPE_NULL) { + // Take care of resolving the found resource to its final value. + //printf("Resolving attribute reference\n"); + ssize_t newBlock = res.resolveReference(&value, block, &resid, + &typeSetFlags, &config); + if (kThrowOnBadId) { + if (newBlock == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return JNI_FALSE; + } + } + if (newBlock >= 0) block = newBlock; + } + + // Deal with the special @null value -- it turns back to TYPE_NULL. + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + } + + //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data); + + // Write the final value back to Java. + dest[STYLE_TYPE] = value.dataType; + dest[STYLE_DATA] = value.data; + dest[STYLE_ASSET_COOKIE] = reinterpret_cast(res.getTableCookie(block)); + dest[STYLE_RESOURCE_ID] = resid; + dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags; + dest[STYLE_DENSITY] = config.density; + dest += STYLE_NUM_ENTRIES; + i+= STYLE_NUM_ENTRIES; + arrayEnt++; + } + + i /= STYLE_NUM_ENTRIES; + + res.unlock(); + + env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); + + return i; } -static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint resid, jobject typed_value, - jboolean resolve_references) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - - Res_value value; - uint32_t flags; - ApkAssetsCookie cookie = theme->GetAttribute(static_cast(resid), &value, &flags); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - - uint32_t ref = 0u; - if (resolve_references) { - ResTable_config selected_config; - cookie = - theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref); - if (cookie == kInvalidCookie) { - return ApkAssetsCookieToJavaCookie(kInvalidCookie); - } - } - return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value); +static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz, + jint cookie, + jstring fileName) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return 0; + } + + ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz); + + ScopedUtfChars fileName8(env, fileName); + if (fileName8.c_str() == NULL) { + return 0; + } + + int32_t assetCookie = static_cast(cookie); + Asset* a = assetCookie + ? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER) + : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie); + + if (a == NULL) { + jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); + return 0; + } + + const DynamicRefTable* dynamicRefTable = + am->getResources().getDynamicRefTableForCookie(assetCookie); + ResXMLTree* block = new ResXMLTree(dynamicRefTable); + status_t err = block->setTo(a->getBuffer(true), a->getLength(), true); + a->close(); + delete a; + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + + return reinterpret_cast(block); } -static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr, - jint priority, jstring tag, jstring prefix) { - ScopedLock assetmanager(AssetManagerFromLong(ptr)); - Theme* theme = reinterpret_cast(theme_ptr); - CHECK(theme->GetAssetManager() == &(*assetmanager)); - (void) assetmanager; - (void) theme; - (void) priority; - (void) tag; - (void) prefix; +static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz, + jint arrayResId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(arrayResId, &startOfBag); + if (N < 0) { + return NULL; + } + + jintArray array = env->NewIntArray(N * 2); + if (array == NULL) { + res.unlockBag(startOfBag); + return NULL; + } + + Res_value value; + const ResTable::bag_entry* bag = startOfBag; + for (size_t i = 0, j = 0; ((ssize_t)i)map.value; + + // Take care of resolving the found resource to its final value. + stringBlock = res.resolveReference(&value, bag->stringBlock, NULL); + if (value.dataType == Res_value::TYPE_STRING) { + stringIndex = value.data; + } + + if (kThrowOnBadId) { + if (stringBlock == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return array; + } + } + + //todo: It might be faster to allocate a C array to contain + // the blocknums and indices, put them in there and then + // do just one SetIntArrayRegion() + env->SetIntArrayRegion(array, j, 1, &stringBlock); + env->SetIntArrayRegion(array, j + 1, 1, &stringIndex); + j = j + 2; + } + res.unlockBag(startOfBag); + return array; } -static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/, - jlong theme_ptr) { - Theme* theme = reinterpret_cast(theme_ptr); - return static_cast(theme->GetChangingConfigurations()); +static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz, + jint arrayResId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(arrayResId, &startOfBag); + if (N < 0) { + return NULL; + } + + jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL); + if (env->ExceptionCheck()) { + res.unlockBag(startOfBag); + return NULL; + } + + Res_value value; + const ResTable::bag_entry* bag = startOfBag; + size_t strLen = 0; + for (size_t i=0; ((ssize_t)i)map.value; + jstring str = NULL; + + // Take care of resolving the found resource to its final value. + ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return array; + } + } + if (value.dataType == Res_value::TYPE_STRING) { + const ResStringPool* pool = res.getTableStringBlock(block); + const char* str8 = pool->string8At(value.data, &strLen); + if (str8 != NULL) { + str = env->NewStringUTF(str8); + } else { + const char16_t* str16 = pool->stringAt(value.data, &strLen); + str = env->NewString(reinterpret_cast(str16), + strLen); + } + + // If one of our NewString{UTF} calls failed due to memory, an + // exception will be pending. + if (env->ExceptionCheck()) { + res.unlockBag(startOfBag); + return NULL; + } + + env->SetObjectArrayElement(array, i, str); + + // str is not NULL at that point, otherwise ExceptionCheck would have been true. + // If we have a large amount of strings in our array, we might + // overflow the local reference table of the VM. + env->DeleteLocalRef(str); + } + } + res.unlockBag(startOfBag); + return array; } -static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - delete reinterpret_cast(asset_ptr); +static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz, + jint arrayResId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(arrayResId, &startOfBag); + if (N < 0) { + return NULL; + } + + jintArray array = env->NewIntArray(N); + if (array == NULL) { + res.unlockBag(startOfBag); + return NULL; + } + + Res_value value; + const ResTable::bag_entry* bag = startOfBag; + for (size_t i=0; ((ssize_t)i)map.value; + + // Take care of resolving the found resource to its final value. + ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); + if (kThrowOnBadId) { + if (block == BAD_INDEX) { + jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); + return array; + } + } + if (value.dataType >= Res_value::TYPE_FIRST_INT + && value.dataType <= Res_value::TYPE_LAST_INT) { + int intVal = value.data; + env->SetIntArrayRegion(array, i, 1, &intVal); + } + } + res.unlockBag(startOfBag); + return array; } -static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - Asset* asset = reinterpret_cast(asset_ptr); - uint8_t b; - ssize_t res = asset->read(&b, sizeof(b)); - return res == sizeof(b) ? static_cast(b) : -1; +static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz, + jint styleId) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return NULL; + } + const ResTable& res(am->getResources()); + + const ResTable::bag_entry* startOfBag; + const ssize_t N = res.lockBag(styleId, &startOfBag); + if (N < 0) { + return NULL; + } + + jintArray array = env->NewIntArray(N); + if (array == NULL) { + res.unlockBag(startOfBag); + return NULL; + } + + const ResTable::bag_entry* bag = startOfBag; + for (size_t i=0; ((ssize_t)i)map.name.ident; + env->SetIntArrayRegion(array, i, 1, &resourceId); + } + res.unlockBag(startOfBag); + return array; } -static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer, - jint offset, jint len) { - if (len == 0) { - return 0; - } +static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem) +{ + if (isSystem) { + verifySystemIdmaps(); + } + AssetManager* am = new AssetManager(); + if (am == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", ""); + return; + } - jsize buffer_len = env->GetArrayLength(java_buffer); - if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len || - offset > buffer_len - len) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); - return -1; - } + am->addDefaultAssets(); - ScopedByteArrayRW byte_array(env, java_buffer); - if (byte_array.get() == nullptr) { - return -1; - } + ALOGV("Created AssetManager %p for Java object %p\n", am, clazz); + env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast(am)); +} - Asset* asset = reinterpret_cast(asset_ptr); - ssize_t res = asset->read(byte_array.get() + offset, len); - if (res < 0) { - jniThrowException(env, "java/io/IOException", ""); - return -1; - } - return res > 0 ? static_cast(res) : -1; +static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz) +{ + AssetManager* am = (AssetManager*) + (env->GetLongField(clazz, gAssetManagerOffsets.mObject)); + ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz); + if (am != NULL) { + delete am; + env->SetLongField(clazz, gAssetManagerOffsets.mObject, 0); + } } -static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset, - jint whence) { - Asset* asset = reinterpret_cast(asset_ptr); - return static_cast(asset->seek( - static_cast(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)))); +static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz) +{ + return Asset::getGlobalCount(); } -static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - Asset* asset = reinterpret_cast(asset_ptr); - return static_cast(asset->getLength()); +static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz) +{ + String8 alloc = Asset::getAssetAllocations(); + if (alloc.length() <= 0) { + return NULL; + } + + jstring str = env->NewStringUTF(alloc.string()); + return str; } -static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { - Asset* asset = reinterpret_cast(asset_ptr); - return static_cast(asset->getRemainingLength()); +static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz) +{ + return AssetManager::getGlobalCount(); } // ---------------------------------------------------------------------------- -// JNI registration. +/* + * JNI registration. + */ static const JNINativeMethod gAssetManagerMethods[] = { - // AssetManager setup methods. - {"nativeCreate", "()J", (void*)NativeCreate}, - {"nativeDestroy", "(J)V", (void*)NativeDestroy}, - {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets}, - {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V", - (void*)NativeSetConfiguration}, - {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;", - (void*)NativeGetAssignedPackageIdentifiers}, - - // AssetManager file methods. - {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList}, - {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset}, - {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*)NativeOpenAssetFd}, - {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset}, - {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*)NativeOpenNonAssetFd}, - {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset}, - - // AssetManager resource methods. - {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue}, - {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I", - (void*)NativeGetResourceBagValue}, - {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes}, - {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;", - (void*)NativeGetResourceStringArray}, - {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo}, - {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray}, - {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize}, - {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray}, - - // AssetManager resource name/ID methods. - {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", - (void*)NativeGetResourceIdentifier}, - {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName}, - {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName}, - {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName}, - {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName}, - {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales}, - {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;", - (void*)NativeGetSizeConfigurations}, - - // Style attribute related methods. - {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle}, - {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs}, - {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes}, - - // Theme related methods. - {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate}, - {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy}, - {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle}, - {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy}, - {"nativeThemeClear", "(J)V", (void*)NativeThemeClear}, - {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I", - (void*)NativeThemeGetAttributeValue}, - {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump}, - {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations}, - - // AssetInputStream methods. - {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy}, - {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar}, - {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead}, - {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek}, - {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength}, - {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength}, - - // System/idmap related methods. - {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps}, - - // Global management/debug methods. - {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount}, - {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations}, - {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount}, + /* name, signature, funcPtr */ + + // Basic asset stuff. + { "openAsset", "(Ljava/lang/String;I)J", + (void*) android_content_AssetManager_openAsset }, + { "openAssetFd", "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*) android_content_AssetManager_openAssetFd }, + { "openNonAssetNative", "(ILjava/lang/String;I)J", + (void*) android_content_AssetManager_openNonAssetNative }, + { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*) android_content_AssetManager_openNonAssetFdNative }, + { "list", "(Ljava/lang/String;)[Ljava/lang/String;", + (void*) android_content_AssetManager_list }, + { "destroyAsset", "(J)V", + (void*) android_content_AssetManager_destroyAsset }, + { "readAssetChar", "(J)I", + (void*) android_content_AssetManager_readAssetChar }, + { "readAsset", "(J[BII)I", + (void*) android_content_AssetManager_readAsset }, + { "seekAsset", "(JJI)J", + (void*) android_content_AssetManager_seekAsset }, + { "getAssetLength", "(J)J", + (void*) android_content_AssetManager_getAssetLength }, + { "getAssetRemainingLength", "(J)J", + (void*) android_content_AssetManager_getAssetRemainingLength }, + { "addAssetPathNative", "(Ljava/lang/String;Z)I", + (void*) android_content_AssetManager_addAssetPath }, + { "addAssetFdNative", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)I", + (void*) android_content_AssetManager_addAssetFd }, + { "addOverlayPathNative", "(Ljava/lang/String;)I", + (void*) android_content_AssetManager_addOverlayPath }, + { "isUpToDate", "()Z", + (void*) android_content_AssetManager_isUpToDate }, + + // Resources. + { "getLocales", "()[Ljava/lang/String;", + (void*) android_content_AssetManager_getLocales }, + { "getNonSystemLocales", "()[Ljava/lang/String;", + (void*) android_content_AssetManager_getNonSystemLocales }, + { "getSizeConfigurations", "()[Landroid/content/res/Configuration;", + (void*) android_content_AssetManager_getSizeConfigurations }, + { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIIII)V", + (void*) android_content_AssetManager_setConfiguration }, + { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + (void*) android_content_AssetManager_getResourceIdentifier }, + { "getResourceName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourceName }, + { "getResourcePackageName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourcePackageName }, + { "getResourceTypeName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourceTypeName }, + { "getResourceEntryName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getResourceEntryName }, + { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I", + (void*) android_content_AssetManager_loadResourceValue }, + { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I", + (void*) android_content_AssetManager_loadResourceBagValue }, + { "getStringBlockCount","()I", + (void*) android_content_AssetManager_getStringBlockCount }, + { "getNativeStringBlock","(I)J", + (void*) android_content_AssetManager_getNativeStringBlock }, + { "getCookieName","(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getCookieName }, + { "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;", + (void*) android_content_AssetManager_getAssignedPackageIdentifiers }, + + // Themes. + { "newTheme", "()J", + (void*) android_content_AssetManager_newTheme }, + { "deleteTheme", "(J)V", + (void*) android_content_AssetManager_deleteTheme }, + { "applyThemeStyle", "(JIZ)V", + (void*) android_content_AssetManager_applyThemeStyle }, + { "copyTheme", "(JJ)V", + (void*) android_content_AssetManager_copyTheme }, + { "clearTheme", "(J)V", + (void*) android_content_AssetManager_clearTheme }, + { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I", + (void*) android_content_AssetManager_loadThemeAttributeValue }, + { "getThemeChangingConfigurations", "(J)I", + (void*) android_content_AssetManager_getThemeChangingConfigurations }, + { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V", + (void*) android_content_AssetManager_dumpTheme }, + { "applyStyle","(JIIJ[IIJJ)V", + (void*) android_content_AssetManager_applyStyle }, + { "resolveAttrs","(JII[I[I[I[I)Z", + (void*) android_content_AssetManager_resolveAttrs }, + { "retrieveAttributes","(J[I[I[I)Z", + (void*) android_content_AssetManager_retrieveAttributes }, + { "getArraySize","(I)I", + (void*) android_content_AssetManager_getArraySize }, + { "retrieveArray","(I[I)I", + (void*) android_content_AssetManager_retrieveArray }, + + // XML files. + { "openXmlAssetNative", "(ILjava/lang/String;)J", + (void*) android_content_AssetManager_openXmlAssetNative }, + + // Arrays. + { "getArrayStringResource","(I)[Ljava/lang/String;", + (void*) android_content_AssetManager_getArrayStringResource }, + { "getArrayStringInfo","(I)[I", + (void*) android_content_AssetManager_getArrayStringInfo }, + { "getArrayIntResource","(I)[I", + (void*) android_content_AssetManager_getArrayIntResource }, + { "getStyleAttributes","(I)[I", + (void*) android_content_AssetManager_getStyleAttributes }, + + // Bookkeeping. + { "init", "(Z)V", + (void*) android_content_AssetManager_init }, + { "destroy", "()V", + (void*) android_content_AssetManager_destroy }, + { "getGlobalAssetCount", "()I", + (void*) android_content_AssetManager_getGlobalAssetCount }, + { "getAssetAllocations", "()Ljava/lang/String;", + (void*) android_content_AssetManager_getAssetAllocations }, + { "getGlobalAssetManagerCount", "()I", + (void*) android_content_AssetManager_getGlobalAssetManagerCount }, }; -int register_android_content_AssetManager(JNIEnv* env) { - jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets"); - gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J"); - - jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); - gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); - gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); - gTypedValueOffsets.mString = - GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;"); - gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); - gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); - gTypedValueOffsets.mChangingConfigurations = - GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I"); - gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); - - jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); - gAssetFileDescriptorOffsets.mFd = - GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;"); - gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); - gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); - - jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); - gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); - - jclass stringClass = FindClassOrDie(env, "java/lang/String"); - g_stringClass = MakeGlobalRefOrDie(env, stringClass); - - jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); - gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); - gSparseArrayOffsets.constructor = - GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "", "()V"); - gSparseArrayOffsets.put = - GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V"); - - jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); - gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); - gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "", "()V"); - gConfigurationOffsets.mSmallestScreenWidthDpOffset = - GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I"); - gConfigurationOffsets.mScreenWidthDpOffset = - GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I"); - gConfigurationOffsets.mScreenHeightDpOffset = - GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I"); - - return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, - NELEM(gAssetManagerMethods)); +int register_android_content_AssetManager(JNIEnv* env) +{ + jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); + gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); + gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); + gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string", + "Ljava/lang/CharSequence;"); + gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); + gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); + gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue, + "changingConfigurations", "I"); + gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); + + jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); + gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd", + "Landroid/os/ParcelFileDescriptor;"); + gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); + gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); + + jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); + gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); + + jclass stringClass = FindClassOrDie(env, "java/lang/String"); + g_stringClass = MakeGlobalRefOrDie(env, stringClass); + + jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); + gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); + gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, + "", "()V"); + gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", + "(ILjava/lang/Object;)V"); + + jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); + gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); + gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, + "", "()V"); + gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, + "smallestScreenWidthDp", "I"); + gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, + "screenWidthDp", "I"); + gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass, + "screenHeightDp", "I"); + + return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, + NELEM(gAssetManagerMethods)); } }; // namespace android diff --git a/core/jni/include/android_runtime/android_util_AssetManager.h b/core/jni/include/android_runtime/android_util_AssetManager.h index 2c1e3579eb92..8dd933707a6a 100644 --- a/core/jni/include/android_runtime/android_util_AssetManager.h +++ b/core/jni/include/android_runtime/android_util_AssetManager.h @@ -14,20 +14,17 @@ * limitations under the License. */ -#ifndef ANDROID_RUNTIME_ASSETMANAGER_H -#define ANDROID_RUNTIME_ASSETMANAGER_H +#ifndef android_util_AssetManager_H +#define android_util_AssetManager_H -#include "androidfw/AssetManager2.h" -#include "androidfw/MutexGuard.h" +#include #include "jni.h" namespace android { -extern AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); -extern Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); -extern Guarded* AssetManagerForNdkAssetManager(AAssetManager* assetmanager); +extern AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject assetMgr); -} // namespace android +} -#endif // ANDROID_RUNTIME_ASSETMANAGER_H +#endif diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 2fc8e952707b..415d3e36adf9 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -265,6 +265,8 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, FindEntryResult* out_entry) { + ATRACE_CALL(); + // Might use this if density_override != 0. ResTable_config density_override_config; @@ -427,7 +429,9 @@ ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_valu for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && in_out_value->data != 0u && iteration < kMaxIterations; iteration++) { - *out_last_reference = in_out_value->data; + if (out_last_reference != nullptr) { + *out_last_reference = in_out_value->data; + } uint32_t new_flags = 0u; cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/, in_out_value, in_out_selected_config, &new_flags); diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index f912af4f7190..60e3845d98a9 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -20,18 +20,13 @@ #include -#include "androidfw/AssetManager2.h" #include "androidfw/AttributeFinder.h" +#include "androidfw/ResourceTypes.h" constexpr bool kDebugStyles = false; namespace android { -// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. -static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { - return cookie != kInvalidCookie ? static_cast(cookie + 1) : static_cast(-1); -} - class XmlAttributeFinder : public BackTrackingAttributeFinder { public: @@ -49,53 +44,58 @@ class XmlAttributeFinder }; class BagAttributeFinder - : public BackTrackingAttributeFinder { + : public BackTrackingAttributeFinder { public: - BagAttributeFinder(const ResolvedBag* bag) - : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr, - bag != nullptr ? bag->entries + bag->entry_count : nullptr) { - } + BagAttributeFinder(const ResTable::bag_entry* start, + const ResTable::bag_entry* end) + : BackTrackingAttributeFinder(start, end) {} - inline uint32_t GetAttribute(const ResolvedBag::Entry* entry) const { - return entry->key; + inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const { + return entry->map.name.ident; } }; -bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, - uint32_t* src_values, size_t src_values_length, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { +bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, + uint32_t def_style_res, uint32_t* src_values, + size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, + uint32_t* out_indices) { if (kDebugStyles) { ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr, def_style_res); } - AssetManager2* assetmanager = theme->GetAssetManager(); + const ResTable& res = theme->getResTable(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_flags = 0u; + uint32_t def_style_bag_type_set_flags = 0; if (def_style_attr != 0) { Res_value value; - if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { + if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) { if (value.dataType == Res_value::TYPE_REFERENCE) { def_style_res = value.data; } } } - // Retrieve the default style bag, if requested. - const ResolvedBag* default_style_bag = nullptr; - if (def_style_res != 0) { - default_style_bag = assetmanager->GetBag(def_style_res); - if (default_style_bag != nullptr) { - def_style_flags |= default_style_bag->type_spec_flags; - } - } + // Now lock down the resource object and start pulling stuff from it. + res.lock(); - BagAttributeFinder def_style_attr_finder(default_style_bag); + // Retrieve the default style bag, if requested. + const ResTable::bag_entry* def_style_start = nullptr; + uint32_t def_style_type_set_flags = 0; + ssize_t bag_off = def_style_res != 0 + ? res.getBagLocked(def_style_res, &def_style_start, + &def_style_type_set_flags) + : -1; + def_style_type_set_flags |= def_style_bag_type_set_flags; + const ResTable::bag_entry* const def_style_end = + def_style_start + (bag_off >= 0 ? bag_off : 0); + BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -106,7 +106,7 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ApkAssetsCookie cookie = kInvalidCookie; + ssize_t block = -1; uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; @@ -122,14 +122,15 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, value.dataType = Res_value::TYPE_ATTRIBUTE; value.data = src_values[ii]; if (kDebugStyles) { - ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data); + ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, + value.data); } } else { - const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident); - if (entry != def_style_attr_finder.end()) { - cookie = entry->cookie; - type_set_flags = def_style_flags; - value = entry->value; + const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident); + if (def_style_entry != def_style_end) { + block = def_style_entry->stringBlock; + type_set_flags = def_style_type_set_flags; + value = def_style_entry->map.value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -139,26 +140,22 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } + ssize_t new_block = + theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); + if (new_block >= 0) block = new_block; if (kDebugStyles) { ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data); } } else if (value.data != Res_value::DATA_NULL_EMPTY) { - // If we still don't have a value for this attribute, try to find it in the theme! - ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); - if (new_cookie != kInvalidCookie) { + // If we still don't have a value for this attribute, try to find + // it in the theme! + ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); + if (new_block >= 0) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_cookie = - assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } + new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); + if (new_block >= 0) block = new_block; if (kDebugStyles) { ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -172,7 +169,7 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + block = -1; } if (kDebugStyles) { @@ -182,7 +179,9 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + out_values[STYLE_ASSET_COOKIE] = + block != -1 ? static_cast(res.getTableCookie(block)) + : static_cast(-1); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -196,80 +195,90 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, out_values += STYLE_NUM_ENTRIES; } + res.unlock(); + if (out_indices != nullptr) { out_indices[0] = indices_idx; } return true; } -void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { - ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, - def_style_attr, def_style_resid, xml_parser); + ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", + theme, def_style_attr, def_style_res, xml_parser); } - AssetManager2* assetmanager = theme->GetAssetManager(); + const ResTable& res = theme->getResTable(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_flags = 0u; + uint32_t def_style_bag_type_set_flags = 0; if (def_style_attr != 0) { Res_value value; - if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { + if (theme->getAttribute(def_style_attr, &value, + &def_style_bag_type_set_flags) >= 0) { if (value.dataType == Res_value::TYPE_REFERENCE) { - def_style_resid = value.data; + def_style_res = value.data; } } } - // Retrieve the style resource ID associated with the current XML tag's style attribute. - uint32_t style_resid = 0u; - uint32_t style_flags = 0u; + // Retrieve the style class associated with the current XML tag. + int style = 0; + uint32_t style_bag_type_set_flags = 0; if (xml_parser != nullptr) { ssize_t idx = xml_parser->indexOfStyle(); if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) { if (value.dataType == value.TYPE_ATTRIBUTE) { - // Resolve the attribute with out theme. - if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) { + if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) { value.dataType = Res_value::TYPE_NULL; } } - if (value.dataType == value.TYPE_REFERENCE) { - style_resid = value.data; + style = value.data; } } } - // Retrieve the default style bag, if requested. - const ResolvedBag* default_style_bag = nullptr; - if (def_style_resid != 0) { - default_style_bag = assetmanager->GetBag(def_style_resid); - if (default_style_bag != nullptr) { - def_style_flags |= default_style_bag->type_spec_flags; - } - } + // Now lock down the resource object and start pulling stuff from it. + res.lock(); - BagAttributeFinder def_style_attr_finder(default_style_bag); + // Retrieve the default style bag, if requested. + const ResTable::bag_entry* def_style_attr_start = nullptr; + uint32_t def_style_type_set_flags = 0; + ssize_t bag_off = def_style_res != 0 + ? res.getBagLocked(def_style_res, &def_style_attr_start, + &def_style_type_set_flags) + : -1; + def_style_type_set_flags |= def_style_bag_type_set_flags; + const ResTable::bag_entry* const def_style_attr_end = + def_style_attr_start + (bag_off >= 0 ? bag_off : 0); + BagAttributeFinder def_style_attr_finder(def_style_attr_start, + def_style_attr_end); // Retrieve the style class bag, if requested. - const ResolvedBag* xml_style_bag = nullptr; - if (style_resid != 0) { - xml_style_bag = assetmanager->GetBag(style_resid); - if (xml_style_bag != nullptr) { - style_flags |= xml_style_bag->type_spec_flags; - } - } - - BagAttributeFinder xml_style_attr_finder(xml_style_bag); + const ResTable::bag_entry* style_attr_start = nullptr; + uint32_t style_type_set_flags = 0; + bag_off = + style != 0 + ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags) + : -1; + style_type_set_flags |= style_bag_type_set_flags; + const ResTable::bag_entry* const style_attr_end = + style_attr_start + (bag_off >= 0 ? bag_off : 0); + BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end); // Retrieve the XML attributes, if requested. + static const ssize_t kXmlBlock = 0x10000000; XmlAttributeFinder xml_attr_finder(xml_parser); + const size_t xml_attr_end = + xml_parser != nullptr ? xml_parser->getAttributeCount() : 0; // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -280,8 +289,8 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ApkAssetsCookie cookie = kInvalidCookie; - uint32_t type_set_flags = 0u; + ssize_t block = kXmlBlock; + uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -293,7 +302,7 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, // Walk through the xml attributes looking for the requested attribute. const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); - if (xml_attr_idx != xml_attr_finder.end()) { + if (xml_attr_idx != xml_attr_end) { // We found the attribute we were looking for. xml_parser->getAttributeValue(xml_attr_idx, &value); if (kDebugStyles) { @@ -303,12 +312,12 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the style class values looking for the requested attribute. - const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident); - if (entry != xml_style_attr_finder.end()) { + const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident); + if (style_attr_entry != style_attr_end) { // We found the attribute we were looking for. - cookie = entry->cookie; - type_set_flags = style_flags; - value = entry->value; + block = style_attr_entry->stringBlock; + type_set_flags = style_type_set_flags; + value = style_attr_entry->map.value; if (kDebugStyles) { ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -317,25 +326,25 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the default style values looking for the requested attribute. - const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident); - if (entry != def_style_attr_finder.end()) { + const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident); + if (def_style_attr_entry != def_style_attr_end) { // We found the attribute we were looking for. - cookie = entry->cookie; - type_set_flags = def_style_flags; - value = entry->value; + block = def_style_attr_entry->stringBlock; + type_set_flags = style_type_set_flags; + value = def_style_attr_entry->map.value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } } } - uint32_t resid = 0u; + uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; + ssize_t new_block = + theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); + if (new_block >= 0) { + block = new_block; } if (kDebugStyles) { @@ -343,15 +352,14 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, } } else if (value.data != Res_value::DATA_NULL_EMPTY) { // If we still don't have a value for this attribute, try to find it in the theme! - ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); - if (new_cookie != kInvalidCookie) { + ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); + if (new_block >= 0) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_cookie = - assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; + new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); + if (new_block >= 0) { + block = new_block; } if (kDebugStyles) { @@ -367,7 +375,7 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + block = kXmlBlock; } if (kDebugStyles) { @@ -377,7 +385,9 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + out_values[STYLE_ASSET_COOKIE] = + block != kXmlBlock ? static_cast(res.getTableCookie(block)) + : static_cast(-1); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -392,28 +402,36 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, out_values += STYLE_NUM_ENTRIES; } + res.unlock(); + // out_indices must NOT be nullptr. out_indices[0] = indices_idx; } -bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { +bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, + uint32_t* attrs, size_t attrs_length, + uint32_t* out_values, uint32_t* out_indices) { ResTable_config config; Res_value value; int indices_idx = 0; + // Now lock down the resource object and start pulling stuff from it. + res->lock(); + // Retrieve the XML attributes, if requested. const size_t xml_attr_count = xml_parser->getAttributeCount(); size_t ix = 0; uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix); + static const ssize_t kXmlBlock = 0x10000000; + // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - ApkAssetsCookie cookie = kInvalidCookie; - uint32_t type_set_flags = 0u; + ssize_t block = kXmlBlock; + uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -432,27 +450,28 @@ bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, u cur_xml_attr = xml_parser->getAttributeNameResID(ix); } - uint32_t resid = 0u; + uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ApkAssetsCookie new_cookie = - assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid); - if (new_cookie != kInvalidCookie) { - cookie = new_cookie; - } + // printf("Resolving attribute reference\n"); + ssize_t new_block = res->resolveReference(&value, block, &resid, + &type_set_flags, &config); + if (new_block >= 0) block = new_block; } // Deal with the special @null value -- it turns back to TYPE_NULL. if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - cookie = kInvalidCookie; + block = kXmlBlock; } // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + out_values[STYLE_ASSET_COOKIE] = + block != kXmlBlock ? static_cast(res->getTableCookie(block)) + : static_cast(-1); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -466,6 +485,8 @@ bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, u out_values += STYLE_NUM_ENTRIES; } + res->unlock(); + if (out_indices != nullptr) { out_indices[0] = indices_idx; } diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index e08848f891f6..28548e27baf0 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -324,6 +324,8 @@ bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_i bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, FindEntryResult* out_entry) const { + ATRACE_CALL(); + // If the type IDs are offset in this package, we need to take that into account when searching // for a type. const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h index 03fad4947dfe..f281921824e7 100644 --- a/libs/androidfw/include/androidfw/AttributeFinder.h +++ b/libs/androidfw/include/androidfw/AttributeFinder.h @@ -58,7 +58,6 @@ class BackTrackingAttributeFinder { BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end); Iterator Find(uint32_t attr); - inline Iterator end(); private: void JumpToClosestAttribute(uint32_t package_id); @@ -202,11 +201,6 @@ Iterator BackTrackingAttributeFinder::Find(uint32_t attr) { return end_; } -template -Iterator BackTrackingAttributeFinder::end() { - return end_; -} - } // namespace android #endif // ANDROIDFW_ATTRIBUTE_FINDER_H diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h index 35ef98d8c704..69b760414846 100644 --- a/libs/androidfw/include/androidfw/AttributeResolution.h +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -17,8 +17,7 @@ #ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H #define ANDROIDFW_ATTRIBUTERESOLUTION_H -#include "androidfw/AssetManager2.h" -#include "androidfw/ResourceTypes.h" +#include namespace android { @@ -43,19 +42,19 @@ enum { // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid, +bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, uint32_t* src_values, size_t src_values_length, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` is NOT optional and must NOT be nullptr. -void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, +bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); } // namespace android diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 1775f5070f4e..965e2dbd2fb2 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -45,17 +45,16 @@ struct FindEntryResult { // A pointer to the resource table entry for this resource. // If the size of the entry is > sizeof(ResTable_entry), it can be cast to // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry; + const ResTable_entry* entry = nullptr; - // The configuration for which the resulting entry was defined. This points to a structure that - // is already swapped to host endianness. - const ResTable_config* config; + // The configuration for which the resulting entry was defined. + const ResTable_config* config = nullptr; - // The bitmask of configuration axis with which the resource value varies. - uint32_t type_flags; + // Stores the resulting bitmask of configuration axis with which the resource value varies. + uint32_t type_flags = 0u; // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table; + const DynamicRefTable* dynamic_ref_table = nullptr; // The string pool reference to the type's name. This uses a different string pool than // the global string pool, but this is hidden from the caller. diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h deleted file mode 100644 index 64924f433245..000000000000 --- a/libs/androidfw/include/androidfw/MutexGuard.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROIDFW_MUTEXGUARD_H -#define ANDROIDFW_MUTEXGUARD_H - -#include -#include - -#include "android-base/macros.h" - -namespace android { - -template -class ScopedLock; - -// Owns the guarded object and protects access to it via a mutex. -// The guarded object is inaccessible via this class. -// The mutex is locked and the object accessed via the ScopedLock class. -// -// NOTE: The template parameter T should not be a raw pointer, since ownership -// is ambiguous and error-prone. Instead use an std::unique_ptr<>. -// -// Example use: -// -// Guarded shared_string("hello"); -// { -// ScopedLock locked_string(shared_string); -// *locked_string += " world"; -// } -// -template -class Guarded { - static_assert(!std::is_pointer::value, "T must not be a raw pointer"); - - public: - explicit Guarded() : guarded_() { - } - - template - explicit Guarded(const T& guarded, - typename std::enable_if::value>::type = void()) - : guarded_(guarded) { - } - - template - explicit Guarded(T&& guarded, - typename std::enable_if::value>::type = void()) - : guarded_(std::move(guarded)) { - } - - private: - friend class ScopedLock; - - DISALLOW_COPY_AND_ASSIGN(Guarded); - - std::mutex lock_; - T guarded_; -}; - -template -class ScopedLock { - public: - explicit ScopedLock(Guarded& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) { - } - - T& operator*() { - return guarded_; - } - - T* operator->() { - return &guarded_; - } - - T* get() { - return &guarded_; - } - - private: - DISALLOW_COPY_AND_ASSIGN(ScopedLock); - - std::lock_guard lock_; - T& guarded_; -}; - -} // namespace android - -#endif // ANDROIDFW_MUTEXGUARD_H diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index cc3053798e7b..2d73ce8f8ee3 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -21,7 +21,6 @@ #include "android-base/file.h" #include "android-base/logging.h" #include "android-base/macros.h" -#include "androidfw/AssetManager2.h" #include "TestHelpers.h" #include "data/styles/R.h" @@ -33,14 +32,15 @@ namespace android { class AttributeResolutionTest : public ::testing::Test { public: virtual void SetUp() override { - styles_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); - ASSERT_NE(nullptr, styles_assets_); - assetmanager_.SetApkAssets({styles_assets_.get()}); + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString( + GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); + ASSERT_EQ(NO_ERROR, table_.add(contents.data(), contents.size(), + 1 /*cookie*/, true /*copyData*/)); } protected: - std::unique_ptr styles_assets_; - AssetManager2 assetmanager_; + ResTable table_; }; class AttributeResolutionXmlTest : public AttributeResolutionTest { @@ -48,12 +48,13 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { virtual void SetUp() override { AttributeResolutionTest::SetUp(); - std::unique_ptr asset = - assetmanager_.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, asset); + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", + "res/layout/layout.xml", &contents)); - ASSERT_EQ(NO_ERROR, - xml_parser_.setTo(asset->getBuffer(true), asset->getLength(), true /*copyData*/)); + ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(), + true /*copyData*/)); // Skip to the first tag. while (xml_parser_.next() != ResXMLParser::START_TAG) { @@ -65,14 +66,14 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { }; TEST_F(AttributeResolutionTest, Theme) { - std::unique_ptr theme = assetmanager_.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); + ResTable::Theme theme(table_); + ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_empty}}; std::array values; - ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/, + ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/, nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(), attrs.size(), values.data(), nullptr /*out_indices*/)); @@ -125,8 +126,8 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { R::attr::attr_four, R::attr::attr_empty}}; std::array values; - ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(), - values.data(), nullptr /*out_indices*/)); + ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(), + nullptr /*out_indices*/)); uint32_t* values_cursor = values.data(); EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]); @@ -170,15 +171,15 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { } TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) { - std::unique_ptr theme = assetmanager_.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); + ResTable::Theme theme(table_); + ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}}; std::array values; std::array indices; - ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), + ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(), attrs.size(), values.data(), indices.data()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index a8abcb5df86c..7149beef797f 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -33,12 +33,12 @@ void GetResourceBenchmarkOld(const std::vector& paths, const ResTab } } - // Make sure to force creation of the ResTable first, or else the configuration doesn't get set. - const ResTable& table = assetmanager.getResources(true); if (config != nullptr) { assetmanager.setConfiguration(*config); } + const ResTable& table = assetmanager.getResources(true); + Res_value value; ResTable_config selected_config; uint32_t flags; diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp index e70d5ea0d566..98e9a42d944d 100644 --- a/native/android/asset_manager.cpp +++ b/native/android/asset_manager.cpp @@ -18,11 +18,9 @@ #include #include -#include #include #include #include -#include #include #include "jni.h" @@ -37,20 +35,21 @@ using namespace android; // ----- struct AAssetDir { - std::unique_ptr mAssetDir; + AssetDir* mAssetDir; size_t mCurFileIndex; String8 mCachedFileName; - explicit AAssetDir(std::unique_ptr dir) : - mAssetDir(std::move(dir)), mCurFileIndex(0) { } + explicit AAssetDir(AssetDir* dir) : mAssetDir(dir), mCurFileIndex(0) { } + ~AAssetDir() { delete mAssetDir; } }; // ----- struct AAsset { - std::unique_ptr mAsset; + Asset* mAsset; - explicit AAsset(std::unique_ptr asset) : mAsset(std::move(asset)) { } + explicit AAsset(Asset* asset) : mAsset(asset) { } + ~AAsset() { delete mAsset; } }; // -------------------- Public native C API -------------------- @@ -105,18 +104,19 @@ AAsset* AAssetManager_open(AAssetManager* amgr, const char* filename, int mode) return NULL; } - ScopedLock locked_mgr(*AssetManagerForNdkAssetManager(amgr)); - std::unique_ptr asset = locked_mgr->Open(filename, amMode); - if (asset == nullptr) { - return nullptr; + AssetManager* mgr = static_cast(amgr); + Asset* asset = mgr->open(filename, amMode); + if (asset == NULL) { + return NULL; } - return new AAsset(std::move(asset)); + + return new AAsset(asset); } AAssetDir* AAssetManager_openDir(AAssetManager* amgr, const char* dirName) { - ScopedLock locked_mgr(*AssetManagerForNdkAssetManager(amgr)); - return new AAssetDir(locked_mgr->OpenDir(dirName)); + AssetManager* mgr = static_cast(amgr); + return new AAssetDir(mgr->openDir(dirName)); } /** diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index 52d0e08e4e7f..b32be736533b 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -24,9 +24,8 @@ #include #include -#include #include -#include +#include #include #include @@ -1665,22 +1664,18 @@ nFileA3DCreateFromAssetStream(JNIEnv *_env, jobject _this, jlong con, jlong nati static jlong nFileA3DCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path) { - Guarded* mgr = AssetManagerForJavaObject(_env, _assetMgr); + AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - std::unique_ptr asset; - { - ScopedLock locked_mgr(*mgr); - asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); - if (asset == nullptr) { - return 0; - } + Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; } - jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset.release()); + jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset); return id; } @@ -1757,25 +1752,22 @@ static jlong nFontCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path, jfloat fontSize, jint dpi) { - Guarded* mgr = AssetManagerForJavaObject(_env, _assetMgr); + AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - std::unique_ptr asset; - { - ScopedLock locked_mgr(*mgr); - asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); - if (asset == nullptr) { - return 0; - } + Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; } jlong id = (jlong)(uintptr_t)rsFontCreateFromMemory((RsContext)con, str.c_str(), str.length(), fontSize, dpi, asset->getBuffer(false), asset->getLength()); + delete asset; return id; } -- cgit v1.2.3-59-g8ed1b From bebfcc46a249a70af04bc18490a897888a142fb8 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 12 Feb 2018 14:27:46 -0800 Subject: Refactor AssetManager Bug: 64071469 Test: atest CtsContentTestCases Change-Id: Ia6856157e8813856268fba003e1e591d690cb26e --- config/hiddenapi-light-greylist.txt | 20 - core/java/android/app/Activity.java | 2 + core/java/android/app/ResourcesManager.java | 202 +- core/java/android/content/pm/PackageParser.java | 66 +- .../content/pm/split/DefaultSplitAssetLoader.java | 75 +- .../pm/split/SplitAssetDependencyLoader.java | 88 +- core/java/android/content/res/ApkAssets.java | 195 ++ core/java/android/content/res/AssetManager.java | 1432 ++++++---- core/java/android/content/res/Resources.java | 9 +- core/java/android/content/res/ResourcesImpl.java | 22 +- core/java/android/content/res/TypedArray.java | 185 +- core/java/android/content/res/XmlBlock.java | 8 +- core/jni/Android.bp | 3 +- core/jni/AndroidRuntime.cpp | 2 + core/jni/android/graphics/FontFamily.cpp | 29 +- core/jni/android_app_NativeActivity.cpp | 2 +- core/jni/android_content_res_ApkAssets.cpp | 157 ++ core/jni/android_util_AssetManager.cpp | 2894 +++++++++----------- .../android_runtime/android_util_AssetManager.h | 15 +- libs/androidfw/Android.bp | 2 + libs/androidfw/ApkAssets.cpp | 22 +- libs/androidfw/AssetManager2.cpp | 308 ++- libs/androidfw/AttributeResolution.cpp | 263 +- libs/androidfw/LoadedArsc.cpp | 354 +-- libs/androidfw/include/androidfw/AssetManager2.h | 66 +- libs/androidfw/include/androidfw/AttributeFinder.h | 6 + .../include/androidfw/AttributeResolution.h | 11 +- libs/androidfw/include/androidfw/LoadedArsc.h | 110 +- libs/androidfw/include/androidfw/MutexGuard.h | 101 + libs/androidfw/include/androidfw/ResourceUtils.h | 2 +- libs/androidfw/tests/ApkAssets_test.cpp | 78 +- libs/androidfw/tests/AssetManager2_bench.cpp | 59 +- libs/androidfw/tests/AssetManager2_test.cpp | 93 +- libs/androidfw/tests/AttributeResolution_bench.cpp | 175 ++ libs/androidfw/tests/AttributeResolution_test.cpp | 75 +- libs/androidfw/tests/BenchmarkHelpers.cpp | 16 +- libs/androidfw/tests/LoadedArsc_test.cpp | 211 +- libs/androidfw/tests/TestHelpers.h | 1 + libs/androidfw/tests/data/app/app.apk | Bin 1138 -> 1402 bytes libs/androidfw/tests/data/app/assets/app_file.txt | 1 + libs/androidfw/tests/data/app/assets/file.txt | 1 + libs/androidfw/tests/data/app/build | 9 +- libs/androidfw/tests/data/basic/R.h | 2 + libs/androidfw/tests/data/basic/basic.apk | Bin 3124 -> 5260 bytes .../tests/data/basic/res/layout/layout.xml | 25 + .../tests/data/basic/res/values/values.xml | 13 + .../data/out_of_order_types/AndroidManifest.xml | 18 + libs/androidfw/tests/data/out_of_order_types/build | 22 + .../out_of_order_types/edited_resources.arsc.txt | 43 + .../data/out_of_order_types/out_of_order_types.apk | Bin 0 -> 1151 bytes .../data/out_of_order_types/res/values/values.xml | 20 + libs/androidfw/tests/data/system/assets/file.txt | 1 + .../data/system/assets/subdir/subdir_file.txt | 1 + libs/androidfw/tests/data/system/build | 4 +- .../tests/data/system/res/values-sv/values.xml | 1 - .../tests/data/system/res/values/themes.xml | 1 + libs/androidfw/tests/data/system/system.apk | Bin 1417 -> 1624 bytes native/android/asset_manager.cpp | 28 +- native/android/configuration.cpp | 9 +- rs/jni/android_renderscript_RenderScript.cpp | 30 +- .../android/server/om/OverlayManagerSettings.java | 2 + 61 files changed, 4419 insertions(+), 3171 deletions(-) create mode 100644 core/java/android/content/res/ApkAssets.java create mode 100644 core/jni/android_content_res_ApkAssets.cpp create mode 100644 libs/androidfw/include/androidfw/MutexGuard.h create mode 100644 libs/androidfw/tests/AttributeResolution_bench.cpp create mode 100644 libs/androidfw/tests/data/app/assets/app_file.txt create mode 100644 libs/androidfw/tests/data/app/assets/file.txt create mode 100644 libs/androidfw/tests/data/basic/res/layout/layout.xml create mode 100644 libs/androidfw/tests/data/out_of_order_types/AndroidManifest.xml create mode 100755 libs/androidfw/tests/data/out_of_order_types/build create mode 100644 libs/androidfw/tests/data/out_of_order_types/edited_resources.arsc.txt create mode 100644 libs/androidfw/tests/data/out_of_order_types/out_of_order_types.apk create mode 100644 libs/androidfw/tests/data/out_of_order_types/res/values/values.xml create mode 100644 libs/androidfw/tests/data/system/assets/file.txt create mode 100644 libs/androidfw/tests/data/system/assets/subdir/subdir_file.txt (limited to 'libs/androidfw/LoadedArsc.cpp') diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 8d70a553f1b3..05211553a2b5 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -526,41 +526,22 @@ Landroid/content/res/AssetFileDescriptor;->mLength:J Landroid/content/res/AssetFileDescriptor;->mStartOffset:J Landroid/content/res/AssetManager;->addAssetPathAsSharedLibrary(Ljava/lang/String;)I Landroid/content/res/AssetManager;->addAssetPath(Ljava/lang/String;)I -Landroid/content/res/AssetManager;->addAssetPathNative(Ljava/lang/String;Z)I -Landroid/content/res/AssetManager;->addAssetPaths([Ljava/lang/String;)[I -Landroid/content/res/AssetManager;->applyStyle(JIIJ[IIJJ)V -Landroid/content/res/AssetManager;->ensureStringBlocks()[Landroid/content/res/StringBlock; -Landroid/content/res/AssetManager;->getArraySize(I)I Landroid/content/res/AssetManager;->getAssignedPackageIdentifiers()Landroid/util/SparseArray; -Landroid/content/res/AssetManager;->getCookieName(I)Ljava/lang/String; -Landroid/content/res/AssetManager;->getNativeStringBlock(I)J Landroid/content/res/AssetManager;->getResourceBagText(II)Ljava/lang/CharSequence; Landroid/content/res/AssetManager;->getResourceEntryName(I)Ljava/lang/String; Landroid/content/res/AssetManager;->getResourceIdentifier(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I Landroid/content/res/AssetManager;->getResourceName(I)Ljava/lang/String; Landroid/content/res/AssetManager;->getResourcePackageName(I)Ljava/lang/String; Landroid/content/res/AssetManager;->getResourceTypeName(I)Ljava/lang/String; -Landroid/content/res/AssetManager;->getStringBlockCount()I Landroid/content/res/AssetManager;->()V Landroid/content/res/AssetManager;->isUpToDate()Z -Landroid/content/res/AssetManager;->loadResourceBagValue(IILandroid/util/TypedValue;Z)I -Landroid/content/res/AssetManager;->loadResourceValue(ISLandroid/util/TypedValue;Z)I -Landroid/content/res/AssetManager;->loadThemeAttributeValue(JILandroid/util/TypedValue;Z)I Landroid/content/res/AssetManager;->mObject:J -Landroid/content/res/AssetManager;->mStringBlocks:[Landroid/content/res/StringBlock; -Landroid/content/res/AssetManager;->openNonAssetFdNative(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor; Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;I)Ljava/io/InputStream; Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;)Ljava/io/InputStream; Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;I)Ljava/io/InputStream; Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;)Ljava/io/InputStream; -Landroid/content/res/AssetManager;->openNonAssetNative(ILjava/lang/String;I)J -Landroid/content/res/AssetManager;->openXmlAssetNative(ILjava/lang/String;)J Landroid/content/res/AssetManager;->resolveAttrs(JII[I[I[I[I)Z -Landroid/content/res/AssetManager;->retrieveArray(I[I)I -Landroid/content/res/AssetManager;->retrieveAttributes(J[I[I[I)Z Landroid/content/res/AssetManager;->setConfiguration(IILjava/lang/String;IIIIIIIIIIIIIII)V -Landroid/content/res/AssetManager;->STYLE_NUM_ENTRIES:I -Landroid/content/res/AssetManager;->STYLE_RESOURCE_ID:I Landroid/content/res/ColorStateList$ColorStateListFactory;->(Landroid/content/res/ColorStateList;)V Landroid/content/res/ColorStateList;->mColors:[I Landroid/content/res/ColorStateList;->mDefaultColor:I @@ -602,7 +583,6 @@ Landroid/content/res/TypedArray;->mResources:Landroid/content/res/Resources; Landroid/content/res/TypedArray;->mTheme:Landroid/content/res/Resources$Theme; Landroid/content/res/TypedArray;->mValue:Landroid/util/TypedValue; Landroid/content/res/TypedArray;->mXml:Landroid/content/res/XmlBlock$Parser; -Landroid/content/res/XmlBlock;->close()V Landroid/content/res/XmlBlock;->([B)V Landroid/content/res/XmlBlock;->newParser()Landroid/content/res/XmlResourceParser; Landroid/content/res/XmlBlock$Parser;->mParseState:J diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 3d04e2c64bee..99c5d2b9a927 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -6366,6 +6366,8 @@ public class Activity extends ContextThemeWrapper } else { writer.print(prefix); writer.println("No AutofillManager"); } + + ResourcesManager.getInstance().dump(prefix, writer); } /** diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index fb11272d7e62..fc5ea6607d87 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -21,6 +21,7 @@ import static android.app.ActivityThread.DEBUG_CONFIGURATION; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.ActivityInfo; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.CompatResources; import android.content.res.CompatibilityInfo; @@ -34,6 +35,7 @@ import android.os.Trace; import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.Log; +import android.util.LruCache; import android.util.Pair; import android.util.Slog; import android.view.Display; @@ -41,9 +43,13 @@ import android.view.DisplayAdjustments; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.IndentingPrintWriter; +import java.io.IOException; +import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collection; import java.util.Objects; import java.util.WeakHashMap; import java.util.function.Predicate; @@ -59,12 +65,7 @@ public class ResourcesManager { * Predicate that returns true if a WeakReference is gc'ed. */ private static final Predicate> sEmptyReferencePredicate = - new Predicate>() { - @Override - public boolean test(WeakReference weakRef) { - return weakRef == null || weakRef.get() == null; - } - }; + weakRef -> weakRef == null || weakRef.get() == null; /** * The global compatibility settings. @@ -89,6 +90,48 @@ public class ResourcesManager { */ private final ArrayList> mResourceReferences = new ArrayList<>(); + private static class ApkKey { + public final String path; + public final boolean sharedLib; + public final boolean overlay; + + ApkKey(String path, boolean sharedLib, boolean overlay) { + this.path = path; + this.sharedLib = sharedLib; + this.overlay = overlay; + } + + @Override + public int hashCode() { + int result = 1; + result = 31 * result + this.path.hashCode(); + result = 31 * result + Boolean.hashCode(this.sharedLib); + result = 31 * result + Boolean.hashCode(this.overlay); + return result; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ApkKey)) { + return false; + } + ApkKey other = (ApkKey) obj; + return this.path.equals(other.path) && this.sharedLib == other.sharedLib + && this.overlay == other.overlay; + } + } + + /** + * The ApkAssets we are caching and intend to hold strong references to. + */ + private final LruCache mLoadedApkAssets = new LruCache<>(15); + + /** + * The ApkAssets that are being referenced in the wild that we can reuse, even if they aren't + * in our LRU cache. Bonus resources :) + */ + private final ArrayMap> mCachedApkAssets = new ArrayMap<>(); + /** * Resources and base configuration override associated with an Activity. */ @@ -260,6 +303,43 @@ public class ResourcesManager { } } + private static String overlayPathToIdmapPath(String path) { + return "/data/resource-cache/" + path.substring(1).replace('/', '@') + "@idmap"; + } + + private @NonNull ApkAssets loadApkAssets(String path, boolean sharedLib, boolean overlay) + throws IOException { + final ApkKey newKey = new ApkKey(path, sharedLib, overlay); + ApkAssets apkAssets = mLoadedApkAssets.get(newKey); + if (apkAssets != null) { + return apkAssets; + } + + // Optimistically check if this ApkAssets exists somewhere else. + final WeakReference apkAssetsRef = mCachedApkAssets.get(newKey); + if (apkAssetsRef != null) { + apkAssets = apkAssetsRef.get(); + if (apkAssets != null) { + mLoadedApkAssets.put(newKey, apkAssets); + return apkAssets; + } else { + // Clean up the reference. + mCachedApkAssets.remove(newKey); + } + } + + // We must load this from disk. + if (overlay) { + apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(path), + false /*system*/); + } else { + apkAssets = ApkAssets.loadFromPath(path, false /*system*/, sharedLib); + } + mLoadedApkAssets.put(newKey, apkAssets); + mCachedApkAssets.put(newKey, new WeakReference<>(apkAssets)); + return apkAssets; + } + /** * Creates an AssetManager from the paths within the ResourcesKey. * @@ -270,13 +350,16 @@ public class ResourcesManager { */ @VisibleForTesting protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) { - AssetManager assets = new AssetManager(); + final AssetManager.Builder builder = new AssetManager.Builder(); // resDir can be null if the 'android' package is creating a new Resources object. // This is fine, since each AssetManager automatically loads the 'android' package // already. if (key.mResDir != null) { - if (assets.addAssetPath(key.mResDir) == 0) { + try { + builder.addApkAssets(loadApkAssets(key.mResDir, false /*sharedLib*/, + false /*overlay*/)); + } catch (IOException e) { Log.e(TAG, "failed to add asset path " + key.mResDir); return null; } @@ -284,7 +367,10 @@ public class ResourcesManager { if (key.mSplitResDirs != null) { for (final String splitResDir : key.mSplitResDirs) { - if (assets.addAssetPath(splitResDir) == 0) { + try { + builder.addApkAssets(loadApkAssets(splitResDir, false /*sharedLib*/, + false /*overlay*/)); + } catch (IOException e) { Log.e(TAG, "failed to add split asset path " + splitResDir); return null; } @@ -293,7 +379,14 @@ public class ResourcesManager { if (key.mOverlayDirs != null) { for (final String idmapPath : key.mOverlayDirs) { - assets.addOverlayPath(idmapPath); + try { + builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/, + true /*overlay*/)); + } catch (IOException e) { + Log.w(TAG, "failed to add overlay path " + idmapPath); + + // continue. + } } } @@ -302,14 +395,73 @@ public class ResourcesManager { if (libDir.endsWith(".apk")) { // Avoid opening files we know do not have resources, // like code-only .jar files. - if (assets.addAssetPathAsSharedLibrary(libDir) == 0) { + try { + builder.addApkAssets(loadApkAssets(libDir, true /*sharedLib*/, + false /*overlay*/)); + } catch (IOException e) { Log.w(TAG, "Asset path '" + libDir + "' does not exist or contains no resources."); + + // continue. } } } } - return assets; + + return builder.build(); + } + + private static int countLiveReferences(Collection> collection) { + int count = 0; + for (WeakReference ref : collection) { + final T value = ref != null ? ref.get() : null; + if (value != null) { + count++; + } + } + return count; + } + + /** + * @hide + */ + public void dump(String prefix, PrintWriter printWriter) { + synchronized (this) { + IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); + for (int i = 0; i < prefix.length() / 2; i++) { + pw.increaseIndent(); + } + + pw.println("ResourcesManager:"); + pw.increaseIndent(); + pw.print("cached apks: total="); + pw.print(mLoadedApkAssets.size()); + pw.print(" created="); + pw.print(mLoadedApkAssets.createCount()); + pw.print(" evicted="); + pw.print(mLoadedApkAssets.evictionCount()); + pw.print(" hit="); + pw.print(mLoadedApkAssets.hitCount()); + pw.print(" miss="); + pw.print(mLoadedApkAssets.missCount()); + pw.print(" max="); + pw.print(mLoadedApkAssets.maxSize()); + pw.println(); + + pw.print("total apks: "); + pw.println(countLiveReferences(mCachedApkAssets.values())); + + pw.print("resources: "); + + int references = countLiveReferences(mResourceReferences); + for (ActivityResources activityResources : mActivityResourceReferences.values()) { + references += countLiveReferences(activityResources.activityResources); + } + pw.println(references); + + pw.print("resource impls: "); + pw.println(countLiveReferences(mResourceImpls.values())); + } } private Configuration generateConfig(@NonNull ResourcesKey key, @NonNull DisplayMetrics dm) { @@ -630,28 +782,16 @@ public class ResourcesManager { // We will create the ResourcesImpl object outside of holding this lock. } - } - - // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now. - ResourcesImpl resourcesImpl = createResourcesImpl(key); - if (resourcesImpl == null) { - return null; - } - synchronized (this) { - ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key); - if (existingResourcesImpl != null) { - if (DEBUG) { - Slog.d(TAG, "- got beat! existing impl=" + existingResourcesImpl - + " new impl=" + resourcesImpl); - } - resourcesImpl.getAssets().close(); - resourcesImpl = existingResourcesImpl; - } else { - // Add this ResourcesImpl to the cache. - mResourceImpls.put(key, new WeakReference<>(resourcesImpl)); + // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now. + ResourcesImpl resourcesImpl = createResourcesImpl(key); + if (resourcesImpl == null) { + return null; } + // Add this ResourcesImpl to the cache. + mResourceImpls.put(key, new WeakReference<>(resourcesImpl)); + final Resources resources; if (activityToken != null) { resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader, diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 2420b639d678..6373a112ed91 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -54,6 +54,7 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper; import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.SplitAssetDependencyLoader; import android.content.pm.split.SplitAssetLoader; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -1287,7 +1288,6 @@ public class PackageParser { */ @Deprecated public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException { - final AssetManager assets = newConfiguredAssetManager(); final PackageLite lite = parseMonolithicPackageLite(apkFile, flags); if (mOnlyCoreApps) { if (!lite.coreApp) { @@ -1296,8 +1296,9 @@ public class PackageParser { } } + final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags); try { - final Package pkg = parseBaseApk(apkFile, assets, flags); + final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags); pkg.setCodePath(apkFile.getCanonicalPath()); pkg.setUse32bitAbi(lite.use32bitAbi); return pkg; @@ -1305,26 +1306,8 @@ public class PackageParser { throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, "Failed to get path: " + apkFile, e); } finally { - IoUtils.closeQuietly(assets); - } - } - - private static int loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags) - throws PackageParserException { - if ((flags & PARSE_MUST_BE_APK) != 0 && !isApkPath(apkPath)) { - throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, - "Invalid package file: " + apkPath); - } - - // The AssetManager guarantees uniqueness for asset paths, so if this asset path - // already exists in the AssetManager, addAssetPath will only return the cookie - // assigned to it. - int cookie = assets.addAssetPath(apkPath); - if (cookie == 0) { - throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, - "Failed adding asset path: " + apkPath); + IoUtils.closeQuietly(assetLoader); } - return cookie; } private Package parseBaseApk(File apkFile, AssetManager assets, int flags) @@ -1342,13 +1325,15 @@ public class PackageParser { if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath); - final int cookie = loadApkIntoAssetManager(assets, apkPath, flags); - - Resources res = null; XmlResourceParser parser = null; try { - res = new Resources(assets, mMetrics, null); + final int cookie = assets.findCookieForPath(apkPath); + if (cookie == 0) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Failed adding asset path: " + apkPath); + } parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); + final Resources res = new Resources(assets, mMetrics, null); final String[] outError = new String[1]; final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError); @@ -1383,15 +1368,18 @@ public class PackageParser { if (DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath); - final int cookie = loadApkIntoAssetManager(assets, apkPath, flags); - final Resources res; XmlResourceParser parser = null; try { - res = new Resources(assets, mMetrics, null); - assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - Build.VERSION.RESOURCES_SDK_INT); + // This must always succeed, as the path has been added to the AssetManager before. + final int cookie = assets.findCookieForPath(apkPath); + if (cookie == 0) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Failed adding asset path: " + apkPath); + } + parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); + res = new Resources(assets, mMetrics, null); final String[] outError = new String[1]; pkg = parseSplitApk(pkg, res, parser, flags, splitIndex, outError); @@ -1593,21 +1581,19 @@ public class PackageParser { int flags) throws PackageParserException { final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); - AssetManager assets = null; XmlResourceParser parser = null; try { - assets = newConfiguredAssetManager(); - int cookie = fd != null - ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath); - if (cookie == 0) { + final ApkAssets apkAssets; + try { + apkAssets = fd != null + ? ApkAssets.loadFromFd(fd, debugPathName, false, false) + : ApkAssets.loadFromPath(apkPath); + } catch (IOException e) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse " + apkPath); } - final DisplayMetrics metrics = new DisplayMetrics(); - metrics.setToDefaults(); - - parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); + parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME); final SigningDetails signingDetails; if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) { @@ -1634,7 +1620,7 @@ public class PackageParser { "Failed to parse " + apkPath, e); } finally { IoUtils.closeQuietly(parser); - IoUtils.closeQuietly(assets); + // TODO(b/72056911): Implement and call close() on ApkAssets. } } diff --git a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java index 99eb4702d32e..9e3a8f48996c 100644 --- a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java +++ b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java @@ -15,10 +15,13 @@ */ package android.content.pm.split; -import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; +import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK; import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageParserException; +import android.content.pm.PackageParser.ParseFlags; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.os.Build; @@ -26,6 +29,8 @@ import com.android.internal.util.ArrayUtils; import libcore.io.IoUtils; +import java.io.IOException; + /** * Loads the base and split APKs into a single AssetManager. * @hide @@ -33,68 +38,66 @@ import libcore.io.IoUtils; public class DefaultSplitAssetLoader implements SplitAssetLoader { private final String mBaseCodePath; private final String[] mSplitCodePaths; - private final int mFlags; - + private final @ParseFlags int mFlags; private AssetManager mCachedAssetManager; - public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, int flags) { + public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, @ParseFlags int flags) { mBaseCodePath = pkg.baseCodePath; mSplitCodePaths = pkg.splitCodePaths; mFlags = flags; } - private static void loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags) - throws PackageParser.PackageParserException { - if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(apkPath)) { - throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, - "Invalid package file: " + apkPath); + private static ApkAssets loadApkAssets(String path, @ParseFlags int flags) + throws PackageParserException { + if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) { + throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, + "Invalid package file: " + path); } - if (assets.addAssetPath(apkPath) == 0) { - throw new PackageParser.PackageParserException( - INSTALL_PARSE_FAILED_BAD_MANIFEST, - "Failed adding asset path: " + apkPath); + try { + return ApkAssets.loadFromPath(path); + } catch (IOException e) { + throw new PackageParserException(INSTALL_FAILED_INVALID_APK, + "Failed to load APK at path " + path, e); } } @Override - public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException { + public AssetManager getBaseAssetManager() throws PackageParserException { if (mCachedAssetManager != null) { return mCachedAssetManager; } - AssetManager assets = new AssetManager(); - try { - assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - Build.VERSION.RESOURCES_SDK_INT); - loadApkIntoAssetManager(assets, mBaseCodePath, mFlags); - - if (!ArrayUtils.isEmpty(mSplitCodePaths)) { - for (String apkPath : mSplitCodePaths) { - loadApkIntoAssetManager(assets, apkPath, mFlags); - } - } + ApkAssets[] apkAssets = new ApkAssets[(mSplitCodePaths != null + ? mSplitCodePaths.length : 0) + 1]; - mCachedAssetManager = assets; - assets = null; - return mCachedAssetManager; - } finally { - if (assets != null) { - IoUtils.closeQuietly(assets); + // Load the base. + int splitIdx = 0; + apkAssets[splitIdx++] = loadApkAssets(mBaseCodePath, mFlags); + + // Load any splits. + if (!ArrayUtils.isEmpty(mSplitCodePaths)) { + for (String apkPath : mSplitCodePaths) { + apkAssets[splitIdx++] = loadApkAssets(apkPath, mFlags); } } + + AssetManager assets = new AssetManager(); + assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Build.VERSION.RESOURCES_SDK_INT); + assets.setApkAssets(apkAssets, false /*invalidateCaches*/); + + mCachedAssetManager = assets; + return mCachedAssetManager; } @Override - public AssetManager getSplitAssetManager(int splitIdx) - throws PackageParser.PackageParserException { + public AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException { return getBaseAssetManager(); } @Override public void close() throws Exception { - if (mCachedAssetManager != null) { - IoUtils.closeQuietly(mCachedAssetManager); - } + IoUtils.closeQuietly(mCachedAssetManager); } } diff --git a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java index 16023f0d9d97..58eaabfa62f2 100644 --- a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java +++ b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java @@ -15,17 +15,21 @@ */ package android.content.pm.split; -import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK; import android.annotation.NonNull; +import android.content.pm.PackageManager; import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageParserException; +import android.content.pm.PackageParser.ParseFlags; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.os.Build; import android.util.SparseArray; import libcore.io.IoUtils; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -34,17 +38,15 @@ import java.util.Collections; * is to be used when an application opts-in to isolated split loading. * @hide */ -public class SplitAssetDependencyLoader - extends SplitDependencyLoader +public class SplitAssetDependencyLoader extends SplitDependencyLoader implements SplitAssetLoader { private final String[] mSplitPaths; - private final int mFlags; - - private String[][] mCachedPaths; - private AssetManager[] mCachedAssetManagers; + private final @ParseFlags int mFlags; + private final ApkAssets[][] mCachedSplitApks; + private final AssetManager[] mCachedAssetManagers; public SplitAssetDependencyLoader(PackageParser.PackageLite pkg, - SparseArray dependencies, int flags) { + SparseArray dependencies, @ParseFlags int flags) { super(dependencies); // The base is inserted into index 0, so we need to shift all the splits by 1. @@ -53,7 +55,7 @@ public class SplitAssetDependencyLoader System.arraycopy(pkg.splitCodePaths, 0, mSplitPaths, 1, pkg.splitCodePaths.length); mFlags = flags; - mCachedPaths = new String[mSplitPaths.length][]; + mCachedSplitApks = new ApkAssets[mSplitPaths.length][]; mCachedAssetManagers = new AssetManager[mSplitPaths.length]; } @@ -62,58 +64,60 @@ public class SplitAssetDependencyLoader return mCachedAssetManagers[splitIdx] != null; } - private static AssetManager createAssetManagerWithPaths(String[] assetPaths, int flags) - throws PackageParser.PackageParserException { - final AssetManager assets = new AssetManager(); + private static ApkAssets loadApkAssets(String path, @ParseFlags int flags) + throws PackageParserException { + if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) { + throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, + "Invalid package file: " + path); + } + try { - assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - Build.VERSION.RESOURCES_SDK_INT); - - for (String assetPath : assetPaths) { - if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && - !PackageParser.isApkPath(assetPath)) { - throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, - "Invalid package file: " + assetPath); - } - - if (assets.addAssetPath(assetPath) == 0) { - throw new PackageParser.PackageParserException( - INSTALL_PARSE_FAILED_BAD_MANIFEST, - "Failed adding asset path: " + assetPath); - } - } - return assets; - } catch (Throwable e) { - IoUtils.closeQuietly(assets); - throw e; + return ApkAssets.loadFromPath(path); + } catch (IOException e) { + throw new PackageParserException(PackageManager.INSTALL_FAILED_INVALID_APK, + "Failed to load APK at path " + path, e); } } + private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) { + final AssetManager assets = new AssetManager(); + assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Build.VERSION.RESOURCES_SDK_INT); + assets.setApkAssets(apkAssets, false /*invalidateCaches*/); + return assets; + } + @Override protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices, - int parentSplitIdx) throws PackageParser.PackageParserException { - final ArrayList assetPaths = new ArrayList<>(); + int parentSplitIdx) throws PackageParserException { + final ArrayList assets = new ArrayList<>(); + + // Include parent ApkAssets. if (parentSplitIdx >= 0) { - Collections.addAll(assetPaths, mCachedPaths[parentSplitIdx]); + Collections.addAll(assets, mCachedSplitApks[parentSplitIdx]); } - assetPaths.add(mSplitPaths[splitIdx]); + // Include this ApkAssets. + assets.add(loadApkAssets(mSplitPaths[splitIdx], mFlags)); + + // Load and include all config splits for this feature. for (int configSplitIdx : configSplitIndices) { - assetPaths.add(mSplitPaths[configSplitIdx]); + assets.add(loadApkAssets(mSplitPaths[configSplitIdx], mFlags)); } - mCachedPaths[splitIdx] = assetPaths.toArray(new String[assetPaths.size()]); - mCachedAssetManagers[splitIdx] = createAssetManagerWithPaths(mCachedPaths[splitIdx], - mFlags); + + // Cache the results. + mCachedSplitApks[splitIdx] = assets.toArray(new ApkAssets[assets.size()]); + mCachedAssetManagers[splitIdx] = createAssetManagerWithAssets(mCachedSplitApks[splitIdx]); } @Override - public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException { + public AssetManager getBaseAssetManager() throws PackageParserException { loadDependenciesForSplit(0); return mCachedAssetManagers[0]; } @Override - public AssetManager getSplitAssetManager(int idx) throws PackageParser.PackageParserException { + public AssetManager getSplitAssetManager(int idx) throws PackageParserException { // Since we insert the base at position 0, and PackageParser keeps splits separate from // the base, we need to adjust the index. loadDependenciesForSplit(idx + 1); diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java new file mode 100644 index 000000000000..9de8be3e86af --- /dev/null +++ b/core/java/android/content/res/ApkAssets.java @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.content.res; + +import android.annotation.NonNull; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; + +import java.io.FileDescriptor; +import java.io.IOException; + +/** + * The loaded, immutable, in-memory representation of an APK. + * + * The main implementation is native C++ and there is very little API surface exposed here. The APK + * is mainly accessed via {@link AssetManager}. + * + * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers, + * making the creation of AssetManagers very cheap. + * @hide + */ +public final class ApkAssets { + @GuardedBy("this") private final long mNativePtr; + @GuardedBy("this") private StringBlock mStringBlock; + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException { + return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @param system When true, the APK is loaded as a system APK (framework). + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system) + throws IOException { + return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @param system When true, the APK is loaded as a system APK (framework). + * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are + * loaded as a shared library. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system, + boolean forceSharedLibrary) throws IOException { + return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications. + * + * Performs a dup of the underlying fd, so you must take care of still closing + * the FileDescriptor yourself (and can do that whenever you want). + * + * @param fd The FileDescriptor of an open, readable APK. + * @param friendlyName The friendly name used to identify this ApkAssets when logging. + * @param system When true, the APK is loaded as a system APK (framework). + * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are + * loaded as a shared library. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd, + @NonNull String friendlyName, boolean system, boolean forceSharedLibrary) + throws IOException { + return new ApkAssets(fd, friendlyName, system, forceSharedLibrary); + } + + /** + * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path + * is encoded within the IDMAP. + * + * @param idmapPath Path to the IDMAP of an overlay APK. + * @param system When true, the APK is loaded as a system APK (framework). + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system) + throws IOException { + return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/); + } + + private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) + throws IOException { + Preconditions.checkNotNull(path, "path"); + mNativePtr = nativeLoad(path, system, forceSharedLib, overlay); + mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); + } + + private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system, + boolean forceSharedLib) throws IOException { + Preconditions.checkNotNull(fd, "fd"); + Preconditions.checkNotNull(friendlyName, "friendlyName"); + mNativePtr = nativeLoadFromFd(fd, friendlyName, system, forceSharedLib); + mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); + } + + public @NonNull String getAssetPath() { + synchronized (this) { + return nativeGetAssetPath(mNativePtr); + } + } + + CharSequence getStringFromPool(int idx) { + synchronized (this) { + return mStringBlock.get(idx); + } + } + + /** + * Retrieve a parser for a compiled XML file. This is associated with a single APK and + * NOT a full AssetManager. This means that shared-library references will not be + * dynamically assigned runtime package IDs. + * + * @param fileName The path to the file within the APK. + * @return An XmlResourceParser. + * @throws IOException if the file was not found or an error occurred retrieving it. + */ + public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException { + Preconditions.checkNotNull(fileName, "fileName"); + synchronized (this) { + long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName); + try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) { + XmlResourceParser parser = block.newParser(); + // If nativeOpenXml doesn't throw, it will always return a valid native pointer, + // which makes newParser always return non-null. But let's be paranoid. + if (parser == null) { + throw new AssertionError("block.newParser() returned a null parser"); + } + return parser; + } + } + } + + /** + * Returns false if the underlying APK was changed since this ApkAssets was loaded. + */ + public boolean isUpToDate() { + synchronized (this) { + return nativeIsUpToDate(mNativePtr); + } + } + + @Override + public String toString() { + return "ApkAssets{path=" + getAssetPath() + "}"; + } + + @Override + protected void finalize() throws Throwable { + nativeDestroy(mNativePtr); + } + + private static native long nativeLoad( + @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) + throws IOException; + private static native long nativeLoadFromFd(@NonNull FileDescriptor fd, + @NonNull String friendlyName, boolean system, boolean forceSharedLib) + throws IOException; + private static native void nativeDestroy(long ptr); + private static native @NonNull String nativeGetAssetPath(long ptr); + private static native long nativeGetStringBlock(long ptr); + private static native boolean nativeIsUpToDate(long ptr); + private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException; +} diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 5f8a34d46ecd..289534273d13 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -18,22 +18,33 @@ package android.content.res; import android.annotation.AnyRes; import android.annotation.ArrayRes; +import android.annotation.AttrRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; +import android.annotation.StyleRes; import android.content.pm.ActivityInfo; import android.content.res.Configuration.NativeConfig; import android.os.ParcelFileDescriptor; +import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; -import java.io.FileDescriptor; +import libcore.io.IoUtils; + +import java.io.BufferedReader; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.channels.FileLock; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; /** @@ -44,7 +55,20 @@ import java.util.HashMap; * bytes. */ public final class AssetManager implements AutoCloseable { - /* modes used when opening an asset */ + private static final String TAG = "AssetManager"; + private static final boolean DEBUG_REFS = false; + + private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk"; + + private static final Object sSync = new Object(); + + private static final ApkAssets[] sEmptyApkAssets = new ApkAssets[0]; + + // Not private for LayoutLib's BridgeAssetManager. + @GuardedBy("sSync") static AssetManager sSystem = null; + + @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0]; + @GuardedBy("sSync") private static ArraySet sSystemApkAssetsSet; /** * Mode for {@link #open(String, int)}: no specific information about how @@ -67,88 +91,392 @@ public final class AssetManager implements AutoCloseable { */ public static final int ACCESS_BUFFER = 3; - private static final String TAG = "AssetManager"; - private static final boolean localLOGV = false || false; - - private static final boolean DEBUG_REFS = false; - - private static final Object sSync = new Object(); - /*package*/ static AssetManager sSystem = null; + @GuardedBy("this") private final TypedValue mValue = new TypedValue(); + @GuardedBy("this") private final long[] mOffsets = new long[2]; - private final TypedValue mValue = new TypedValue(); - private final long[] mOffsets = new long[2]; - - // For communication with native code. - private long mObject; + // Pointer to native implementation, stuffed inside a long. + @GuardedBy("this") private long mObject; + + // The loaded asset paths. + @GuardedBy("this") private ApkAssets[] mApkAssets; + + // Debug/reference counting implementation. + @GuardedBy("this") private boolean mOpen = true; + @GuardedBy("this") private int mNumRefs = 1; + @GuardedBy("this") private HashMap mRefStacks; + + /** + * A Builder class that helps create an AssetManager with only a single invocation of + * {@link AssetManager#setApkAssets(ApkAssets[], boolean)}. Without using this builder, + * AssetManager must ensure there are system ApkAssets loaded at all times, which when combined + * with the user's call to add additional ApkAssets, results in multiple calls to + * {@link AssetManager#setApkAssets(ApkAssets[], boolean)}. + * @hide + */ + public static class Builder { + private ArrayList mUserApkAssets = new ArrayList<>(); + + public Builder addApkAssets(ApkAssets apkAssets) { + mUserApkAssets.add(apkAssets); + return this; + } + + public AssetManager build() { + // Retrieving the system ApkAssets forces their creation as well. + final ApkAssets[] systemApkAssets = getSystem().getApkAssets(); + + final int totalApkAssetCount = systemApkAssets.length + mUserApkAssets.size(); + final ApkAssets[] apkAssets = new ApkAssets[totalApkAssetCount]; + + System.arraycopy(systemApkAssets, 0, apkAssets, 0, systemApkAssets.length); + + final int userApkAssetCount = mUserApkAssets.size(); + for (int i = 0; i < userApkAssetCount; i++) { + apkAssets[i + systemApkAssets.length] = mUserApkAssets.get(i); + } + + // Calling this constructor prevents creation of system ApkAssets, which we took care + // of in this Builder. + final AssetManager assetManager = new AssetManager(false /*sentinel*/); + assetManager.mApkAssets = apkAssets; + AssetManager.nativeSetApkAssets(assetManager.mObject, apkAssets, + false /*invalidateCaches*/); + return assetManager; + } + } - private StringBlock mStringBlocks[] = null; - - private int mNumRefs = 1; - private boolean mOpen = true; - private HashMap mRefStacks; - /** * Create a new AssetManager containing only the basic system assets. * Applications will not generally use this method, instead retrieving the * appropriate asset manager with {@link Resources#getAssets}. Not for * use by applications. - * {@hide} + * @hide */ public AssetManager() { - synchronized (this) { - if (DEBUG_REFS) { - mNumRefs = 0; - incRefsLocked(this.hashCode()); - } - init(false); - if (localLOGV) Log.v(TAG, "New asset manager: " + this); - ensureSystemAssets(); + final ApkAssets[] assets; + synchronized (sSync) { + createSystemAssetsInZygoteLocked(); + assets = sSystemApkAssets; } - } - private static void ensureSystemAssets() { - synchronized (sSync) { - if (sSystem == null) { - AssetManager system = new AssetManager(true); - system.makeStringBlocks(null); - sSystem = system; - } + mObject = nativeCreate(); + if (DEBUG_REFS) { + mNumRefs = 0; + incRefsLocked(hashCode()); } + + // Always set the framework resources. + setApkAssets(assets, false /*invalidateCaches*/); } - - private AssetManager(boolean isSystem) { + + /** + * Private constructor that doesn't call ensureSystemAssets. + * Used for the creation of system assets. + */ + @SuppressWarnings("unused") + private AssetManager(boolean sentinel) { + mObject = nativeCreate(); if (DEBUG_REFS) { - synchronized (this) { - mNumRefs = 0; - incRefsLocked(this.hashCode()); + mNumRefs = 0; + incRefsLocked(hashCode()); + } + } + + /** + * This must be called from Zygote so that system assets are shared by all applications. + */ + @GuardedBy("sSync") + private static void createSystemAssetsInZygoteLocked() { + if (sSystem != null) { + return; + } + + // Make sure that all IDMAPs are up to date. + nativeVerifySystemIdmaps(); + + try { + final ArrayList apkAssets = new ArrayList<>(); + apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/)); + loadStaticRuntimeOverlays(apkAssets); + + sSystemApkAssetsSet = new ArraySet<>(apkAssets); + sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]); + sSystem = new AssetManager(true /*sentinel*/); + sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/); + } catch (IOException e) { + throw new IllegalStateException("Failed to create system AssetManager", e); + } + } + + /** + * Loads the static runtime overlays declared in /data/resource-cache/overlays.list. + * Throws an exception if the file is corrupt or if loading the APKs referenced by the file + * fails. Returns quietly if the overlays.list file doesn't exist. + * @param outApkAssets The list to fill with the loaded ApkAssets. + */ + private static void loadStaticRuntimeOverlays(ArrayList outApkAssets) + throws IOException { + final FileInputStream fis; + try { + fis = new FileInputStream("/data/resource-cache/overlays.list"); + } catch (FileNotFoundException e) { + // We might not have any overlays, this is fine. We catch here since ApkAssets + // loading can also fail with the same exception, which we would want to propagate. + Log.i(TAG, "no overlays.list file found"); + return; + } + + try { + // Acquire a lock so that any idmap scanning doesn't impact the current set. + // The order of this try-with-resources block matters. We must release the lock, and + // then close the file streams when exiting the block. + try (final BufferedReader br = new BufferedReader(new InputStreamReader(fis)); + final FileLock flock = fis.getChannel().lock(0, Long.MAX_VALUE, true /*shared*/)) { + for (String line; (line = br.readLine()) != null; ) { + final String idmapPath = line.split(" ")[1]; + outApkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/)); + } } + } finally { + // When BufferedReader is closed above, FileInputStream is closed as well. But let's be + // paranoid. + IoUtils.closeQuietly(fis); } - init(true); - if (localLOGV) Log.v(TAG, "New asset manager: " + this); } /** * Return a global shared asset manager that provides access to only * system assets (no application assets). - * {@hide} + * @hide */ public static AssetManager getSystem() { - ensureSystemAssets(); - return sSystem; + synchronized (sSync) { + createSystemAssetsInZygoteLocked(); + return sSystem; + } } /** * Close this asset manager. */ + @Override public void close() { - synchronized(this) { - //System.out.println("Release: num=" + mNumRefs - // + ", released=" + mReleased); + synchronized (this) { + if (!mOpen) { + return; + } + + mOpen = false; + decRefsLocked(hashCode()); + } + } + + /** + * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)} + * family of methods. + * + * @param apkAssets The new set of paths. + * @param invalidateCaches Whether to invalidate any caches. This should almost always be true. + * Set this to false if you are appending new resources + * (not new configurations). + * @hide + */ + public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) { + Preconditions.checkNotNull(apkAssets, "apkAssets"); + + ApkAssets[] newApkAssets = new ApkAssets[sSystemApkAssets.length + apkAssets.length]; + + // Copy the system assets first. + System.arraycopy(sSystemApkAssets, 0, newApkAssets, 0, sSystemApkAssets.length); + + // Copy the given ApkAssets if they are not already in the system list. + int newLength = sSystemApkAssets.length; + for (ApkAssets apkAsset : apkAssets) { + if (!sSystemApkAssetsSet.contains(apkAsset)) { + newApkAssets[newLength++] = apkAsset; + } + } + + // Truncate if necessary. + if (newLength != newApkAssets.length) { + newApkAssets = Arrays.copyOf(newApkAssets, newLength); + } + + synchronized (this) { + ensureOpenLocked(); + mApkAssets = newApkAssets; + nativeSetApkAssets(mObject, mApkAssets, invalidateCaches); + if (invalidateCaches) { + // Invalidate all caches. + invalidateCachesLocked(-1); + } + } + } + + /** + * Invalidates the caches in this AssetManager according to the bitmask `diff`. + * + * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}. + * @see ActivityInfo.Config + */ + private void invalidateCachesLocked(int diff) { + // TODO(adamlesinski): Currently there are no caches to invalidate in Java code. + } + + /** + * Returns the set of ApkAssets loaded by this AssetManager. If the AssetManager is closed, this + * returns a 0-length array. + * @hide + */ + public @NonNull ApkAssets[] getApkAssets() { + synchronized (this) { if (mOpen) { - mOpen = false; - decRefsLocked(this.hashCode()); + return mApkAssets; } } + return sEmptyApkAssets; + } + + /** + * Returns a cookie for use with the other APIs of AssetManager. + * @return 0 if the path was not found, otherwise a positive integer cookie representing + * this path in the AssetManager. + * @hide + */ + public int findCookieForPath(@NonNull String path) { + Preconditions.checkNotNull(path, "path"); + synchronized (this) { + ensureValidLocked(); + final int count = mApkAssets.length; + for (int i = 0; i < count; i++) { + if (path.equals(mApkAssets[i].getAssetPath())) { + return i + 1; + } + } + } + return 0; + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addAssetPath(String path) { + return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/); + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addAssetPathAsSharedLibrary(String path) { + return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/); + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addOverlayPath(String path) { + return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/); + } + + private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) { + Preconditions.checkNotNull(path, "path"); + synchronized (this) { + ensureOpenLocked(); + final int count = mApkAssets.length; + + // See if we already have it loaded. + for (int i = 0; i < count; i++) { + if (mApkAssets[i].getAssetPath().equals(path)) { + return i + 1; + } + } + + final ApkAssets assets; + try { + if (overlay) { + // TODO(b/70343104): This hardcoded path will be removed once + // addAssetPathInternal is deleted. + final String idmapPath = "/data/resource-cache/" + + path.substring(1).replace('/', '@') + + "@idmap"; + assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/); + } else { + assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib); + } + } catch (IOException e) { + return 0; + } + + mApkAssets = Arrays.copyOf(mApkAssets, count + 1); + mApkAssets[count] = assets; + nativeSetApkAssets(mObject, mApkAssets, true); + invalidateCachesLocked(-1); + return count + 1; + } + } + + /** + * Ensures that the native implementation has not been destroyed. + * The AssetManager may have been closed, but references to it still exist + * and therefore the native implementation is not destroyed. + */ + @GuardedBy("this") + private void ensureValidLocked() { + if (mObject == 0) { + throw new RuntimeException("AssetManager has been destroyed"); + } + } + + /** + * Ensures that the AssetManager has not been explicitly closed. If this method passes, + * then this implies that ensureValidLocked() also passes. + */ + @GuardedBy("this") + private void ensureOpenLocked() { + // If mOpen is true, this implies that mObject != 0. + if (!mOpen) { + throw new RuntimeException("AssetManager has been closed"); + } + } + + /** + * Populates {@code outValue} with the data associated a particular + * resource identifier for the current configuration. + * + * @param resId the resource identifier to load + * @param densityDpi the density bucket for which to load the resource + * @param outValue the typed value in which to put the data + * @param resolveRefs {@code true} to resolve references, {@code false} + * to leave them unresolved + * @return {@code true} if the data was loaded into {@code outValue}, + * {@code false} otherwise + */ + boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, + boolean resolveRefs) { + Preconditions.checkNotNull(outValue, "outValue"); + synchronized (this) { + ensureValidLocked(); + final int cookie = nativeGetResourceValue( + mObject, resId, (short) densityDpi, outValue, resolveRefs); + if (cookie <= 0) { + return false; + } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); + } + return true; + } } /** @@ -158,8 +486,7 @@ public final class AssetManager implements AutoCloseable { * @param resId the resource identifier to load * @return the string value, or {@code null} */ - @Nullable - final CharSequence getResourceText(@StringRes int resId) { + @Nullable CharSequence getResourceText(@StringRes int resId) { synchronized (this) { final TypedValue outValue = mValue; if (getResourceValue(resId, 0, outValue, true)) { @@ -174,15 +501,15 @@ public final class AssetManager implements AutoCloseable { * identifier for the current configuration. * * @param resId the resource identifier to load - * @param bagEntryId + * @param bagEntryId the index into the bag to load * @return the string value, or {@code null} */ - @Nullable - final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { + @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { synchronized (this) { + ensureValidLocked(); final TypedValue outValue = mValue; - final int block = loadResourceBagValue(resId, bagEntryId, outValue, true); - if (block < 0) { + final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue); + if (cookie <= 0) { return null; } @@ -191,52 +518,60 @@ public final class AssetManager implements AutoCloseable { outValue.changingConfigurations); if (outValue.type == TypedValue.TYPE_STRING) { - return mStringBlocks[block].get(outValue.data); + return mApkAssets[cookie - 1].getStringFromPool(outValue.data); } return outValue.coerceToString(); } } + int getResourceArraySize(@ArrayRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceArraySize(mObject, resId); + } + } + /** - * Retrieves the string array associated with a particular resource - * identifier for the current configuration. + * Populates `outData` with array elements of `resId`. `outData` is normally + * used with + * {@link TypedArray}. * - * @param resId the resource identifier of the string array - * @return the string array, or {@code null} + * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES} + * long, + * with the indices of the data representing the type, value, asset cookie, + * resource ID, + * configuration change mask, and density of the element. + * + * @param resId The resource ID of an array resource. + * @param outData The array to populate with data. + * @return The length of the array. + * + * @see TypedArray#STYLE_TYPE + * @see TypedArray#STYLE_DATA + * @see TypedArray#STYLE_ASSET_COOKIE + * @see TypedArray#STYLE_RESOURCE_ID + * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS + * @see TypedArray#STYLE_DENSITY */ - @Nullable - final String[] getResourceStringArray(@ArrayRes int resId) { - return getArrayStringResource(resId); + int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) { + Preconditions.checkNotNull(outData, "outData"); + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceArray(mObject, resId, outData); + } } /** - * Populates {@code outValue} with the data associated a particular - * resource identifier for the current configuration. + * Retrieves the string array associated with a particular resource + * identifier for the current configuration. * - * @param resId the resource identifier to load - * @param densityDpi the density bucket for which to load the resource - * @param outValue the typed value in which to put the data - * @param resolveRefs {@code true} to resolve references, {@code false} - * to leave them unresolved - * @return {@code true} if the data was loaded into {@code outValue}, - * {@code false} otherwise + * @param resId the resource identifier of the string array + * @return the string array, or {@code null} */ - final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, - boolean resolveRefs) { + @Nullable String[] getResourceStringArray(@ArrayRes int resId) { synchronized (this) { - final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs); - if (block < 0) { - return false; - } - - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); - - if (outValue.type == TypedValue.TYPE_STRING) { - outValue.string = mStringBlocks[block].get(outValue.data); - } - return true; + ensureValidLocked(); + return nativeGetResourceStringArray(mObject, resId); } } @@ -246,26 +581,48 @@ public final class AssetManager implements AutoCloseable { * * @param resId the resource id of the string array */ - final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { + @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { synchronized (this) { - final int[] rawInfoArray = getArrayStringInfo(resId); + ensureValidLocked(); + final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId); if (rawInfoArray == null) { return null; } + final int rawInfoArrayLen = rawInfoArray.length; final int infoArrayLen = rawInfoArrayLen / 2; - int block; - int index; final CharSequence[] retArray = new CharSequence[infoArrayLen]; for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) { - block = rawInfoArray[i]; - index = rawInfoArray[i + 1]; - retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null; + int cookie = rawInfoArray[i]; + int index = rawInfoArray[i + 1]; + retArray[j] = (index >= 0 && cookie > 0) + ? mApkAssets[cookie - 1].getStringFromPool(index) : null; } return retArray; } } + @Nullable int[] getResourceIntArray(@ArrayRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceIntArray(mObject, resId); + } + } + + /** + * Get the attributes for a style resource. These are the <item> + * elements in + * a <style> resource. + * @param resId The resource ID of the style + * @return An array of attribute IDs. + */ + @AttrRes int[] getStyleAttributes(@StyleRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetStyleAttributes(mObject, resId); + } + } + /** * Populates {@code outValue} with the data associated with a particular * resource identifier for the current configuration. Resolves theme @@ -279,73 +636,88 @@ public final class AssetManager implements AutoCloseable { * @return {@code true} if the data was loaded into {@code outValue}, * {@code false} otherwise */ - final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, + boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, boolean resolveRefs) { - final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs); - if (block < 0) { - return false; + Preconditions.checkNotNull(outValue, "outValue"); + synchronized (this) { + ensureValidLocked(); + final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue, + resolveRefs); + if (cookie <= 0) { + return false; + } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); + } + return true; } + } - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); + void dumpTheme(long theme, int priority, String tag, String prefix) { + synchronized (this) { + ensureValidLocked(); + nativeThemeDump(mObject, theme, priority, tag, prefix); + } + } - if (outValue.type == TypedValue.TYPE_STRING) { - final StringBlock[] blocks = ensureStringBlocks(); - outValue.string = blocks[block].get(outValue.data); + @Nullable String getResourceName(@AnyRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceName(mObject, resId); } - return true; } - /** - * Ensures the string blocks are loaded. - * - * @return the string blocks - */ - @NonNull - final StringBlock[] ensureStringBlocks() { + @Nullable String getResourcePackageName(@AnyRes int resId) { synchronized (this) { - if (mStringBlocks == null) { - makeStringBlocks(sSystem.mStringBlocks); - } - return mStringBlocks; + ensureValidLocked(); + return nativeGetResourcePackageName(mObject, resId); } } - /*package*/ final void makeStringBlocks(StringBlock[] seed) { - final int seedNum = (seed != null) ? seed.length : 0; - final int num = getStringBlockCount(); - mStringBlocks = new StringBlock[num]; - if (localLOGV) Log.v(TAG, "Making string blocks for " + this - + ": " + num); - for (int i=0; i Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len; - } - public final void close() throws IOException { - synchronized (AssetManager.this) { - if (mAsset != 0) { - destroyAsset(mAsset); - mAsset = 0; - decRefsLocked(hashCode()); - } - } - } - public final void mark(int readlimit) { - mMarkPos = seekAsset(mAsset, 0, 0); + ensureOpen(); + return nativeAssetReadChar(mAssetNativePtr); } - public final void reset() throws IOException { - seekAsset(mAsset, mMarkPos, -1); - } - public final int read(byte[] b) throws IOException { - return readAsset(mAsset, b, 0, b.length); + + @Override + public final int read(@NonNull byte[] b) throws IOException { + ensureOpen(); + Preconditions.checkNotNull(b, "b"); + return nativeAssetRead(mAssetNativePtr, b, 0, b.length); } - public final int read(byte[] b, int off, int len) throws IOException { - return readAsset(mAsset, b, off, len); + + @Override + public final int read(@NonNull byte[] b, int off, int len) throws IOException { + ensureOpen(); + Preconditions.checkNotNull(b, "b"); + return nativeAssetRead(mAssetNativePtr, b, off, len); } + + @Override public final long skip(long n) throws IOException { - long pos = seekAsset(mAsset, 0, 0); - if ((pos+n) > mLength) { - n = mLength-pos; + ensureOpen(); + long pos = nativeAssetSeek(mAssetNativePtr, 0, 0); + if ((pos + n) > mLength) { + n = mLength - pos; } if (n > 0) { - seekAsset(mAsset, n, 0); + nativeAssetSeek(mAssetNativePtr, n, 0); } return n; } - protected void finalize() throws Throwable - { - close(); + @Override + public final int available() throws IOException { + ensureOpen(); + final long len = nativeAssetGetRemainingLength(mAssetNativePtr); + return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len; } - private long mAsset; - private long mLength; - private long mMarkPos; - } - - /** - * Add an additional set of assets to the asset manager. This can be - * either a directory or ZIP file. Not for use by applications. Returns - * the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public final int addAssetPath(String path) { - return addAssetPathInternal(path, false); - } - - /** - * Add an application assets to the asset manager and loading it as shared library. - * This can be either a directory or ZIP file. Not for use by applications. Returns - * the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public final int addAssetPathAsSharedLibrary(String path) { - return addAssetPathInternal(path, true); - } - - private final int addAssetPathInternal(String path, boolean appAsLib) { - synchronized (this) { - int res = addAssetPathNative(path, appAsLib); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final boolean markSupported() { + return true; } - } - - private native final int addAssetPathNative(String path, boolean appAsLib); - - /** - * Add an additional set of assets to the asset manager from an already open - * FileDescriptor. Not for use by applications. - * This does not give full AssetManager functionality for these assets, - * since the origin of the file is not known for purposes of sharing, - * overlay resolution, and other features. However it does allow you - * to do simple access to the contents of the given fd as an apk file. - * Performs a dup of the underlying fd, so you must take care of still closing - * the FileDescriptor yourself (and can do that whenever you want). - * Returns the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public int addAssetFd(FileDescriptor fd, String debugPathName) { - return addAssetFdInternal(fd, debugPathName, false); - } - private int addAssetFdInternal(FileDescriptor fd, String debugPathName, - boolean appAsLib) { - synchronized (this) { - int res = addAssetFdNative(fd, debugPathName, appAsLib); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final void mark(int readlimit) { + ensureOpen(); + mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0); } - } - - private native int addAssetFdNative(FileDescriptor fd, String debugPathName, - boolean appAsLib); - /** - * Add a set of assets to overlay an already added set of assets. - * - * This is only intended for application resources. System wide resources - * are handled before any Java code is executed. - * - * {@hide} - */ - - public final int addOverlayPath(String idmapPath) { - synchronized (this) { - int res = addOverlayPathNative(idmapPath); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final void reset() throws IOException { + ensureOpen(); + nativeAssetSeek(mAssetNativePtr, mMarkPos, -1); } - } - /** - * See addOverlayPath. - * - * {@hide} - */ - public native final int addOverlayPathNative(String idmapPath); + @Override + public final void close() throws IOException { + if (mAssetNativePtr != 0) { + nativeAssetDestroy(mAssetNativePtr); + mAssetNativePtr = 0; - /** - * Add multiple sets of assets to the asset manager at once. See - * {@link #addAssetPath(String)} for more information. Returns array of - * cookies for each added asset with 0 indicating failure, or null if - * the input array of paths is null. - * {@hide} - */ - public final int[] addAssetPaths(String[] paths) { - if (paths == null) { - return null; + synchronized (AssetManager.this) { + decRefsLocked(hashCode()); + } + } } - int[] cookies = new int[paths.length]; - for (int i = 0; i < paths.length; i++) { - cookies[i] = addAssetPath(paths[i]); + @Override + protected void finalize() throws Throwable { + close(); } - return cookies; + private void ensureOpen() { + if (mAssetNativePtr == 0) { + throw new IllegalStateException("AssetInputStream is closed"); + } + } } /** * Determine whether the state in this asset manager is up-to-date with * the files on the filesystem. If false is returned, you need to * instantiate a new AssetManager class to see the new data. - * {@hide} + * @hide */ - public native final boolean isUpToDate(); + public boolean isUpToDate() { + for (ApkAssets apkAssets : getApkAssets()) { + if (!apkAssets.isUpToDate()) { + return false; + } + } + return true; + } /** * Get the locales that this asset manager contains data for. @@ -786,7 +1189,12 @@ public final class AssetManager implements AutoCloseable { * are of the form {@code ll_CC} where {@code ll} is a two letter language code, * and {@code CC} is a two letter country code. */ - public native final String[] getLocales(); + public String[] getLocales() { + synchronized (this) { + ensureValidLocked(); + return nativeGetLocales(mObject, false /*excludeSystem*/); + } + } /** * Same as getLocales(), except that locales that are only provided by the system (i.e. those @@ -796,132 +1204,58 @@ public final class AssetManager implements AutoCloseable { * assets support Cherokee and French, getLocales() would return * [Cherokee, English, French, German], while getNonSystemLocales() would return * [Cherokee, French]. - * {@hide} + * @hide */ - public native final String[] getNonSystemLocales(); - - /** {@hide} */ - public native final Configuration[] getSizeConfigurations(); + public String[] getNonSystemLocales() { + synchronized (this) { + ensureValidLocked(); + return nativeGetLocales(mObject, true /*excludeSystem*/); + } + } /** - * Change the configuation used when retrieving resources. Not for use by - * applications. - * {@hide} + * @hide */ - public native final void setConfiguration(int mcc, int mnc, String locale, - int orientation, int touchscreen, int density, int keyboard, - int keyboardHidden, int navigation, int screenWidth, int screenHeight, - int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, - int screenLayout, int uiMode, int colorMode, int majorVersion); + Configuration[] getSizeConfigurations() { + synchronized (this) { + ensureValidLocked(); + return nativeGetSizeConfigurations(mObject); + } + } /** - * Retrieve the resource identifier for the given resource name. + * Change the configuration used when retrieving resources. Not for use by + * applications. + * @hide */ - /*package*/ native final int getResourceIdentifier(String name, - String defType, - String defPackage); + public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation, + int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, + int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, + int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) { + synchronized (this) { + ensureValidLocked(); + nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density, + keyboard, keyboardHidden, navigation, screenWidth, screenHeight, + smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode, + colorMode, majorVersion); + } + } - /*package*/ native final String getResourceName(int resid); - /*package*/ native final String getResourcePackageName(int resid); - /*package*/ native final String getResourceTypeName(int resid); - /*package*/ native final String getResourceEntryName(int resid); - - private native final long openAsset(String fileName, int accessMode); - private final native ParcelFileDescriptor openAssetFd(String fileName, - long[] outOffsets) throws IOException; - private native final long openNonAssetNative(int cookie, String fileName, - int accessMode); - private native ParcelFileDescriptor openNonAssetFdNative(int cookie, - String fileName, long[] outOffsets) throws IOException; - private native final void destroyAsset(long asset); - private native final int readAssetChar(long asset); - private native final int readAsset(long asset, byte[] b, int off, int len); - private native final long seekAsset(long asset, long offset, int whence); - private native final long getAssetLength(long asset); - private native final long getAssetRemainingLength(long asset); - - /** Returns true if the resource was found, filling in mRetStringBlock and - * mRetData. */ - private native final int loadResourceValue(int ident, short density, TypedValue outValue, - boolean resolve); - /** Returns true if the resource was found, filling in mRetStringBlock and - * mRetData. */ - private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue, - boolean resolve); - /*package*/ static final int STYLE_NUM_ENTRIES = 6; - /*package*/ static final int STYLE_TYPE = 0; - /*package*/ static final int STYLE_DATA = 1; - /*package*/ static final int STYLE_ASSET_COOKIE = 2; - /*package*/ static final int STYLE_RESOURCE_ID = 3; - - /* Offset within typed data array for native changingConfigurations. */ - static final int STYLE_CHANGING_CONFIGURATIONS = 4; - - /*package*/ static final int STYLE_DENSITY = 5; - /*package*/ native static final void applyStyle(long theme, - int defStyleAttr, int defStyleRes, long xmlParser, - int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress); - /*package*/ native static final boolean resolveAttrs(long theme, - int defStyleAttr, int defStyleRes, int[] inValues, - int[] inAttrs, int[] outValues, int[] outIndices); - /*package*/ native final boolean retrieveAttributes( - long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); - /*package*/ native final int getArraySize(int resource); - /*package*/ native final int retrieveArray(int resource, int[] outValues); - private native final int getStringBlockCount(); - private native final long getNativeStringBlock(int block); - - /** - * {@hide} - */ - public native final String getCookieName(int cookie); - - /** - * {@hide} - */ - public native final SparseArray getAssignedPackageIdentifiers(); - - /** - * {@hide} - */ - public native static final int getGlobalAssetCount(); - /** - * {@hide} + * @hide */ - public native static final String getAssetAllocations(); - - /** - * {@hide} - */ - public native static final int getGlobalAssetManagerCount(); - - private native final long newTheme(); - private native final void deleteTheme(long theme); - /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force); - /*package*/ native static final void copyTheme(long dest, long source); - /*package*/ native static final void clearTheme(long theme); - /*package*/ native static final int loadThemeAttributeValue(long theme, int ident, - TypedValue outValue, - boolean resolve); - /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix); - /*package*/ native static final @NativeConfig int getThemeChangingConfigurations(long theme); - - private native final long openXmlAssetNative(int cookie, String fileName); - - private native final String[] getArrayStringResource(int arrayRes); - private native final int[] getArrayStringInfo(int arrayRes); - /*package*/ native final int[] getArrayIntResource(int arrayRes); - /*package*/ native final int[] getStyleAttributes(int themeRes); - - private native final void init(boolean isSystem); - private native final void destroy(); + public SparseArray getAssignedPackageIdentifiers() { + synchronized (this) { + ensureValidLocked(); + return nativeGetAssignedPackageIdentifiers(mObject); + } + } @GuardedBy("this") - private final void incRefsLocked(long id) { + private void incRefsLocked(long id) { if (DEBUG_REFS) { if (mRefStacks == null) { - mRefStacks = new HashMap(); + mRefStacks = new HashMap<>(); } RuntimeException ex = new RuntimeException(); ex.fillInStackTrace(); @@ -931,15 +1265,117 @@ public final class AssetManager implements AutoCloseable { } @GuardedBy("this") - private final void decRefsLocked(long id) { + private void decRefsLocked(long id) { if (DEBUG_REFS && mRefStacks != null) { mRefStacks.remove(id); } mNumRefs--; - //System.out.println("Dec streams: mNumRefs=" + mNumRefs - // + " mReleased=" + mReleased); - if (mNumRefs == 0) { - destroy(); + if (mNumRefs == 0 && mObject != 0) { + nativeDestroy(mObject); + mObject = 0; + mApkAssets = sEmptyApkAssets; } } + + // AssetManager setup native methods. + private static native long nativeCreate(); + private static native void nativeDestroy(long ptr); + private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, + boolean invalidateCaches); + private static native void nativeSetConfiguration(long ptr, int mcc, int mnc, + @Nullable String locale, int orientation, int touchscreen, int density, int keyboard, + int keyboardHidden, int navigation, int screenWidth, int screenHeight, + int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, + int uiMode, int colorMode, int majorVersion); + private static native @NonNull SparseArray nativeGetAssignedPackageIdentifiers( + long ptr); + + // File native methods. + private static native @Nullable String[] nativeList(long ptr, @NonNull String path) + throws IOException; + private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode); + private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr, + @NonNull String fileName, long[] outOffsets) throws IOException; + private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName, + int accessMode); + private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie, + @NonNull String fileName, @NonNull long[] outOffsets) throws IOException; + private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName); + + // Primitive resource native methods. + private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density, + @NonNull TypedValue outValue, boolean resolveReferences); + private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId, + @NonNull TypedValue outValue); + + private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr, + @StyleRes int resId); + private static native @Nullable String[] nativeGetResourceStringArray(long ptr, + @ArrayRes int resId); + private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr, + @ArrayRes int resId); + private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId); + private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId); + private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId, + @NonNull int[] outValues); + + // Resource name/ID native methods. + private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name, + @Nullable String defType, @Nullable String defPackage); + private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid); + private static native @Nullable String nativeGetResourcePackageName(long ptr, + @AnyRes int resid); + private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid); + private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid); + private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem); + private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr); + + // Style attribute retrieval native methods. + private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, + @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs, + long outValuesAddress, long outIndicesAddress); + private static native boolean nativeResolveAttrs(long ptr, long themePtr, + @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, + @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); + private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr, + @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); + + // Theme related native methods + private static native long nativeThemeCreate(long ptr); + private static native void nativeThemeDestroy(long themePtr); + private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, + boolean force); + static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr); + static native void nativeThemeClear(long themePtr); + private static native int nativeThemeGetAttributeValue(long ptr, long themePtr, + @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve); + private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag, + String prefix); + static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr); + + // AssetInputStream related native methods. + private static native void nativeAssetDestroy(long assetPtr); + private static native int nativeAssetReadChar(long assetPtr); + private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len); + private static native long nativeAssetSeek(long assetPtr, long offset, int whence); + private static native long nativeAssetGetLength(long assetPtr); + private static native long nativeAssetGetRemainingLength(long assetPtr); + + private static native void nativeVerifySystemIdmaps(); + + // Global debug native methods. + /** + * @hide + */ + public static native int getGlobalAssetCount(); + + /** + * @hide + */ + public static native String getAssetAllocations(); + + /** + * @hide + */ + public static native int getGlobalAssetManagerCount(); } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index ad85e71b86f9..d8133824f757 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -590,7 +590,7 @@ public class Resources { */ @NonNull public int[] getIntArray(@ArrayRes int id) throws NotFoundException { - int[] res = mResourcesImpl.getAssets().getArrayIntResource(id); + int[] res = mResourcesImpl.getAssets().getResourceIntArray(id); if (res != null) { return res; } @@ -613,13 +613,13 @@ public class Resources { @NonNull public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException { final ResourcesImpl impl = mResourcesImpl; - int len = impl.getAssets().getArraySize(id); + int len = impl.getAssets().getResourceArraySize(id); if (len < 0) { throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id)); } TypedArray array = TypedArray.obtain(this, len); - array.mLength = impl.getAssets().retrieveArray(id, array.mData); + array.mLength = impl.getAssets().getResourceArray(id, array.mData); array.mIndices[0] = 0; return array; @@ -1794,8 +1794,7 @@ public class Resources { // out the attributes from the XML file (applying type information // contained in the resources and such). XmlBlock.Parser parser = (XmlBlock.Parser)set; - mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, attrs, - array.mData, array.mIndices); + mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices); array.mXml = parser; diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 424fa833cd48..157910a043e9 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -170,7 +170,6 @@ public class ResourcesImpl { mDisplayAdjustments = displayAdjustments; mConfiguration.setToDefaults(); updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo()); - mAssets.ensureStringBlocks(); } public DisplayAdjustments getDisplayAdjustments() { @@ -1300,8 +1299,7 @@ public class ResourcesImpl { void applyStyle(int resId, boolean force) { synchronized (mKey) { - AssetManager.applyThemeStyle(mTheme, resId, force); - + mAssets.applyStyleToTheme(mTheme, resId, force); mThemeResId = resId; mKey.append(resId, force); } @@ -1310,7 +1308,7 @@ public class ResourcesImpl { void setTo(ThemeImpl other) { synchronized (mKey) { synchronized (other.mKey) { - AssetManager.copyTheme(mTheme, other.mTheme); + AssetManager.nativeThemeCopy(mTheme, other.mTheme); mThemeResId = other.mThemeResId; mKey.setTo(other.getKey()); @@ -1333,12 +1331,10 @@ public class ResourcesImpl { // out the attributes from the XML file (applying type information // contained in the resources and such). final XmlBlock.Parser parser = (XmlBlock.Parser) set; - AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes, - parser != null ? parser.mParseState : 0, - attrs, attrs.length, array.mDataAddress, array.mIndicesAddress); + mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs, + array.mDataAddress, array.mIndicesAddress); array.mTheme = wrapper; array.mXml = parser; - return array; } } @@ -1355,7 +1351,7 @@ public class ResourcesImpl { } final TypedArray array = TypedArray.obtain(wrapper.getResources(), len); - AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); + mAssets.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); array.mTheme = wrapper; array.mXml = null; return array; @@ -1375,14 +1371,14 @@ public class ResourcesImpl { @Config int getChangingConfigurations() { synchronized (mKey) { final @NativeConfig int nativeChangingConfig = - AssetManager.getThemeChangingConfigurations(mTheme); + AssetManager.nativeThemeGetChangingConfigurations(mTheme); return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig); } } public void dump(int priority, String tag, String prefix) { synchronized (mKey) { - AssetManager.dumpTheme(mTheme, priority, tag, prefix); + mAssets.dumpTheme(mTheme, priority, tag, prefix); } } @@ -1411,13 +1407,13 @@ public class ResourcesImpl { */ void rebase() { synchronized (mKey) { - AssetManager.clearTheme(mTheme); + AssetManager.nativeThemeClear(mTheme); // Reapply the same styles in the same order. for (int i = 0; i < mKey.mCount; i++) { final int resId = mKey.mResId[i]; final boolean force = mKey.mForce[i]; - AssetManager.applyThemeStyle(mTheme, resId, force); + mAssets.applyStyleToTheme(mTheme, resId, force); } } } diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index f33c75168a5f..cbb3c6df0558 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -61,6 +61,15 @@ public class TypedArray { return attrs; } + // STYLE_ prefixed constants are offsets within the typed data array. + static final int STYLE_NUM_ENTRIES = 6; + static final int STYLE_TYPE = 0; + static final int STYLE_DATA = 1; + static final int STYLE_ASSET_COOKIE = 2; + static final int STYLE_RESOURCE_ID = 3; + static final int STYLE_CHANGING_CONFIGURATIONS = 4; + static final int STYLE_DENSITY = 5; + private final Resources mResources; private DisplayMetrics mMetrics; private AssetManager mAssets; @@ -78,7 +87,7 @@ public class TypedArray { private void resize(int len) { mLength = len; - final int dataLen = len * AssetManager.STYLE_NUM_ENTRIES; + final int dataLen = len * STYLE_NUM_ENTRIES; final int indicesLen = len + 1; final VMRuntime runtime = VMRuntime.getRuntime(); if (mDataAddress == 0 || mData.length < dataLen) { @@ -166,9 +175,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -203,9 +212,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -242,14 +251,13 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_STRING) { - final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; + final int cookie = data[index + STYLE_ASSET_COOKIE]; if (cookie < 0) { - return mXml.getPooledString( - data[index+AssetManager.STYLE_DATA]).toString(); + return mXml.getPooledString(data[index + STYLE_DATA]).toString(); } } return null; @@ -274,11 +282,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); + data[index + STYLE_CHANGING_CONFIGURATIONS]); if ((changingConfigs & ~allowedChangingConfigs) != 0) { return null; } @@ -320,14 +328,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA] != 0; + return data[index + STYLE_DATA] != 0; } final TypedValue v = mValue; @@ -359,14 +367,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } final TypedValue v = mValue; @@ -396,16 +404,16 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FLOAT) { - return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]); + return Float.intBitsToFloat(data[index + STYLE_DATA]); } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } final TypedValue v = mValue; @@ -446,15 +454,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_STRING) { final TypedValue value = mValue; if (getValueAt(index, value)) { @@ -498,7 +506,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -533,7 +541,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -564,15 +572,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -612,15 +620,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimension( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -661,15 +668,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelOffset( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -711,15 +717,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index+AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -755,16 +760,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index+AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -795,15 +799,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } return defValue; @@ -833,15 +836,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FRACTION) { - return TypedValue.complexToFraction( - data[index+AssetManager.STYLE_DATA], base, pbase); + return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -874,10 +876,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) { - final int resid = data[index+AssetManager.STYLE_RESOURCE_ID]; + if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) { + final int resid = data[index + STYLE_RESOURCE_ID]; if (resid != 0) { return resid; } @@ -902,10 +904,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { - return data[index + AssetManager.STYLE_DATA]; + if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { + return data[index + STYLE_DATA]; } return defValue; } @@ -939,7 +941,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -975,7 +977,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -1006,7 +1008,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { return mResources.getTextArray(value.resourceId); } return null; @@ -1027,7 +1029,7 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue); + return getValueAt(index * STYLE_NUM_ENTRIES, outValue); } /** @@ -1043,8 +1045,8 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; - return mData[index + AssetManager.STYLE_TYPE]; + index *= STYLE_NUM_ENTRIES; + return mData[index + STYLE_TYPE]; } /** @@ -1063,9 +1065,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; return type != TypedValue.TYPE_NULL; } @@ -1084,11 +1086,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; return type != TypedValue.TYPE_NULL - || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; + || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; } /** @@ -1109,7 +1111,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { return value; } return null; @@ -1181,16 +1183,16 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * AssetManager.STYLE_NUM_ENTRIES; - if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { + final int index = i * STYLE_NUM_ENTRIES; + if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { // Not an attribute, ignore. continue; } // Null the entry so that we can safely call getZzz(). - data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL; + data[index + STYLE_TYPE] = TypedValue.TYPE_NULL; - final int attr = data[index + AssetManager.STYLE_DATA]; + final int attr = data[index + STYLE_DATA]; if (attr == 0) { // Useless data, ignore. continue; @@ -1231,45 +1233,44 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * AssetManager.STYLE_NUM_ENTRIES; - final int type = data[index + AssetManager.STYLE_TYPE]; + final int index = i * STYLE_NUM_ENTRIES; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { continue; } changingConfig |= ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); + data[index + STYLE_CHANGING_CONFIGURATIONS]); } return changingConfig; } private boolean getValueAt(int index, TypedValue outValue) { final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return false; } outValue.type = type; - outValue.data = data[index+AssetManager.STYLE_DATA]; - outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; - outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID]; + outValue.data = data[index + STYLE_DATA]; + outValue.assetCookie = data[index + STYLE_ASSET_COOKIE]; + outValue.resourceId = data[index + STYLE_RESOURCE_ID]; outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); - outValue.density = data[index+AssetManager.STYLE_DENSITY]; + data[index + STYLE_CHANGING_CONFIGURATIONS]); + outValue.density = data[index + STYLE_DENSITY]; outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null; return true; } private CharSequence loadStringValueAt(int index) { final int[] data = mData; - final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; + final int cookie = data[index + STYLE_ASSET_COOKIE]; if (cookie < 0) { if (mXml != null) { - return mXml.getPooledString( - data[index+AssetManager.STYLE_DATA]); + return mXml.getPooledString(data[index + STYLE_DATA]); } return null; } - return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]); + return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]); } /** @hide */ diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java index e6b957414ea8..d4ccffb83ca5 100644 --- a/core/java/android/content/res/XmlBlock.java +++ b/core/java/android/content/res/XmlBlock.java @@ -16,6 +16,7 @@ package android.content.res; +import android.annotation.Nullable; import android.util.TypedValue; import com.android.internal.util.XmlUtils; @@ -33,7 +34,7 @@ import java.io.Reader; * * {@hide} */ -final class XmlBlock { +final class XmlBlock implements AutoCloseable { private static final boolean DEBUG=false; public XmlBlock(byte[] data) { @@ -48,6 +49,7 @@ final class XmlBlock { mStrings = new StringBlock(nativeGetStringBlock(mNative), false); } + @Override public void close() { synchronized (this) { if (mOpen) { @@ -478,13 +480,13 @@ final class XmlBlock { * are doing! The given native object must exist for the entire lifetime * of this newly creating XmlBlock. */ - XmlBlock(AssetManager assets, long xmlBlock) { + XmlBlock(@Nullable AssetManager assets, long xmlBlock) { mAssets = assets; mNative = xmlBlock; mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false); } - private final AssetManager mAssets; + private @Nullable final AssetManager mAssets; private final long mNative; /*package*/ final StringBlock mStrings; private boolean mOpen = true; diff --git a/core/jni/Android.bp b/core/jni/Android.bp index b048977ec87a..1d2209315849 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -110,8 +110,8 @@ cc_library_shared { "android_util_AssetManager.cpp", "android_util_Binder.cpp", "android_util_EventLog.cpp", - "android_util_MemoryIntArray.cpp", "android_util_Log.cpp", + "android_util_MemoryIntArray.cpp", "android_util_PathParser.cpp", "android_util_Process.cpp", "android_util_StringBlock.cpp", @@ -191,6 +191,7 @@ cc_library_shared { "android_backup_FileBackupHelperBase.cpp", "android_backup_BackupHelperDispatcher.cpp", "android_app_backup_FullBackup.cpp", + "android_content_res_ApkAssets.cpp", "android_content_res_ObbScanner.cpp", "android_content_res_Configuration.cpp", "android_animation_PropertyValuesHolder.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index f280c7a6b9da..5ae4a521ad6d 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -123,6 +123,7 @@ extern int register_android_util_MemoryIntArray(JNIEnv* env); extern int register_android_util_PathParser(JNIEnv* env); extern int register_android_content_StringBlock(JNIEnv* env); extern int register_android_content_XmlBlock(JNIEnv* env); +extern int register_android_content_res_ApkAssets(JNIEnv* env); extern int register_android_graphics_Canvas(JNIEnv* env); extern int register_android_graphics_CanvasProperty(JNIEnv* env); extern int register_android_graphics_ColorFilter(JNIEnv* env); @@ -1346,6 +1347,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_AssetManager), REG_JNI(register_android_content_StringBlock), REG_JNI(register_android_content_XmlBlock), + REG_JNI(register_android_content_res_ApkAssets), REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_Hyphenator), REG_JNI(register_android_text_MeasuredParagraph), diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index 48aef4a8b320..ed032c78f6c7 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include "Utils.h" #include "FontUtils.h" @@ -205,7 +205,8 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b NPE_CHECK_RETURN_ZERO(env, jpath); NativeFamilyBuilder* builder = reinterpret_cast(builderPtr); - AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr); + + Guarded* mgr = AssetManagerForJavaObject(env, jassetMgr); if (NULL == mgr) { builder->axes.clear(); return false; @@ -217,27 +218,33 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b return false; } - Asset* asset; - if (isAsset) { - asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); - } else { - asset = cookie ? mgr->openNonAsset(static_cast(cookie), str.c_str(), - Asset::ACCESS_BUFFER) : mgr->openNonAsset(str.c_str(), Asset::ACCESS_BUFFER); + std::unique_ptr asset; + { + ScopedLock locked_mgr(*mgr); + if (isAsset) { + asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); + } else if (cookie > 0) { + // Valid java cookies are 1-based, but AssetManager cookies are 0-based. + asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast(cookie - 1), + Asset::ACCESS_BUFFER); + } else { + asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER); + } } - if (NULL == asset) { + if (nullptr == asset) { builder->axes.clear(); return false; } const void* buf = asset->getBuffer(false); if (NULL == buf) { - delete asset; builder->axes.clear(); return false; } - sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset)); + sk_sp data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, + asset.release())); return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); } diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 09e37e1a3de6..49a24a30f77e 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -361,7 +361,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName code->sdkVersion = sdkVersion; code->javaAssetManager = env->NewGlobalRef(jAssetMgr); - code->assetManager = assetManagerForJavaObject(env, jAssetMgr); + code->assetManager = NdkAssetManagerForJavaObject(env, jAssetMgr); if (obbDir != NULL) { dirStr = env->GetStringUTFChars(obbDir, NULL); diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp new file mode 100644 index 000000000000..7738d849be39 --- /dev/null +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_RESOURCES + +#include "android-base/macros.h" +#include "android-base/stringprintf.h" +#include "android-base/unique_fd.h" +#include "androidfw/ApkAssets.h" +#include "utils/misc.h" +#include "utils/Trace.h" + +#include "core_jni_helpers.h" +#include "jni.h" +#include "nativehelper/ScopedUtfChars.h" + +using ::android::base::unique_fd; + +namespace android { + +static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboolean system, + jboolean force_shared_lib, jboolean overlay) { + ScopedUtfChars path(env, java_path); + if (path.c_str() == nullptr) { + return 0; + } + + ATRACE_NAME(base::StringPrintf("LoadApkAssets(%s)", path.c_str()).c_str()); + + std::unique_ptr apk_assets; + if (overlay) { + apk_assets = ApkAssets::LoadOverlay(path.c_str(), system); + } else if (force_shared_lib) { + apk_assets = ApkAssets::LoadAsSharedLibrary(path.c_str(), system); + } else { + apk_assets = ApkAssets::Load(path.c_str(), system); + } + + if (apk_assets == nullptr) { + std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str()); + jniThrowException(env, "java/io/IOException", error_msg.c_str()); + return 0; + } + return reinterpret_cast(apk_assets.release()); +} + +static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor, + jstring friendly_name, jboolean system, jboolean force_shared_lib) { + ScopedUtfChars friendly_name_utf8(env, friendly_name); + if (friendly_name_utf8.c_str() == nullptr) { + return 0; + } + + ATRACE_NAME(base::StringPrintf("LoadApkAssetsFd(%s)", friendly_name_utf8.c_str()).c_str()); + + int fd = jniGetFDFromFileDescriptor(env, file_descriptor); + if (fd < 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); + return 0; + } + + unique_fd dup_fd(::dup(fd)); + if (dup_fd < 0) { + jniThrowIOException(env, errno); + return 0; + } + + std::unique_ptr apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), + friendly_name_utf8.c_str(), + system, force_shared_lib); + if (apk_assets == nullptr) { + std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d", + friendly_name_utf8.c_str(), dup_fd.get()); + jniThrowException(env, "java/io/IOException", error_msg.c_str()); + return 0; + } + return reinterpret_cast(apk_assets.release()); +} + +static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + delete reinterpret_cast(ptr); +} + +static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + return env->NewStringUTF(apk_assets->GetPath().c_str()); +} + +static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + return reinterpret_cast(apk_assets->GetLoadedArsc()->GetStringPool()); +} + +static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast(ptr); + (void)apk_assets; + return JNI_TRUE; +} + +static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) { + ScopedUtfChars path_utf8(env, file_name); + if (path_utf8.c_str() == nullptr) { + return 0; + } + + const ApkAssets* apk_assets = reinterpret_cast(ptr); + std::unique_ptr asset = apk_assets->Open(path_utf8.c_str(), + Asset::AccessMode::ACCESS_RANDOM); + if (asset == nullptr) { + jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str()); + return 0; + } + + // DynamicRefTable is only needed when looking up resource references. Opening an XML file + // directly from an ApkAssets has no notion of proper resource references. + std::unique_ptr xml_tree = util::make_unique(nullptr /*dynamicRefTable*/); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); + asset.reset(); + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + return reinterpret_cast(xml_tree.release()); +} + +// JNI registration. +static const JNINativeMethod gApkAssetsMethods[] = { + {"nativeLoad", "(Ljava/lang/String;ZZZ)J", (void*)NativeLoad}, + {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZ)J", + (void*)NativeLoadFromFd}, + {"nativeDestroy", "(J)V", (void*)NativeDestroy}, + {"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath}, + {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock}, + {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate}, + {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml}, +}; + +int register_android_content_res_ApkAssets(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods, + arraysize(gApkAssetsMethods)); +} + +} // namespace android diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 683b4c490ec3..8be6ed8c415d 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -1,1851 +1,1449 @@ -/* //device/libs/android_runtime/android_util_AssetManager.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ +/* + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define ATRACE_TAG ATRACE_TAG_RESOURCES #define LOG_TAG "asset" -#include - #include #include #include -#include -#include #include #include +#include +#include #include // for AID_SYSTEM +#include "android-base/logging.h" +#include "android-base/properties.h" +#include "android-base/stringprintf.h" +#include "android_runtime/android_util_AssetManager.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_util_Binder.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" +#include "androidfw/AssetManager2.h" #include "androidfw/AttributeResolution.h" +#include "androidfw/MutexGuard.h" #include "androidfw/ResourceTypes.h" -#include "android_runtime/AndroidRuntime.h" -#include "android_util_Binder.h" #include "core_jni_helpers.h" #include "jni.h" -#include -#include -#include +#include "nativehelper/JNIHelp.h" +#include "nativehelper/ScopedPrimitiveArray.h" +#include "nativehelper/ScopedStringChars.h" +#include "nativehelper/ScopedUtfChars.h" #include "utils/Log.h" #include "utils/misc.h" #include "utils/String8.h" +#include "utils/Trace.h" extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap); extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap); +using ::android::base::StringPrintf; namespace android { -static const bool kThrowOnBadId = false; - // ---------------------------------------------------------------------------- -static struct typedvalue_offsets_t -{ - jfieldID mType; - jfieldID mData; - jfieldID mString; - jfieldID mAssetCookie; - jfieldID mResourceId; - jfieldID mChangingConfigurations; - jfieldID mDensity; +static struct typedvalue_offsets_t { + jfieldID mType; + jfieldID mData; + jfieldID mString; + jfieldID mAssetCookie; + jfieldID mResourceId; + jfieldID mChangingConfigurations; + jfieldID mDensity; } gTypedValueOffsets; -static struct assetfiledescriptor_offsets_t -{ - jfieldID mFd; - jfieldID mStartOffset; - jfieldID mLength; +static struct assetfiledescriptor_offsets_t { + jfieldID mFd; + jfieldID mStartOffset; + jfieldID mLength; } gAssetFileDescriptorOffsets; -static struct assetmanager_offsets_t -{ - jfieldID mObject; +static struct assetmanager_offsets_t { + jfieldID mObject; } gAssetManagerOffsets; -static struct sparsearray_offsets_t -{ - jclass classObject; - jmethodID constructor; - jmethodID put; +static struct { + jfieldID native_ptr; +} gApkAssetsFields; + +static struct sparsearray_offsets_t { + jclass classObject; + jmethodID constructor; + jmethodID put; } gSparseArrayOffsets; -static struct configuration_offsets_t -{ - jclass classObject; - jmethodID constructor; - jfieldID mSmallestScreenWidthDpOffset; - jfieldID mScreenWidthDpOffset; - jfieldID mScreenHeightDpOffset; +static struct configuration_offsets_t { + jclass classObject; + jmethodID constructor; + jfieldID mSmallestScreenWidthDpOffset; + jfieldID mScreenWidthDpOffset; + jfieldID mScreenHeightDpOffset; } gConfigurationOffsets; -jclass g_stringClass = NULL; +jclass g_stringClass = nullptr; // ---------------------------------------------------------------------------- -static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, - const Res_value& value, uint32_t ref, ssize_t block, - uint32_t typeSpecFlags, ResTable_config* config = NULL); - -jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, - const Res_value& value, uint32_t ref, ssize_t block, - uint32_t typeSpecFlags, ResTable_config* config) -{ - env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType); - env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie, - static_cast(table->getTableCookie(block))); - env->SetIntField(outValue, gTypedValueOffsets.mData, value.data); - env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL); - env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref); - env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations, - typeSpecFlags); - if (config != NULL) { - env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density); - } - return block; +// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. +constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { + return cookie != kInvalidCookie ? static_cast(cookie + 1) : -1; } -// This is called by zygote (running as user root) as part of preloadResources. -static void verifySystemIdmaps() -{ - pid_t pid; - char system_id[10]; - - snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM); - - switch (pid = fork()) { - case -1: - ALOGE("failed to fork for idmap: %s", strerror(errno)); - break; - case 0: // child - { - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata; - - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - - capheader.version = _LINUX_CAPABILITY_VERSION; - capheader.pid = 0; - - if (capget(&capheader, &capdata) != 0) { - ALOGE("capget: %s\n", strerror(errno)); - exit(1); - } - - capdata.effective = capdata.permitted; - if (capset(&capheader, &capdata) != 0) { - ALOGE("capset: %s\n", strerror(errno)); - exit(1); - } - - if (setgid(AID_SYSTEM) != 0) { - ALOGE("setgid: %s\n", strerror(errno)); - exit(1); - } - - if (setuid(AID_SYSTEM) != 0) { - ALOGE("setuid: %s\n", strerror(errno)); - exit(1); - } - - // Generic idmap parameters - const char* argv[8]; - int argc = 0; - struct stat st; - - memset(argv, NULL, sizeof(argv)); - argv[argc++] = AssetManager::IDMAP_BIN; - argv[argc++] = "--scan"; - argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; - argv[argc++] = AssetManager::TARGET_APK_PATH; - argv[argc++] = AssetManager::IDMAP_DIR; - - // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, - // use OVERLAY_DIR/ in addition to OVERLAY_DIR. - char subdir[PROP_VALUE_MAX]; - int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir); - if (len > 0) { - String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir; - if (stat(overlayPath.string(), &st) == 0) { - argv[argc++] = overlayPath.string(); - } - } - if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::OVERLAY_DIR; - } - - if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; - } - - // Finally, invoke idmap (if any overlay directory exists) - if (argc > 5) { - execv(AssetManager::IDMAP_BIN, (char* const*)argv); - ALOGE("failed to execv for idmap: %s", strerror(errno)); - exit(1); // should never get here - } else { - exit(0); - } - } - break; - default: // parent - waitpid(pid, NULL, 0); - break; - } +constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) { + return cookie > 0 ? static_cast(cookie - 1) : kInvalidCookie; } - -// ---------------------------------------------------------------------------- - -// this guy is exported to other jni routines -AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj) -{ - jlong amHandle = env->GetLongField(obj, gAssetManagerOffsets.mObject); - AssetManager* am = reinterpret_cast(amHandle); - if (am != NULL) { - return am; - } - jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); - return NULL; +// This is called by zygote (running as user root) as part of preloadResources. +static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { + switch (pid_t pid = fork()) { + case -1: + PLOG(ERROR) << "failed to fork for idmap"; + break; + + // child + case 0: { + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata; + + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + + capheader.version = _LINUX_CAPABILITY_VERSION; + capheader.pid = 0; + + if (capget(&capheader, &capdata) != 0) { + PLOG(ERROR) << "capget"; + exit(1); + } + + capdata.effective = capdata.permitted; + if (capset(&capheader, &capdata) != 0) { + PLOG(ERROR) << "capset"; + exit(1); + } + + if (setgid(AID_SYSTEM) != 0) { + PLOG(ERROR) << "setgid"; + exit(1); + } + + if (setuid(AID_SYSTEM) != 0) { + PLOG(ERROR) << "setuid"; + exit(1); + } + + // Generic idmap parameters + const char* argv[8]; + int argc = 0; + struct stat st; + + memset(argv, 0, sizeof(argv)); + argv[argc++] = AssetManager::IDMAP_BIN; + argv[argc++] = "--scan"; + argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; + argv[argc++] = AssetManager::TARGET_APK_PATH; + argv[argc++] = AssetManager::IDMAP_DIR; + + // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, + // use OVERLAY_DIR/ in addition to OVERLAY_DIR. + std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY, + ""); + if (!overlay_theme_path.empty()) { + overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path; + if (stat(overlay_theme_path.c_str(), &st) == 0) { + argv[argc++] = overlay_theme_path.c_str(); + } + } + + if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::OVERLAY_DIR; + } + + if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; + } + + // Finally, invoke idmap (if any overlay directory exists) + if (argc > 5) { + execv(AssetManager::IDMAP_BIN, (char* const*)argv); + PLOG(ERROR) << "failed to execv for idmap"; + exit(1); // should never get here + } else { + exit(0); + } + } break; + + // parent + default: + waitpid(pid, nullptr, 0); + break; + } } -static jlong android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz, - jstring fileName, jint mode) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openAsset in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Empty file name"); - return -1; - } - - if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM - && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return -1; - } - - Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return -1; - } - - //printf("Created Asset Stream: %p\n", a); - - return reinterpret_cast(a); +static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref, + uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType); + env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie, + ApkAssetsCookieToJavaCookie(cookie)); + env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data); + env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr); + env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref); + env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags); + if (config != nullptr) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density); + } + return static_cast(ApkAssetsCookieToJavaCookie(cookie)); } -static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets) -{ - off64_t startOffset, length; - int fd = a->openFileDescriptor(&startOffset, &length); - delete a; - - if (fd < 0) { - jniThrowException(env, "java/io/FileNotFoundException", - "This file can not be opened as a file descriptor; it is probably compressed"); - return NULL; - } - - jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0); - if (offsets == NULL) { - close(fd); - return NULL; - } - - offsets[0] = startOffset; - offsets[1] = length; - - env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0); +// ---------------------------------------------------------------------------- - jobject fileDesc = jniCreateFileDescriptor(env, fd); - if (fileDesc == NULL) { - close(fd); - return NULL; - } +// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance. +struct GuardedAssetManager : public ::AAssetManager { + Guarded guarded_assetmanager; +}; - return newParcelFileDescriptor(env, fileDesc); +::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { + jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject); + ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle); + if (am == nullptr) { + jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); + return nullptr; + } + return am; } -static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz, - jstring fileName, jlongArray outOffsets) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ALOGV("openAssetFd in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - //printf("Created Asset Stream: %p\n", a); - - return returnParcelFileDescriptor(env, a, outOffsets); +Guarded* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) { + if (assetmanager == nullptr) { + return nullptr; + } + return &reinterpret_cast(assetmanager)->guarded_assetmanager; } -static jlong android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName, - jint mode) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return -1; - } - - if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM - && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return -1; - } - - Asset* a = cookie - ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), - (Asset::AccessMode)mode) - : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return -1; - } - - //printf("Created Asset Stream: %p\n", a); - - return reinterpret_cast(a); +Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { + return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager)); } -static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName, - jlongArray outOffsets) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ALOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - Asset* a = cookie - ? am->openNonAsset(static_cast(cookie), fileName8.c_str(), Asset::ACCESS_RANDOM) - : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - //printf("Created Asset Stream: %p\n", a); - - return returnParcelFileDescriptor(env, a, outOffsets); +static Guarded& AssetManagerFromLong(jlong ptr) { + return *AssetManagerForNdkAssetManager(reinterpret_cast(ptr)); } -static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz, - jstring fileName) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - AssetDir* dir = am->openDir(fileName8.c_str()); - - if (dir == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - size_t N = dir->getFileCount(); - - jobjectArray array = env->NewObjectArray(dir->getFileCount(), - g_stringClass, NULL); - if (array == NULL) { - delete dir; - return NULL; - } - - for (size_t i=0; igetFileName(i); - jstring str = env->NewStringUTF(name.string()); - if (str == NULL) { - delete dir; - return NULL; - } - env->SetObjectArrayElement(array, i, str); - env->DeleteLocalRef(str); - } - - delete dir; - - return array; +static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr asset, + jlongArray out_offsets) { + off64_t start_offset, length; + int fd = asset->openFileDescriptor(&start_offset, &length); + asset.reset(); + + if (fd < 0) { + jniThrowException(env, "java/io/FileNotFoundException", + "This file can not be opened as a file descriptor; it is probably " + "compressed"); + return nullptr; + } + + jlong* offsets = reinterpret_cast(env->GetPrimitiveArrayCritical(out_offsets, 0)); + if (offsets == nullptr) { + close(fd); + return nullptr; + } + + offsets[0] = start_offset; + offsets[1] = length; + + env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0); + + jobject file_desc = jniCreateFileDescriptor(env, fd); + if (file_desc == nullptr) { + close(fd); + return nullptr; + } + return newParcelFileDescriptor(env, file_desc); } -static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - //printf("Destroying Asset Stream: %p\n", a); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return; - } - - delete a; +static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) { + return Asset::getGlobalCount(); } -static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - uint8_t b; - ssize_t res = a->read(&b, 1); - return res == 1 ? b : -1; +static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) { + String8 alloc = Asset::getAssetAllocations(); + if (alloc.length() <= 0) { + return nullptr; + } + return env->NewStringUTF(alloc.string()); } -static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz, - jlong assetHandle, jbyteArray bArray, - jint off, jint len) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL || bArray == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - if (len == 0) { - return 0; - } - - jsize bLen = env->GetArrayLength(bArray); - if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); - return -1; - } - - jbyte* b = env->GetByteArrayElements(bArray, NULL); - ssize_t res = a->read(b+off, len); - env->ReleaseByteArrayElements(bArray, b, 0); - - if (res > 0) return static_cast(res); - - if (res < 0) { - jniThrowException(env, "java/io/IOException", ""); - } - return -1; +static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) { + // TODO(adamlesinski): Switch to AssetManager2. + return AssetManager::getGlobalCount(); } -static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz, - jlong assetHandle, - jlong offset, jint whence) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - return a->seek( - offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)); +static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) { + // AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and + // AssetManager2 in a contiguous block (GuardedAssetManager). + return reinterpret_cast(new GuardedAssetManager()); } -static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - return a->getLength(); +static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + delete reinterpret_cast(ptr); } -static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast(assetHandle); +static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jobjectArray apk_assets_array, jboolean invalidate_caches) { + ATRACE_NAME("AssetManager::SetApkAssets"); - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; + const jsize apk_assets_len = env->GetArrayLength(apk_assets_array); + std::vector apk_assets; + apk_assets.reserve(apk_assets_len); + for (jsize i = 0; i < apk_assets_len; i++) { + jobject obj = env->GetObjectArrayElement(apk_assets_array, i); + if (obj == nullptr) { + std::string msg = StringPrintf("ApkAssets at index %d is null", i); + jniThrowNullPointerException(env, msg.c_str()); + return; } - return a->getRemainingLength(); -} - -static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz, - jstring path, jboolean appAsLib) -{ - ScopedUtfChars path8(env, path); - if (path8.c_str() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; + jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr); + if (env->ExceptionCheck()) { + return; } + apk_assets.push_back(reinterpret_cast(apk_assets_native_ptr)); + } - int32_t cookie; - bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib); - - return (res) ? static_cast(cookie) : 0; + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + assetmanager->SetApkAssets(apk_assets, invalidate_caches); } -static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz, - jstring idmapPath) -{ - ScopedUtfChars idmapPath8(env, idmapPath); - if (idmapPath8.c_str() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - int32_t cookie; - bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie); - - return (res) ? (jint)cookie : 0; +static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc, + jstring locale, jint orientation, jint touchscreen, jint density, + jint keyboard, jint keyboard_hidden, jint navigation, + jint screen_width, jint screen_height, + jint smallest_screen_width_dp, jint screen_width_dp, + jint screen_height_dp, jint screen_layout, jint ui_mode, + jint color_mode, jint major_version) { + ATRACE_NAME("AssetManager::SetConfiguration"); + + ResTable_config configuration; + memset(&configuration, 0, sizeof(configuration)); + configuration.mcc = static_cast(mcc); + configuration.mnc = static_cast(mnc); + configuration.orientation = static_cast(orientation); + configuration.touchscreen = static_cast(touchscreen); + configuration.density = static_cast(density); + configuration.keyboard = static_cast(keyboard); + configuration.inputFlags = static_cast(keyboard_hidden); + configuration.navigation = static_cast(navigation); + configuration.screenWidth = static_cast(screen_width); + configuration.screenHeight = static_cast(screen_height); + configuration.smallestScreenWidthDp = static_cast(smallest_screen_width_dp); + configuration.screenWidthDp = static_cast(screen_width_dp); + configuration.screenHeightDp = static_cast(screen_height_dp); + configuration.screenLayout = static_cast(screen_layout); + configuration.uiMode = static_cast(ui_mode); + configuration.colorMode = static_cast(color_mode); + configuration.sdkVersion = static_cast(major_version); + + if (locale != nullptr) { + ScopedUtfChars locale_utf8(env, locale); + CHECK(locale_utf8.c_str() != nullptr); + configuration.setBcp47Locale(locale_utf8.c_str()); + } + + // Constants duplicated from Java class android.content.res.Configuration. + static const jint kScreenLayoutRoundMask = 0x300; + static const jint kScreenLayoutRoundShift = 8; + + // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer + // in C++. We must extract the round qualifier out of the Java screenLayout and put it + // into screenLayout2. + configuration.screenLayout2 = + static_cast((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + assetmanager->SetConfiguration(configuration); } -static jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz, - jobject fileDescriptor, jstring debugPathName, - jboolean appAsLib) -{ - ScopedUtfChars debugPathName8(env, debugPathName); +static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); - int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (fd < 0) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); - return 0; - } + jobject sparse_array = + env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor); - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } + if (sparse_array == nullptr) { + // An exception is pending. + return nullptr; + } - int dupfd = ::dup(fd); - if (dupfd < 0) { - jniThrowIOException(env, errno); - return 0; + assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) { + jstring jpackage_name = env->NewStringUTF(package_name.c_str()); + if (jpackage_name == nullptr) { + // An exception is pending. + return; } - int32_t cookie; - bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib); - - return (res) ? static_cast(cookie) : 0; + env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast(package_id), + jpackage_name); + }); + return sparse_array; } -static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_TRUE; - } - return am->isUpToDate() ? JNI_TRUE : JNI_FALSE; -} +static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) { + ScopedUtfChars path_utf8(env, path); + if (path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } -static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales) -{ - Vector locales; + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset_dir = + assetmanager->OpenDir(path_utf8.c_str()); + if (asset_dir == nullptr) { + jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str()); + return nullptr; + } - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } + const size_t file_count = asset_dir->getFileCount(); - am->getLocales(&locales, includeSystemLocales); + jobjectArray array = env->NewObjectArray(file_count, g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } - const int N = locales.size(); + for (size_t i = 0; i < file_count; i++) { + jstring java_string = env->NewStringUTF(asset_dir->getFileName(i).string()); - jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL); - if (result == NULL) { - return NULL; + // Check for errors creating the strings (if malformed or no memory). + if (env->ExceptionCheck()) { + return nullptr; } - for (int i=0; iNewStringUTF(locales[i].string()); - if (str == NULL) { - return NULL; - } - env->SetObjectArrayElement(result, i, str); - env->DeleteLocalRef(str); - } + env->SetObjectArrayElement(array, i, java_string); - return result; + // If we have a large amount of string in our array, we might overflow the + // local reference table of the VM. + env->DeleteLocalRef(java_string); + } + return array; } -static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz) -{ - return getLocales(env, clazz, true /* include system locales */); +static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, + jint access_mode) { + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + ATRACE_NAME(base::StringPrintf("AssetManager::OpenAsset(%s)", asset_path_utf8.c_str()).c_str()); + + if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && + access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset = + assetmanager->Open(asset_path_utf8.c_str(), static_cast(access_mode)); + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + return reinterpret_cast(asset.release()); } -static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz) -{ - return getLocales(env, clazz, false /* don't include system locales */); +static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, + jlongArray out_offsets) { + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + ATRACE_NAME(base::StringPrintf("AssetManager::OpenAssetFd(%s)", asset_path_utf8.c_str()).c_str()); + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return nullptr; + } + return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); } -static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) { - jobject result = env->NewObject(gConfigurationOffsets.classObject, - gConfigurationOffsets.constructor); - if (result == NULL) { - return NULL; - } - - env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, - config.smallestScreenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); - - return result; +static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path, jint access_mode) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + ATRACE_NAME(base::StringPrintf("AssetManager::OpenNonAsset(%s)", asset_path_utf8.c_str()).c_str()); + + if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && + access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return 0; + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, + static_cast(access_mode)); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), + static_cast(access_mode)); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + return reinterpret_cast(asset.release()); } -static jobjectArray getSizeConfigurationsInternal(JNIEnv* env, - const Vector& configs) { - const int N = configs.size(); - jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL); - if (result == NULL) { - return NULL; - } - - for (int i=0; iDeleteLocalRef(result); - return NULL; - } - - env->SetObjectArrayElement(result, i, config); - env->DeleteLocalRef(config); - } - - return result; +static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path, jlongArray out_offsets) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + ATRACE_NAME(base::StringPrintf("AssetManager::OpenNonAssetFd(%s)", asset_path_utf8.c_str()).c_str()); + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return nullptr; + } + return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); } -static jobjectArray android_content_AssetManager_getSizeConfigurations(JNIEnv* env, jobject clazz) { - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - const ResTable& res(am->getResources()); - Vector configs; - res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */); - - return getSizeConfigurationsInternal(env, configs); +static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + ATRACE_NAME(base::StringPrintf("AssetManager::OpenXmlAsset(%s)", asset_path_utf8.c_str()).c_str()); + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + + // May be nullptr. + const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie); + + std::unique_ptr xml_tree = util::make_unique(dynamic_ref_table); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); + asset.reset(); + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + return reinterpret_cast(xml_tree.release()); } -static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz, - jint mcc, jint mnc, - jstring locale, jint orientation, - jint touchscreen, jint density, - jint keyboard, jint keyboardHidden, - jint navigation, - jint screenWidth, jint screenHeight, - jint smallestScreenWidthDp, - jint screenWidthDp, jint screenHeightDp, - jint screenLayout, jint uiMode, - jint colorMode, jint sdkVersion) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return; - } - - ResTable_config config; - memset(&config, 0, sizeof(config)); - - const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL; - - // Constants duplicated from Java class android.content.res.Configuration. - static const jint kScreenLayoutRoundMask = 0x300; - static const jint kScreenLayoutRoundShift = 8; - - config.mcc = (uint16_t)mcc; - config.mnc = (uint16_t)mnc; - config.orientation = (uint8_t)orientation; - config.touchscreen = (uint8_t)touchscreen; - config.density = (uint16_t)density; - config.keyboard = (uint8_t)keyboard; - config.inputFlags = (uint8_t)keyboardHidden; - config.navigation = (uint8_t)navigation; - config.screenWidth = (uint16_t)screenWidth; - config.screenHeight = (uint16_t)screenHeight; - config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp; - config.screenWidthDp = (uint16_t)screenWidthDp; - config.screenHeightDp = (uint16_t)screenHeightDp; - config.screenLayout = (uint8_t)screenLayout; - config.uiMode = (uint8_t)uiMode; - config.colorMode = (uint8_t)colorMode; - config.sdkVersion = (uint16_t)sdkVersion; - config.minorVersion = 0; - - // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer - // in C++. We must extract the round qualifier out of the Java screenLayout and put it - // into screenLayout2. - config.screenLayout2 = - (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); - - am->setConfiguration(config, locale8); - - if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8); +static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jshort density, jobject typed_value, + jboolean resolve_references) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Res_value value; + ResTable_config selected_config; + uint32_t flags; + ApkAssetsCookie cookie = + assetmanager->GetResource(static_cast(resid), false /*may_be_bag*/, + static_cast(density), &value, &selected_config, &flags); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t ref = static_cast(resid); + if (resolve_references) { + cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + } + return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value); } -static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz, - jstring name, - jstring defType, - jstring defPackage) -{ - ScopedStringChars name16(env, name); - if (name16.get() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - const char16_t* defType16 = reinterpret_cast(defType) - ? reinterpret_cast(env->GetStringChars(defType, NULL)) - : NULL; - jsize defTypeLen = defType - ? env->GetStringLength(defType) : 0; - const char16_t* defPackage16 = reinterpret_cast(defPackage) - ? reinterpret_cast(env->GetStringChars(defPackage, - NULL)) - : NULL; - jsize defPackageLen = defPackage - ? env->GetStringLength(defPackage) : 0; - - jint ident = am->getResources().identifierForName( - reinterpret_cast(name16.get()), name16.size(), - defType16, defTypeLen, defPackage16, defPackageLen); - - if (defPackage16) { - env->ReleaseStringChars(defPackage, - reinterpret_cast(defPackage16)); - } - if (defType16) { - env->ReleaseStringChars(defType, - reinterpret_cast(defType16)); - } - - return ident; +static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jint bag_entry_id, jobject typed_value) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t type_spec_flags = bag->type_spec_flags; + ApkAssetsCookie cookie = kInvalidCookie; + const Res_value* bag_value = nullptr; + for (const ResolvedBag::Entry& entry : bag) { + if (entry.key == static_cast(bag_entry_id)) { + cookie = entry.cookie; + bag_value = &entry.value; + + // Keep searching (the old implementation did that). + } + } + + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + Res_value value = *bag_value; + uint32_t ref = static_cast(resid); + ResTable_config selected_config; + cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value); } -static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - String16 str; - if (name.package != NULL) { - str.setTo(name.package, name.packageLen); - } - if (name.type8 != NULL || name.type != NULL) { - if (str.size() > 0) { - char16_t div = ':'; - str.append(&div, 1); - } - if (name.type8 != NULL) { - str.append(String16(name.type8, name.typeLen)); - } else { - str.append(name.type, name.typeLen); - } - } - if (name.name8 != NULL || name.name != NULL) { - if (str.size() > 0) { - char16_t div = '/'; - str.append(&div, 1); - } - if (name.name8 != NULL) { - str.append(String16(name.name8, name.nameLen)); - } else { - str.append(name.name, name.nameLen); - } - } - - return env->NewString((const jchar*)str.string(), str.size()); +static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count); + if (env->ExceptionCheck()) { + return nullptr; + } + + for (uint32_t i = 0; i < bag->entry_count; i++) { + jint attr_resid = bag->entries[i].key; + env->SetIntArrayRegion(array, i, 1, &attr_resid); + } + return array; } -static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - if (name.package != NULL) { - return env->NewString((const jchar*)name.package, name.packageLen); - } - - return NULL; +static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } + + for (uint32_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + + // Resolve any references to their final value. + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return nullptr; + } + + if (value.dataType == Res_value::TYPE_STRING) { + const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie]; + const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool(); + + jstring java_string = nullptr; + size_t str_len; + const char* str_utf8 = pool->string8At(value.data, &str_len); + if (str_utf8 != nullptr) { + java_string = env->NewStringUTF(str_utf8); + } else { + const char16_t* str_utf16 = pool->stringAt(value.data, &str_len); + java_string = env->NewString(reinterpret_cast(str_utf16), str_len); + } + + // Check for errors creating the strings (if malformed or no memory). + if (env->ExceptionCheck()) { + return nullptr; + } + + env->SetObjectArrayElement(array, i, java_string); + + // If we have a large amount of string in our array, we might overflow the + // local reference table of the VM. + env->DeleteLocalRef(java_string); + } + } + return array; } -static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - if (name.type8 != NULL) { - return env->NewStringUTF(name.type8); - } - - if (name.type != NULL) { - return env->NewString((const jchar*)name.type, name.typeLen); - } +static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count * 2); + if (array == nullptr) { + return nullptr; + } + + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); + if (buffer == nullptr) { + return nullptr; + } + + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); + return nullptr; + } + + jint string_index = -1; + if (value.dataType == Res_value::TYPE_STRING) { + string_index = static_cast(value.data); + } + + buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie); + buffer[(i * 2) + 1] = string_index; + } + env->ReleasePrimitiveArrayCritical(array, buffer, 0); + return array; +} - return NULL; +static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count); + if (array == nullptr) { + return nullptr; + } + + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); + if (buffer == nullptr) { + return nullptr; + } + + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); + return nullptr; + } + + if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) { + buffer[i] = static_cast(value.data); + } + } + env->ReleasePrimitiveArrayCritical(array, buffer, 0); + return array; } -static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } +static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return -1; + } + return static_cast(bag->entry_count); +} - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } +static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jintArray out_data) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast(resid)); + if (bag == nullptr) { + return -1; + } - if (name.name8 != NULL) { - return env->NewStringUTF(name.name8); - } + const jsize out_data_length = env->GetArrayLength(out_data); + if (env->ExceptionCheck()) { + return -1; + } - if (name.name != NULL) { - return env->NewString((const jchar*)name.name, name.nameLen); - } + if (static_cast(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough"); + return -1; + } - return NULL; + jint* buffer = reinterpret_cast(env->GetPrimitiveArrayCritical(out_data, nullptr)); + if (buffer == nullptr) { + return -1; + } + + jint* cursor = buffer; + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + selected_config.density = 0; + uint32_t flags = bag->type_spec_flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT); + return -1; + } + + // Deal with the special @null value -- it turns back to TYPE_NULL. + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + } + + cursor[STYLE_TYPE] = static_cast(value.dataType); + cursor[STYLE_DATA] = static_cast(value.data); + cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + cursor[STYLE_RESOURCE_ID] = static_cast(ref); + cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast(flags); + cursor[STYLE_DENSITY] = static_cast(selected_config.density); + cursor += STYLE_NUM_ENTRIES; + } + env->ReleasePrimitiveArrayCritical(out_data, buffer, 0); + return static_cast(bag->entry_count); } -static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz, - jint ident, - jshort density, - jobject outValue, - jboolean resolve) -{ - if (outValue == NULL) { - jniThrowNullPointerException(env, "outValue"); - return 0; - } - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - Res_value value; - ResTable_config config; - uint32_t typeSpecFlags; - ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - uint32_t ref = ident; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - } - if (block >= 0) { - return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config); - } - - return static_cast(block); +static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name, + jstring def_type, jstring def_package) { + ScopedUtfChars name_utf8(env, name); + if (name_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + std::string type; + if (def_type != nullptr) { + ScopedUtfChars type_utf8(env, def_type); + CHECK(type_utf8.c_str() != nullptr); + type = type_utf8.c_str(); + } + + std::string package; + if (def_package != nullptr) { + ScopedUtfChars package_utf8(env, def_package); + CHECK(package_utf8.c_str() != nullptr); + package = package_utf8.c_str(); + } + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + return static_cast(assetmanager->GetResourceId(name_utf8.c_str(), type, package)); } -static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz, - jint ident, jint bagEntryId, - jobject outValue, jboolean resolve) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - - ssize_t block = -1; - Res_value value; +static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } - const ResTable::bag_entry* entry = NULL; - uint32_t typeSpecFlags; - ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags); + std::string result; + if (name.package != nullptr) { + result.append(name.package, name.package_len); + } - for (ssize_t i=0; imap.name.ident) { - block = entry->stringBlock; - value = entry->map.value; - } - entry++; + if (name.type != nullptr || name.type16 != nullptr) { + if (!result.empty()) { + result += ":"; } - res.unlock(); - - if (block < 0) { - return static_cast(block); + if (name.type != nullptr) { + result.append(name.type, name.type_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len)); } + } - uint32_t ref = ident; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - } - if (block >= 0) { - return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags); + if (name.entry != nullptr || name.entry16 != nullptr) { + if (!result.empty()) { + result += "/"; } - return static_cast(block); -} - -static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; + if (name.entry != nullptr) { + result.append(name.entry, name.entry_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); } - return am->getResources().getTableCount(); + } + return env->NewStringUTF(result.c_str()); } -static jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz, - jint block) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - return reinterpret_cast(am->getResources().getTableStringBlock(block)); +static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.package != nullptr) { + return env->NewStringUTF(name.package); + } + return nullptr; } -static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz, - jint cookie) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - String8 name(am->getAssetPath(static_cast(cookie))); - if (name.length() == 0) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name"); - return NULL; - } - jstring str = env->NewStringUTF(name.string()); - return str; +static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.type != nullptr) { + return env->NewStringUTF(name.type); + } else if (name.type16 != nullptr) { + return env->NewString(reinterpret_cast(name.type16), name.type_len); + } + return nullptr; } -static jobject android_content_AssetManager_getAssignedPackageIdentifiers(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - const ResTable& res = am->getResources(); - - jobject sparseArray = env->NewObject(gSparseArrayOffsets.classObject, - gSparseArrayOffsets.constructor); - const size_t N = res.getBasePackageCount(); - for (size_t i = 0; i < N; i++) { - const String16 name = res.getBasePackageName(i); - env->CallVoidMethod( - sparseArray, gSparseArrayOffsets.put, - static_cast(res.getBasePackageId(i)), - env->NewString(reinterpret_cast(name.string()), - name.size())); - } - return sparseArray; +static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast(resid), &name)) { + return nullptr; + } + + if (name.entry != nullptr) { + return env->NewStringUTF(name.entry); + } else if (name.entry16 != nullptr) { + return env->NewString(reinterpret_cast(name.entry16), name.entry_len); + } + return nullptr; } -static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - return reinterpret_cast(new ResTable::Theme(am->getResources())); +static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr, + jboolean exclude_system) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::set locales = + assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/); + + jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } + + size_t idx = 0; + for (const std::string& locale : locales) { + jstring java_string = env->NewStringUTF(locale.c_str()); + if (java_string == nullptr) { + return nullptr; + } + env->SetObjectArrayElement(array, idx++, java_string); + env->DeleteLocalRef(java_string); + } + return array; } -static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz, - jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - delete theme; +static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) { + jobject result = + env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor); + if (result == nullptr) { + return nullptr; + } + + env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, + config.smallestScreenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); + return result; } -static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz, - jlong themeHandle, - jint styleRes, - jboolean force) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - theme->applyStyle(styleRes, force ? true : false); -} +static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + std::set configurations = + assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/); -static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz, - jlong destHandle, jlong srcHandle) -{ - ResTable::Theme* dest = reinterpret_cast(destHandle); - ResTable::Theme* src = reinterpret_cast(srcHandle); - dest->setTo(*src); -} + jobjectArray array = + env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr); + if (array == nullptr) { + return nullptr; + } -static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - theme->clear(); -} - -static jint android_content_AssetManager_loadThemeAttributeValue( - JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - const ResTable& res(theme->getResTable()); - - Res_value value; - // XXX value could be different in different configs! - uint32_t typeSpecFlags = 0; - ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags); - uint32_t ref = 0; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } + size_t idx = 0; + for (const ResTable_config& configuration : configurations) { + jobject java_configuration = ConstructConfigurationObject(env, configuration); + if (java_configuration == nullptr) { + return nullptr; } - return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block; -} -static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz, - jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - return theme->getChangingConfigurations(); + env->SetObjectArrayElement(array, idx++, java_configuration); + env->DeleteLocalRef(java_configuration); + } + return array; } -static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz, - jlong themeHandle, jint pri, - jstring tag, jstring prefix) -{ - ResTable::Theme* theme = reinterpret_cast(themeHandle); - const ResTable& res(theme->getResTable()); - (void)res; - - // XXX Need to use params. - theme->dumpToLog(); +static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr, + jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); + uint32_t* out_values = reinterpret_cast(out_values_ptr); + uint32_t* out_indices = reinterpret_cast(out_indices_ptr); + + jsize attrs_len = env->GetArrayLength(java_attrs); + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return; + } + + ApplyStyle(theme, xml_parser, static_cast(def_style_attr), + static_cast(def_style_resid), reinterpret_cast(attrs), attrs_len, + out_values, out_indices); + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); } -static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz, - jlong themeToken, - jint defStyleAttr, - jint defStyleRes, - jintArray inValues, - jintArray attrs, - jintArray outValues, - jintArray outIndices) -{ - if (themeToken == 0) { - jniThrowNullPointerException(env, "theme token"); - return JNI_FALSE; - } - if (attrs == NULL) { - jniThrowNullPointerException(env, "attrs"); - return JNI_FALSE; - } - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - const jsize NI = env->GetArrayLength(attrs); - const jsize NV = env->GetArrayLength(outValues); - if (NV < (NI*STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); - return JNI_FALSE; - } - - jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); - if (src == NULL) { +static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint def_style_attr, jint def_style_resid, jintArray java_values, + jintArray java_attrs, jintArray out_java_values, + jintArray out_java_indices) { + const jsize attrs_len = env->GetArrayLength(java_attrs); + const jsize out_values_len = env->GetArrayLength(out_java_values); + if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); + return JNI_FALSE; + } + + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return JNI_FALSE; + } + + jint* values = nullptr; + jsize values_len = 0; + if (java_values != nullptr) { + values_len = env->GetArrayLength(java_values); + values = reinterpret_cast(env->GetPrimitiveArrayCritical(java_values, nullptr)); + if (values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return JNI_FALSE; + } + } + + jint* out_values = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); + if (out_values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + return JNI_FALSE; + } + + jint* out_indices = nullptr; + if (out_java_indices != nullptr) { + jsize out_indices_len = env->GetArrayLength(out_java_indices); + if (out_indices_len > attrs_len) { + out_indices = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); + if (out_indices == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); return JNI_FALSE; - } - - jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0); - const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues); + } + } + } + + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + bool result = ResolveAttrs( + theme, static_cast(def_style_attr), static_cast(def_style_resid), + reinterpret_cast(values), values_len, reinterpret_cast(attrs), + attrs_len, reinterpret_cast(out_values), reinterpret_cast(out_indices)); + if (out_indices != nullptr) { + env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); + } + + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return result ? JNI_TRUE : JNI_FALSE; +} - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - if (baseDest == NULL) { - env->ReleasePrimitiveArrayCritical(attrs, src, 0); +static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jlong xml_parser_ptr, jintArray java_attrs, + jintArray out_java_values, jintArray out_java_indices) { + const jsize attrs_len = env->GetArrayLength(java_attrs); + const jsize out_values_len = env->GetArrayLength(out_java_values); + if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); + return JNI_FALSE; + } + + jint* attrs = reinterpret_cast(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return JNI_FALSE; + } + + jint* out_values = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); + if (out_values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return JNI_FALSE; + } + + jint* out_indices = nullptr; + if (out_java_indices != nullptr) { + jsize out_indices_len = env->GetArrayLength(out_java_indices); + if (out_indices_len > attrs_len) { + out_indices = + reinterpret_cast(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); + if (out_indices == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); return JNI_FALSE; + } } + } - jint* indices = NULL; - if (outIndices != NULL) { - if (env->GetArrayLength(outIndices) > NI) { - indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); - } - } + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + ResXMLParser* xml_parser = reinterpret_cast(xml_parser_ptr); - ResTable::Theme* theme = reinterpret_cast(themeToken); - bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes, - (uint32_t*) srcValues, NSV, - (uint32_t*) src, NI, - (uint32_t*) baseDest, - (uint32_t*) indices); + bool result = RetrieveAttributes(assetmanager.get(), xml_parser, + reinterpret_cast(attrs), attrs_len, + reinterpret_cast(out_values), + reinterpret_cast(out_indices)); - if (indices != NULL) { - env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); - } - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0); - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return result ? JNI_TRUE : JNI_FALSE; + if (out_indices != nullptr) { + env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); + } + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return result ? JNI_TRUE : JNI_FALSE; } -static void android_content_AssetManager_applyStyle(JNIEnv* env, jobject, jlong themeToken, - jint defStyleAttr, jint defStyleRes, jlong xmlParserToken, jintArray attrsObj, jint length, - jlong outValuesAddress, jlong outIndicesAddress) { - jint* attrs = env->GetIntArrayElements(attrsObj, 0); - ResTable::Theme* theme = reinterpret_cast(themeToken); - ResXMLParser* xmlParser = reinterpret_cast(xmlParserToken); - uint32_t* outValues = reinterpret_cast(static_cast(outValuesAddress)); - uint32_t* outIndices = reinterpret_cast(static_cast(outIndicesAddress)); - ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes, - reinterpret_cast(attrs), length, outValues, outIndices); - env->ReleaseIntArrayElements(attrsObj, attrs, JNI_ABORT); +static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + return reinterpret_cast(assetmanager->NewTheme().release()); } -static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz, - jlong xmlParserToken, - jintArray attrs, - jintArray outValues, - jintArray outIndices) -{ - if (xmlParserToken == 0) { - jniThrowNullPointerException(env, "xmlParserToken"); - return JNI_FALSE; - } - if (attrs == NULL) { - jniThrowNullPointerException(env, "attrs"); - return JNI_FALSE; - } - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_FALSE; - } - const ResTable& res(am->getResources()); - ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken; - - const jsize NI = env->GetArrayLength(attrs); - const jsize NV = env->GetArrayLength(outValues); - if (NV < (NI*STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); - return JNI_FALSE; - } - - jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); - if (src == NULL) { - return JNI_FALSE; - } - - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - if (baseDest == NULL) { - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return JNI_FALSE; - } - - jint* indices = NULL; - if (outIndices != NULL) { - if (env->GetArrayLength(outIndices) > NI) { - indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); - } - } - - bool result = RetrieveAttributes(&res, xmlParser, - (uint32_t*) src, NI, - (uint32_t*) baseDest, - (uint32_t*) indices); - - if (indices != NULL) { - env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); - } - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return result ? JNI_TRUE : JNI_FALSE; +static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { + delete reinterpret_cast(theme_ptr); } -static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz, - jint id) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - res.lock(); - const ResTable::bag_entry* defStyleEnt = NULL; - ssize_t bagOff = res.getBagLocked(id, &defStyleEnt); - res.unlock(); - - return static_cast(bagOff); +static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint resid, jboolean force) { + // AssetManager is accessed via the theme, so grab an explicit lock here. + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + theme->ApplyStyle(static_cast(resid), force); + + // TODO(adamlesinski): Consider surfacing exception when result is failure. + // CTS currently expects no exceptions from this method. + // std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid); + // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str()); } -static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz, - jint id, - jintArray outValues) -{ - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_FALSE; - } - const ResTable& res(am->getResources()); - ResTable_config config; - Res_value value; - ssize_t block; - - const jsize NV = env->GetArrayLength(outValues); - - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - jint* dest = baseDest; - if (dest == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", ""); - return JNI_FALSE; - } - - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - - const ResTable::bag_entry* arrayEnt = NULL; - uint32_t arrayTypeSetFlags = 0; - ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags); - const ResTable::bag_entry* endArrayEnt = arrayEnt + - (bagOff >= 0 ? bagOff : 0); - - int i = 0; - uint32_t typeSetFlags; - while (i < NV && arrayEnt < endArrayEnt) { - block = arrayEnt->stringBlock; - typeSetFlags = arrayTypeSetFlags; - config.density = 0; - value = arrayEnt->map.value; - - uint32_t resid = 0; - if (value.dataType != Res_value::TYPE_NULL) { - // Take care of resolving the found resource to its final value. - //printf("Resolving attribute reference\n"); - ssize_t newBlock = res.resolveReference(&value, block, &resid, - &typeSetFlags, &config); - if (kThrowOnBadId) { - if (newBlock == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return JNI_FALSE; - } - } - if (newBlock >= 0) block = newBlock; - } - - // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; - } - - //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data); - - // Write the final value back to Java. - dest[STYLE_TYPE] = value.dataType; - dest[STYLE_DATA] = value.data; - dest[STYLE_ASSET_COOKIE] = reinterpret_cast(res.getTableCookie(block)); - dest[STYLE_RESOURCE_ID] = resid; - dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags; - dest[STYLE_DENSITY] = config.density; - dest += STYLE_NUM_ENTRIES; - i+= STYLE_NUM_ENTRIES; - arrayEnt++; - } - - i /= STYLE_NUM_ENTRIES; - - res.unlock(); - - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - - return i; +static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr, + jlong src_theme_ptr) { + Theme* dst_theme = reinterpret_cast(dst_theme_ptr); + Theme* src_theme = reinterpret_cast(src_theme_ptr); + if (!dst_theme->SetTo(*src_theme)) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "Themes are from different AssetManagers"); + } } -static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return 0; - } - - int32_t assetCookie = static_cast(cookie); - Asset* a = assetCookie - ? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER) - : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return 0; - } - - const DynamicRefTable* dynamicRefTable = - am->getResources().getDynamicRefTableForCookie(assetCookie); - ResXMLTree* block = new ResXMLTree(dynamicRefTable); - status_t err = block->setTo(a->getBuffer(true), a->getLength(), true); - a->close(); - delete a; - - if (err != NO_ERROR) { - jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); - return 0; - } - - return reinterpret_cast(block); +static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { + reinterpret_cast(theme_ptr)->Clear(); } -static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N * 2); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - for (size_t i = 0, j = 0; ((ssize_t)i)map.value; - - // Take care of resolving the found resource to its final value. - stringBlock = res.resolveReference(&value, bag->stringBlock, NULL); - if (value.dataType == Res_value::TYPE_STRING) { - stringIndex = value.data; - } - - if (kThrowOnBadId) { - if (stringBlock == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - - //todo: It might be faster to allocate a C array to contain - // the blocknums and indices, put them in there and then - // do just one SetIntArrayRegion() - env->SetIntArrayRegion(array, j, 1, &stringBlock); - env->SetIntArrayRegion(array, j + 1, 1, &stringIndex); - j = j + 2; - } - res.unlockBag(startOfBag); - return array; +static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint resid, jobject typed_value, + jboolean resolve_references) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie = theme->GetAttribute(static_cast(resid), &value, &flags); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t ref = 0u; + if (resolve_references) { + ResTable_config selected_config; + cookie = + theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + } + return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value); } -static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL); - if (env->ExceptionCheck()) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - size_t strLen = 0; - for (size_t i=0; ((ssize_t)i)map.value; - jstring str = NULL; - - // Take care of resolving the found resource to its final value. - ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - if (value.dataType == Res_value::TYPE_STRING) { - const ResStringPool* pool = res.getTableStringBlock(block); - const char* str8 = pool->string8At(value.data, &strLen); - if (str8 != NULL) { - str = env->NewStringUTF(str8); - } else { - const char16_t* str16 = pool->stringAt(value.data, &strLen); - str = env->NewString(reinterpret_cast(str16), - strLen); - } - - // If one of our NewString{UTF} calls failed due to memory, an - // exception will be pending. - if (env->ExceptionCheck()) { - res.unlockBag(startOfBag); - return NULL; - } - - env->SetObjectArrayElement(array, i, str); - - // str is not NULL at that point, otherwise ExceptionCheck would have been true. - // If we have a large amount of strings in our array, we might - // overflow the local reference table of the VM. - env->DeleteLocalRef(str); - } - } - res.unlockBag(startOfBag); - return array; +static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint priority, jstring tag, jstring prefix) { + ScopedLock assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + (void) theme; + (void) priority; + (void) tag; + (void) prefix; } -static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - for (size_t i=0; ((ssize_t)i)map.value; - - // Take care of resolving the found resource to its final value. - ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - if (value.dataType >= Res_value::TYPE_FIRST_INT - && value.dataType <= Res_value::TYPE_LAST_INT) { - int intVal = value.data; - env->SetIntArrayRegion(array, i, 1, &intVal); - } - } - res.unlockBag(startOfBag); - return array; +static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/, + jlong theme_ptr) { + Theme* theme = reinterpret_cast(theme_ptr); + return static_cast(theme->GetChangingConfigurations()); } -static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz, - jint styleId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(styleId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } +static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + delete reinterpret_cast(asset_ptr); +} - const ResTable::bag_entry* bag = startOfBag; - for (size_t i=0; ((ssize_t)i)map.name.ident; - env->SetIntArrayRegion(array, i, 1, &resourceId); - } - res.unlockBag(startOfBag); - return array; +static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + uint8_t b; + ssize_t res = asset->read(&b, sizeof(b)); + return res == sizeof(b) ? static_cast(b) : -1; } -static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem) -{ - if (isSystem) { - verifySystemIdmaps(); - } - AssetManager* am = new AssetManager(); - if (am == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", ""); - return; - } +static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer, + jint offset, jint len) { + if (len == 0) { + return 0; + } - am->addDefaultAssets(); + jsize buffer_len = env->GetArrayLength(java_buffer); + if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len || + offset > buffer_len - len) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); + return -1; + } - ALOGV("Created AssetManager %p for Java object %p\n", am, clazz); - env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast(am)); -} + ScopedByteArrayRW byte_array(env, java_buffer); + if (byte_array.get() == nullptr) { + return -1; + } -static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz) -{ - AssetManager* am = (AssetManager*) - (env->GetLongField(clazz, gAssetManagerOffsets.mObject)); - ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz); - if (am != NULL) { - delete am; - env->SetLongField(clazz, gAssetManagerOffsets.mObject, 0); - } + Asset* asset = reinterpret_cast(asset_ptr); + ssize_t res = asset->read(byte_array.get() + offset, len); + if (res < 0) { + jniThrowException(env, "java/io/IOException", ""); + return -1; + } + return res > 0 ? static_cast(res) : -1; } -static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz) -{ - return Asset::getGlobalCount(); +static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset, + jint whence) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->seek( + static_cast(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)))); } -static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz) -{ - String8 alloc = Asset::getAssetAllocations(); - if (alloc.length() <= 0) { - return NULL; - } - - jstring str = env->NewStringUTF(alloc.string()); - return str; +static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->getLength()); } -static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz) -{ - return AssetManager::getGlobalCount(); +static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast(asset_ptr); + return static_cast(asset->getRemainingLength()); } // ---------------------------------------------------------------------------- -/* - * JNI registration. - */ +// JNI registration. static const JNINativeMethod gAssetManagerMethods[] = { - /* name, signature, funcPtr */ - - // Basic asset stuff. - { "openAsset", "(Ljava/lang/String;I)J", - (void*) android_content_AssetManager_openAsset }, - { "openAssetFd", "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*) android_content_AssetManager_openAssetFd }, - { "openNonAssetNative", "(ILjava/lang/String;I)J", - (void*) android_content_AssetManager_openNonAssetNative }, - { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*) android_content_AssetManager_openNonAssetFdNative }, - { "list", "(Ljava/lang/String;)[Ljava/lang/String;", - (void*) android_content_AssetManager_list }, - { "destroyAsset", "(J)V", - (void*) android_content_AssetManager_destroyAsset }, - { "readAssetChar", "(J)I", - (void*) android_content_AssetManager_readAssetChar }, - { "readAsset", "(J[BII)I", - (void*) android_content_AssetManager_readAsset }, - { "seekAsset", "(JJI)J", - (void*) android_content_AssetManager_seekAsset }, - { "getAssetLength", "(J)J", - (void*) android_content_AssetManager_getAssetLength }, - { "getAssetRemainingLength", "(J)J", - (void*) android_content_AssetManager_getAssetRemainingLength }, - { "addAssetPathNative", "(Ljava/lang/String;Z)I", - (void*) android_content_AssetManager_addAssetPath }, - { "addAssetFdNative", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)I", - (void*) android_content_AssetManager_addAssetFd }, - { "addOverlayPathNative", "(Ljava/lang/String;)I", - (void*) android_content_AssetManager_addOverlayPath }, - { "isUpToDate", "()Z", - (void*) android_content_AssetManager_isUpToDate }, - - // Resources. - { "getLocales", "()[Ljava/lang/String;", - (void*) android_content_AssetManager_getLocales }, - { "getNonSystemLocales", "()[Ljava/lang/String;", - (void*) android_content_AssetManager_getNonSystemLocales }, - { "getSizeConfigurations", "()[Landroid/content/res/Configuration;", - (void*) android_content_AssetManager_getSizeConfigurations }, - { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIIII)V", - (void*) android_content_AssetManager_setConfiguration }, - { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", - (void*) android_content_AssetManager_getResourceIdentifier }, - { "getResourceName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceName }, - { "getResourcePackageName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourcePackageName }, - { "getResourceTypeName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceTypeName }, - { "getResourceEntryName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceEntryName }, - { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadResourceValue }, - { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadResourceBagValue }, - { "getStringBlockCount","()I", - (void*) android_content_AssetManager_getStringBlockCount }, - { "getNativeStringBlock","(I)J", - (void*) android_content_AssetManager_getNativeStringBlock }, - { "getCookieName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getCookieName }, - { "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;", - (void*) android_content_AssetManager_getAssignedPackageIdentifiers }, - - // Themes. - { "newTheme", "()J", - (void*) android_content_AssetManager_newTheme }, - { "deleteTheme", "(J)V", - (void*) android_content_AssetManager_deleteTheme }, - { "applyThemeStyle", "(JIZ)V", - (void*) android_content_AssetManager_applyThemeStyle }, - { "copyTheme", "(JJ)V", - (void*) android_content_AssetManager_copyTheme }, - { "clearTheme", "(J)V", - (void*) android_content_AssetManager_clearTheme }, - { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadThemeAttributeValue }, - { "getThemeChangingConfigurations", "(J)I", - (void*) android_content_AssetManager_getThemeChangingConfigurations }, - { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V", - (void*) android_content_AssetManager_dumpTheme }, - { "applyStyle","(JIIJ[IIJJ)V", - (void*) android_content_AssetManager_applyStyle }, - { "resolveAttrs","(JII[I[I[I[I)Z", - (void*) android_content_AssetManager_resolveAttrs }, - { "retrieveAttributes","(J[I[I[I)Z", - (void*) android_content_AssetManager_retrieveAttributes }, - { "getArraySize","(I)I", - (void*) android_content_AssetManager_getArraySize }, - { "retrieveArray","(I[I)I", - (void*) android_content_AssetManager_retrieveArray }, - - // XML files. - { "openXmlAssetNative", "(ILjava/lang/String;)J", - (void*) android_content_AssetManager_openXmlAssetNative }, - - // Arrays. - { "getArrayStringResource","(I)[Ljava/lang/String;", - (void*) android_content_AssetManager_getArrayStringResource }, - { "getArrayStringInfo","(I)[I", - (void*) android_content_AssetManager_getArrayStringInfo }, - { "getArrayIntResource","(I)[I", - (void*) android_content_AssetManager_getArrayIntResource }, - { "getStyleAttributes","(I)[I", - (void*) android_content_AssetManager_getStyleAttributes }, - - // Bookkeeping. - { "init", "(Z)V", - (void*) android_content_AssetManager_init }, - { "destroy", "()V", - (void*) android_content_AssetManager_destroy }, - { "getGlobalAssetCount", "()I", - (void*) android_content_AssetManager_getGlobalAssetCount }, - { "getAssetAllocations", "()Ljava/lang/String;", - (void*) android_content_AssetManager_getAssetAllocations }, - { "getGlobalAssetManagerCount", "()I", - (void*) android_content_AssetManager_getGlobalAssetManagerCount }, + // AssetManager setup methods. + {"nativeCreate", "()J", (void*)NativeCreate}, + {"nativeDestroy", "(J)V", (void*)NativeDestroy}, + {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets}, + {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V", + (void*)NativeSetConfiguration}, + {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;", + (void*)NativeGetAssignedPackageIdentifiers}, + + // AssetManager file methods. + {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList}, + {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset}, + {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*)NativeOpenAssetFd}, + {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset}, + {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*)NativeOpenNonAssetFd}, + {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset}, + + // AssetManager resource methods. + {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue}, + {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I", + (void*)NativeGetResourceBagValue}, + {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes}, + {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;", + (void*)NativeGetResourceStringArray}, + {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo}, + {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray}, + {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize}, + {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray}, + + // AssetManager resource name/ID methods. + {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + (void*)NativeGetResourceIdentifier}, + {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName}, + {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName}, + {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName}, + {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName}, + {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales}, + {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;", + (void*)NativeGetSizeConfigurations}, + + // Style attribute related methods. + {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle}, + {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs}, + {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes}, + + // Theme related methods. + {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate}, + {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy}, + {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle}, + {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy}, + {"nativeThemeClear", "(J)V", (void*)NativeThemeClear}, + {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I", + (void*)NativeThemeGetAttributeValue}, + {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump}, + {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations}, + + // AssetInputStream methods. + {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy}, + {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar}, + {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead}, + {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek}, + {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength}, + {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength}, + + // System/idmap related methods. + {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps}, + + // Global management/debug methods. + {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount}, + {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations}, + {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount}, }; -int register_android_content_AssetManager(JNIEnv* env) -{ - jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); - gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); - gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); - gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string", - "Ljava/lang/CharSequence;"); - gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); - gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); - gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue, - "changingConfigurations", "I"); - gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); - - jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); - gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd", - "Landroid/os/ParcelFileDescriptor;"); - gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); - gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); - - jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); - gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); - - jclass stringClass = FindClassOrDie(env, "java/lang/String"); - g_stringClass = MakeGlobalRefOrDie(env, stringClass); - - jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); - gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); - gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, - "", "()V"); - gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", - "(ILjava/lang/Object;)V"); - - jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); - gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); - gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, - "", "()V"); - gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, - "smallestScreenWidthDp", "I"); - gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, - "screenWidthDp", "I"); - gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass, - "screenHeightDp", "I"); - - return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, - NELEM(gAssetManagerMethods)); +int register_android_content_AssetManager(JNIEnv* env) { + jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets"); + gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J"); + + jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); + gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); + gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); + gTypedValueOffsets.mString = + GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;"); + gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); + gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); + gTypedValueOffsets.mChangingConfigurations = + GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I"); + gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); + + jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); + gAssetFileDescriptorOffsets.mFd = + GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;"); + gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); + gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); + + jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); + gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); + + jclass stringClass = FindClassOrDie(env, "java/lang/String"); + g_stringClass = MakeGlobalRefOrDie(env, stringClass); + + jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); + gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); + gSparseArrayOffsets.constructor = + GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "", "()V"); + gSparseArrayOffsets.put = + GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V"); + + jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); + gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); + gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "", "()V"); + gConfigurationOffsets.mSmallestScreenWidthDpOffset = + GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I"); + gConfigurationOffsets.mScreenWidthDpOffset = + GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I"); + gConfigurationOffsets.mScreenHeightDpOffset = + GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I"); + + return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, + NELEM(gAssetManagerMethods)); } }; // namespace android diff --git a/core/jni/include/android_runtime/android_util_AssetManager.h b/core/jni/include/android_runtime/android_util_AssetManager.h index 8dd933707a6a..2c1e3579eb92 100644 --- a/core/jni/include/android_runtime/android_util_AssetManager.h +++ b/core/jni/include/android_runtime/android_util_AssetManager.h @@ -14,17 +14,20 @@ * limitations under the License. */ -#ifndef android_util_AssetManager_H -#define android_util_AssetManager_H +#ifndef ANDROID_RUNTIME_ASSETMANAGER_H +#define ANDROID_RUNTIME_ASSETMANAGER_H -#include +#include "androidfw/AssetManager2.h" +#include "androidfw/MutexGuard.h" #include "jni.h" namespace android { -extern AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject assetMgr); +extern AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); +extern Guarded* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); +extern Guarded* AssetManagerForNdkAssetManager(AAssetManager* assetmanager); -} +} // namespace android -#endif +#endif // ANDROID_RUNTIME_ASSETMANAGER_H diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 251b2e773cfb..70d52164ff74 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -145,6 +145,7 @@ cc_test { "tests/TypeWrappers_test.cpp", "tests/ZipUtils_test.cpp", ], + static_libs: ["libgmock"], target: { android: { srcs: [ @@ -171,6 +172,7 @@ cc_benchmark { // Actual benchmarks. "tests/AssetManager2_bench.cpp", + "tests/AttributeResolution_bench.cpp", "tests/SparseEntry_bench.cpp", "tests/Theme_bench.cpp", ], diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index da0205d72125..8f58f74d4652 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define ATRACE_TAG ATRACE_TAG_RESOURCES - #include "androidfw/ApkAssets.h" #include @@ -27,7 +25,6 @@ #include "android-base/utf8.h" #include "utils/Compat.h" #include "utils/FileMap.h" -#include "utils/Trace.h" #include "ziparchive/zip_archive.h" #include "androidfw/Asset.h" @@ -105,8 +102,6 @@ std::unique_ptr ApkAssets::CreateAssetFromFile(const std::string& path) { std::unique_ptr ApkAssets::LoadImpl( unique_fd fd, const std::string& path, std::unique_ptr idmap_asset, std::unique_ptr loaded_idmap, bool system, bool load_as_shared_library) { - ATRACE_CALL(); - ::ZipArchiveHandle unmanaged_handle; int32_t result; if (fd >= 0) { @@ -163,7 +158,6 @@ std::unique_ptr ApkAssets::LoadImpl( } std::unique_ptr ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const { - ATRACE_CALL(); CHECK(zip_handle_ != nullptr); ::ZipString name(path.c_str()); @@ -231,12 +225,16 @@ bool ApkAssets::ForEachFile(const std::string& root_path, while ((result = ::Next(cookie, &entry, &name)) == 0) { StringPiece full_file_path(reinterpret_cast(name.name), name.name_length); StringPiece leaf_file_path = full_file_path.substr(root_path_full.size()); - auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/'); - if (iter != leaf_file_path.end()) { - dirs.insert( - leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string()); - } else if (!leaf_file_path.empty()) { - f(leaf_file_path, kFileTypeRegular); + + if (!leaf_file_path.empty()) { + auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/'); + if (iter != leaf_file_path.end()) { + std::string dir = + leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string(); + dirs.insert(std::move(dir)); + } else { + f(leaf_file_path, kFileTypeRegular); + } } } ::EndIteration(cookie); diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 415d3e36adf9..d9f1293183b7 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -36,6 +36,31 @@ namespace android { +struct FindEntryResult { + // A pointer to the resource table entry for this resource. + // If the size of the entry is > sizeof(ResTable_entry), it can be cast to + // a ResTable_map_entry and processed as a bag/map. + const ResTable_entry* entry; + + // The configuration for which the resulting entry was defined. This is already swapped to host + // endianness. + ResTable_config config; + + // The bitmask of configuration axis with which the resource value varies. + uint32_t type_flags; + + // The dynamic package ID map for the package from which this resource came from. + const DynamicRefTable* dynamic_ref_table; + + // The string pool reference to the type's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef type_string_ref; + + // The string pool reference to the entry's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef entry_string_ref; +}; + AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } @@ -44,6 +69,7 @@ bool AssetManager2::SetApkAssets(const std::vector& apk_assets bool invalidate_caches) { apk_assets_ = apk_assets; BuildDynamicRefTable(); + RebuildFilterList(); if (invalidate_caches) { InvalidateCaches(static_cast(-1)); } @@ -74,12 +100,14 @@ void AssetManager2::BuildDynamicRefTable() { if (idx == 0xff) { package_ids_[package_id] = idx = static_cast(package_groups_.size()); package_groups_.push_back({}); - package_groups_.back().dynamic_ref_table.mAssignedPackageId = package_id; + DynamicRefTable& ref_table = package_groups_.back().dynamic_ref_table; + ref_table.mAssignedPackageId = package_id; + ref_table.mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f; } PackageGroup* package_group = &package_groups_[idx]; // Add the package and to the set of packages with the same ID. - package_group->packages_.push_back(package.get()); + package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); package_group->cookies_.push_back(static_cast(i)); // Add the package name -> build time ID mappings. @@ -94,7 +122,7 @@ void AssetManager2::BuildDynamicRefTable() { // Now assign the runtime IDs so that we have a build-time to runtime ID map. const auto package_groups_end = package_groups_.end(); for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) { - const std::string& package_name = iter->packages_[0]->GetPackageName(); + const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName(); for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) { iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()), iter->dynamic_ref_table.mAssignedPackageId); @@ -105,20 +133,33 @@ void AssetManager2::BuildDynamicRefTable() { void AssetManager2::DumpToLog() const { base::ScopedLogSeverity _log(base::INFO); + LOG(INFO) << base::StringPrintf("AssetManager2(this=%p)", this); + std::string list; + for (const auto& apk_assets : apk_assets_) { + base::StringAppendF(&list, "%s,", apk_assets->GetPath().c_str()); + } + LOG(INFO) << "ApkAssets: " << list; + + list = ""; for (size_t i = 0; i < package_ids_.size(); i++) { if (package_ids_[i] != 0xff) { - base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]); + base::StringAppendF(&list, "%02x -> %d, ", (int)i, package_ids_[i]); } } LOG(INFO) << "Package ID map: " << list; for (const auto& package_group: package_groups_) { - list = ""; - for (const auto& package : package_group.packages_) { - base::StringAppendF(&list, "%s(%02x), ", package->GetPackageName().c_str(), package->GetPackageId()); - } - LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list; + list = ""; + for (const auto& package : package_group.packages_) { + const LoadedPackage* loaded_package = package.loaded_package_; + base::StringAppendF(&list, "%s(%02x%s), ", loaded_package->GetPackageName().c_str(), + loaded_package->GetPackageId(), + (loaded_package->IsDynamic() ? " dynamic" : "")); + } + LOG(INFO) << base::StringPrintf("PG (%02x): ", + package_group.dynamic_ref_table.mAssignedPackageId) + << list; } } @@ -157,53 +198,55 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { configuration_ = configuration; if (diff) { + RebuildFilterList(); InvalidateCaches(static_cast(diff)); } } std::set AssetManager2::GetResourceConfigurations(bool exclude_system, - bool exclude_mipmap) { - ATRACE_CALL(); + bool exclude_mipmap) const { + ATRACE_NAME("AssetManager::GetResourceConfigurations"); std::set configurations; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { - if (exclude_system && package->IsSystem()) { + for (const ConfiguredPackage& package : package_group.packages_) { + if (exclude_system && package.loaded_package_->IsSystem()) { continue; } - package->CollectConfigurations(exclude_mipmap, &configurations); + package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations); } } return configurations; } std::set AssetManager2::GetResourceLocales(bool exclude_system, - bool merge_equivalent_languages) { - ATRACE_CALL(); + bool merge_equivalent_languages) const { + ATRACE_NAME("AssetManager::GetResourceLocales"); std::set locales; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { - if (exclude_system && package->IsSystem()) { + for (const ConfiguredPackage& package : package_group.packages_) { + if (exclude_system && package.loaded_package_->IsSystem()) { continue; } - package->CollectLocales(merge_equivalent_languages, &locales); + package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales); } } return locales; } -std::unique_ptr AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) { +std::unique_ptr AssetManager2::Open(const std::string& filename, + Asset::AccessMode mode) const { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, mode); } std::unique_ptr AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) { + Asset::AccessMode mode) const { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, cookie, mode); } -std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) { - ATRACE_CALL(); +std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) const { + ATRACE_NAME("AssetManager::OpenDir"); std::string full_path = "assets/" + dirname; std::unique_ptr> files = @@ -236,8 +279,7 @@ std::unique_ptr AssetManager2::OpenDir(const std::string& dirname) { // is inconsistent for split APKs. std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie) { - ATRACE_CALL(); + ApkAssetsCookie* out_cookie) const { for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) { std::unique_ptr asset = apk_assets_[i]->Open(filename, mode); if (asset) { @@ -255,8 +297,8 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, - ApkAssetsCookie cookie, Asset::AccessMode mode) { - ATRACE_CALL(); + ApkAssetsCookie cookie, + Asset::AccessMode mode) const { if (cookie < 0 || static_cast(cookie) >= apk_assets_.size()) { return {}; } @@ -264,14 +306,13 @@ std::unique_ptr AssetManager2::OpenNonAsset(const std::string& filename, } ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, - bool stop_at_first_match, FindEntryResult* out_entry) { - ATRACE_CALL(); - + bool /*stop_at_first_match*/, + FindEntryResult* out_entry) const { // Might use this if density_override != 0. ResTable_config density_override_config; // Select our configuration or generate a density override configuration. - ResTable_config* desired_config = &configuration_; + const ResTable_config* desired_config = &configuration_; if (density_override != 0 && density_override != configuration_.density) { density_override_config = configuration_; density_override_config.density = density_override; @@ -285,55 +326,135 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri const uint32_t package_id = get_package_id(resid); const uint8_t type_idx = get_type_id(resid) - 1; - const uint16_t entry_id = get_entry_id(resid); + const uint16_t entry_idx = get_entry_id(resid); - const uint8_t idx = package_ids_[package_id]; - if (idx == 0xff) { + const uint8_t package_idx = package_ids_[package_id]; + if (package_idx == 0xff) { LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); return kInvalidCookie; } - FindEntryResult best_entry; - ApkAssetsCookie best_cookie = kInvalidCookie; - uint32_t cumulated_flags = 0u; - - const PackageGroup& package_group = package_groups_[idx]; + const PackageGroup& package_group = package_groups_[package_idx]; const size_t package_count = package_group.packages_.size(); - FindEntryResult current_entry; - for (size_t i = 0; i < package_count; i++) { - const LoadedPackage* loaded_package = package_group.packages_[i]; - if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry)) { + + ApkAssetsCookie best_cookie = kInvalidCookie; + const LoadedPackage* best_package = nullptr; + const ResTable_type* best_type = nullptr; + const ResTable_config* best_config = nullptr; + ResTable_config best_config_copy; + uint32_t best_offset = 0u; + uint32_t type_flags = 0u; + + // If desired_config is the same as the set configuration, then we can use our filtered list + // and we don't need to match the configurations, since they already matched. + const bool use_fast_path = desired_config == &configuration_; + + for (size_t pi = 0; pi < package_count; pi++) { + const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; + const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_; + ApkAssetsCookie cookie = package_group.cookies_[pi]; + + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx); + if (UNLIKELY(type_spec == nullptr)) { continue; } - cumulated_flags |= current_entry.type_flags; + uint16_t local_entry_idx = entry_idx; - const ResTable_config* current_config = current_entry.config; - const ResTable_config* best_config = best_entry.config; - if (best_cookie == kInvalidCookie || - current_config->isBetterThan(*best_config, desired_config) || - (loaded_package->IsOverlay() && current_config->compare(*best_config) == 0)) { - best_entry = current_entry; - best_cookie = package_group.cookies_[i]; - if (stop_at_first_match) { - break; + // If there is an IDMAP supplied with this package, translate the entry ID. + if (type_spec->idmap_entries != nullptr) { + if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) { + // There is no mapping, so the resource is not meant to be in this overlay package. + continue; } } + + type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx); + + // If the package is an overlay, then even configurations that are the same MUST be chosen. + const bool package_is_overlay = loaded_package->IsOverlay(); + + const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; + if (use_fast_path) { + const std::vector& candidate_configs = filtered_group.configurations; + const size_t type_count = candidate_configs.size(); + for (uint32_t i = 0; i < type_count; i++) { + const ResTable_config& this_config = candidate_configs[i]; + + // We can skip calling ResTable_config::match() because we know that all candidate + // configurations that do NOT match have been filtered-out. + if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || + (package_is_overlay && this_config.compare(*best_config) == 0)) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const ResTable_type* type_chunk = filtered_group.types[i]; + const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + best_cookie = cookie; + best_package = loaded_package; + best_type = type_chunk; + best_config = &this_config; + best_offset = offset; + } + } + } else { + // This is the slower path, which doesn't use the filtered list of configurations. + // Here we must read the ResTable_config from the mmapped APK, convert it to host endianness + // and fill in any new fields that did not exist when the APK was compiled. + // Furthermore when selecting configurations we can't just record the pointer to the + // ResTable_config, we must copy it. + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config this_config; + this_config.copyFromDtoH((*iter)->config); + + if (this_config.match(*desired_config)) { + if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || + (package_is_overlay && this_config.compare(*best_config) == 0)) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + best_cookie = cookie; + best_package = loaded_package; + best_type = *iter; + best_config_copy = this_config; + best_config = &best_config_copy; + best_offset = offset; + } + } + } + } + } + + if (UNLIKELY(best_cookie == kInvalidCookie)) { + return kInvalidCookie; } - if (best_cookie == kInvalidCookie) { + const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset); + if (UNLIKELY(best_entry == nullptr)) { return kInvalidCookie; } - *out_entry = best_entry; + out_entry->entry = best_entry; + out_entry->config = *best_config; + out_entry->type_flags = type_flags; + out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); + out_entry->entry_string_ref = + StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; - out_entry->type_flags = cumulated_flags; return best_cookie; } -bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { - ATRACE_CALL(); - +bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const { FindEntryResult entry; ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */, &entry); @@ -341,7 +462,8 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { return false; } - const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid); + const LoadedPackage* package = + apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); if (package == nullptr) { return false; } @@ -369,7 +491,7 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { return true; } -bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { +bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const { FindEntryResult entry; ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); @@ -383,9 +505,7 @@ bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) { - ATRACE_CALL(); - + uint32_t* out_flags) const { FindEntryResult entry; ApkAssetsCookie cookie = FindEntry(resid, density_override, false /* stop_at_first_match */, &entry); @@ -402,7 +522,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Create a reference since we can't represent this complex type as a Res_value. out_value->dataType = Res_value::TYPE_REFERENCE; out_value->data = resid; - *out_selected_config = *entry.config; + *out_selected_config = entry.config; *out_flags = entry.type_flags; return cookie; } @@ -414,7 +534,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Convert the package ID to the runtime assigned package ID. entry.dynamic_ref_table->lookupResourceValue(out_value); - *out_selected_config = *entry.config; + *out_selected_config = entry.config; *out_flags = entry.type_flags; return cookie; } @@ -422,16 +542,13 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) { - ATRACE_CALL(); + uint32_t* out_last_reference) const { constexpr const int kMaxIterations = 20; for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && in_out_value->data != 0u && iteration < kMaxIterations; iteration++) { - if (out_last_reference != nullptr) { - *out_last_reference = in_out_value->data; - } + *out_last_reference = in_out_value->data; uint32_t new_flags = 0u; cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/, in_out_value, in_out_selected_config, &new_flags); @@ -450,7 +567,7 @@ ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_valu } const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { - ATRACE_CALL(); + ATRACE_NAME("AssetManager::GetBag"); auto cached_iter = cached_bags_.find(resid); if (cached_iter != cached_bags_.end()) { @@ -492,7 +609,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { // Attributes, arrays, etc don't have a resource id as the name. They specify // other data, which would be wrong to change via a lookup. if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, + resid); return nullptr; } } @@ -524,7 +642,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { const ResolvedBag* parent_bag = GetBag(parent_resid); if (parent_bag == nullptr) { // Failed to get the parent that should exist. - LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid); + LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, + resid); return nullptr; } @@ -543,7 +662,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t child_key = dtohl(map_entry->name.ident); if (!is_internal_resid(child_key)) { if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, + resid); return nullptr; } } @@ -582,7 +702,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t new_key = dtohl(map_entry->name.ident); if (!is_internal_resid(new_key)) { if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, + resid); return nullptr; } } @@ -638,7 +759,7 @@ static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) { uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const std::string& fallback_type, - const std::string& fallback_package) { + const std::string& fallback_package) const { StringPiece package_name, type, entry; if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) { return 0u; @@ -670,7 +791,8 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const static std::u16string kAttrPrivate16 = u"^attr-private"; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { + for (const ConfiguredPackage& package_impl : package_group.packages_) { + const LoadedPackage* package = package_impl.loaded_package_; if (package_name != package->GetPackageName()) { // All packages in the same group are expected to have the same package name. break; @@ -692,6 +814,32 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, return 0u; } +void AssetManager2::RebuildFilterList() { + for (PackageGroup& group : package_groups_) { + for (ConfiguredPackage& impl : group.packages_) { + // Destroy it. + impl.filtered_configs_.~ByteBucketArray(); + + // Re-create it. + new (&impl.filtered_configs_) ByteBucketArray(); + + // Create the filters here. + impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec* spec, uint8_t type_index) { + FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_index); + const auto iter_end = spec->types + spec->type_count; + for (auto iter = spec->types; iter != iter_end; ++iter) { + ResTable_config this_config; + this_config.copyFromDtoH((*iter)->config); + if (this_config.match(configuration_)) { + group.configurations.push_back(this_config); + group.types.push_back(*iter); + } + } + }); + } + } +} + void AssetManager2::InvalidateCaches(uint32_t diff) { if (diff == 0xffffffffu) { // Everything must go. @@ -743,7 +891,7 @@ struct Theme::Package { }; bool Theme::ApplyStyle(uint32_t resid, bool force) { - ATRACE_CALL(); + ATRACE_NAME("Theme::ApplyStyle"); const ResolvedBag* bag = asset_manager_->GetBag(resid); if (bag == nullptr) { @@ -872,7 +1020,7 @@ ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_type_spec_flags, - uint32_t* out_last_ref) { + uint32_t* out_last_ref) const { if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) { uint32_t new_flags; cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags); diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index 60e3845d98a9..f912af4f7190 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -20,13 +20,18 @@ #include +#include "androidfw/AssetManager2.h" #include "androidfw/AttributeFinder.h" -#include "androidfw/ResourceTypes.h" constexpr bool kDebugStyles = false; namespace android { +// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. +static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { + return cookie != kInvalidCookie ? static_cast(cookie + 1) : static_cast(-1); +} + class XmlAttributeFinder : public BackTrackingAttributeFinder { public: @@ -44,58 +49,53 @@ class XmlAttributeFinder }; class BagAttributeFinder - : public BackTrackingAttributeFinder { + : public BackTrackingAttributeFinder { public: - BagAttributeFinder(const ResTable::bag_entry* start, - const ResTable::bag_entry* end) - : BackTrackingAttributeFinder(start, end) {} + BagAttributeFinder(const ResolvedBag* bag) + : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr, + bag != nullptr ? bag->entries + bag->entry_count : nullptr) { + } - inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const { - return entry->map.name.ident; + inline uint32_t GetAttribute(const ResolvedBag::Entry* entry) const { + return entry->key; } }; -bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, - uint32_t def_style_res, uint32_t* src_values, - size_t src_values_length, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, - uint32_t* out_indices) { +bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, + uint32_t* src_values, size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr, def_style_res); } - const ResTable& res = theme->getResTable(); + AssetManager2* assetmanager = theme->GetAssetManager(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_bag_type_set_flags = 0; + uint32_t def_style_flags = 0u; if (def_style_attr != 0) { Res_value value; - if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) { + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { if (value.dataType == Res_value::TYPE_REFERENCE) { def_style_res = value.data; } } } - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - // Retrieve the default style bag, if requested. - const ResTable::bag_entry* def_style_start = nullptr; - uint32_t def_style_type_set_flags = 0; - ssize_t bag_off = def_style_res != 0 - ? res.getBagLocked(def_style_res, &def_style_start, - &def_style_type_set_flags) - : -1; - def_style_type_set_flags |= def_style_bag_type_set_flags; - const ResTable::bag_entry* const def_style_end = - def_style_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end); + const ResolvedBag* default_style_bag = nullptr; + if (def_style_res != 0) { + default_style_bag = assetmanager->GetBag(def_style_res); + if (default_style_bag != nullptr) { + def_style_flags |= default_style_bag->type_spec_flags; + } + } + + BagAttributeFinder def_style_attr_finder(default_style_bag); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -106,7 +106,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ssize_t block = -1; + ApkAssetsCookie cookie = kInvalidCookie; uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; @@ -122,15 +122,14 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, value.dataType = Res_value::TYPE_ATTRIBUTE; value.data = src_values[ii]; if (kDebugStyles) { - ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, - value.data); + ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data); } } else { - const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident); - if (def_style_entry != def_style_end) { - block = def_style_entry->stringBlock; - type_set_flags = def_style_type_set_flags; - value = def_style_entry->map.value; + const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident); + if (entry != def_style_attr_finder.end()) { + cookie = entry->cookie; + type_set_flags = def_style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -140,22 +139,26 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ssize_t new_block = - theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); - if (new_block >= 0) block = new_block; + ApkAssetsCookie new_cookie = + theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } if (kDebugStyles) { ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data); } } else if (value.data != Res_value::DATA_NULL_EMPTY) { - // If we still don't have a value for this attribute, try to find - // it in the theme! - ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); - if (new_block >= 0) { + // If we still don't have a value for this attribute, try to find it in the theme! + ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); + if (new_cookie != kInvalidCookie) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); - if (new_block >= 0) block = new_block; + new_cookie = + assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } if (kDebugStyles) { ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -169,7 +172,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = -1; + cookie = kInvalidCookie; } if (kDebugStyles) { @@ -179,9 +182,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != -1 ? static_cast(res.getTableCookie(block)) - : static_cast(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -195,90 +196,80 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, out_values += STYLE_NUM_ENTRIES; } - res.unlock(); - if (out_indices != nullptr) { out_indices[0] = indices_idx; } return true; } -void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { - ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", - theme, def_style_attr, def_style_res, xml_parser); + ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, + def_style_attr, def_style_resid, xml_parser); } - const ResTable& res = theme->getResTable(); + AssetManager2* assetmanager = theme->GetAssetManager(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_bag_type_set_flags = 0; + uint32_t def_style_flags = 0u; if (def_style_attr != 0) { Res_value value; - if (theme->getAttribute(def_style_attr, &value, - &def_style_bag_type_set_flags) >= 0) { + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { if (value.dataType == Res_value::TYPE_REFERENCE) { - def_style_res = value.data; + def_style_resid = value.data; } } } - // Retrieve the style class associated with the current XML tag. - int style = 0; - uint32_t style_bag_type_set_flags = 0; + // Retrieve the style resource ID associated with the current XML tag's style attribute. + uint32_t style_resid = 0u; + uint32_t style_flags = 0u; if (xml_parser != nullptr) { ssize_t idx = xml_parser->indexOfStyle(); if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) { if (value.dataType == value.TYPE_ATTRIBUTE) { - if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) { + // Resolve the attribute with out theme. + if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) { value.dataType = Res_value::TYPE_NULL; } } + if (value.dataType == value.TYPE_REFERENCE) { - style = value.data; + style_resid = value.data; } } } - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - // Retrieve the default style bag, if requested. - const ResTable::bag_entry* def_style_attr_start = nullptr; - uint32_t def_style_type_set_flags = 0; - ssize_t bag_off = def_style_res != 0 - ? res.getBagLocked(def_style_res, &def_style_attr_start, - &def_style_type_set_flags) - : -1; - def_style_type_set_flags |= def_style_bag_type_set_flags; - const ResTable::bag_entry* const def_style_attr_end = - def_style_attr_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder def_style_attr_finder(def_style_attr_start, - def_style_attr_end); + const ResolvedBag* default_style_bag = nullptr; + if (def_style_resid != 0) { + default_style_bag = assetmanager->GetBag(def_style_resid); + if (default_style_bag != nullptr) { + def_style_flags |= default_style_bag->type_spec_flags; + } + } + + BagAttributeFinder def_style_attr_finder(default_style_bag); // Retrieve the style class bag, if requested. - const ResTable::bag_entry* style_attr_start = nullptr; - uint32_t style_type_set_flags = 0; - bag_off = - style != 0 - ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags) - : -1; - style_type_set_flags |= style_bag_type_set_flags; - const ResTable::bag_entry* const style_attr_end = - style_attr_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end); + const ResolvedBag* xml_style_bag = nullptr; + if (style_resid != 0) { + xml_style_bag = assetmanager->GetBag(style_resid); + if (xml_style_bag != nullptr) { + style_flags |= xml_style_bag->type_spec_flags; + } + } + + BagAttributeFinder xml_style_attr_finder(xml_style_bag); // Retrieve the XML attributes, if requested. - static const ssize_t kXmlBlock = 0x10000000; XmlAttributeFinder xml_attr_finder(xml_parser); - const size_t xml_attr_end = - xml_parser != nullptr ? xml_parser->getAttributeCount() : 0; // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -289,8 +280,8 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ssize_t block = kXmlBlock; - uint32_t type_set_flags = 0; + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0u; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -302,7 +293,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s // Walk through the xml attributes looking for the requested attribute. const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); - if (xml_attr_idx != xml_attr_end) { + if (xml_attr_idx != xml_attr_finder.end()) { // We found the attribute we were looking for. xml_parser->getAttributeValue(xml_attr_idx, &value); if (kDebugStyles) { @@ -312,12 +303,12 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the style class values looking for the requested attribute. - const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident); - if (style_attr_entry != style_attr_end) { + const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident); + if (entry != xml_style_attr_finder.end()) { // We found the attribute we were looking for. - block = style_attr_entry->stringBlock; - type_set_flags = style_type_set_flags; - value = style_attr_entry->map.value; + cookie = entry->cookie; + type_set_flags = style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -326,25 +317,25 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the default style values looking for the requested attribute. - const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident); - if (def_style_attr_entry != def_style_attr_end) { + const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident); + if (entry != def_style_attr_finder.end()) { // We found the attribute we were looking for. - block = def_style_attr_entry->stringBlock; - type_set_flags = style_type_set_flags; - value = def_style_attr_entry->map.value; + cookie = entry->cookie; + type_set_flags = def_style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } } } - uint32_t resid = 0; + uint32_t resid = 0u; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ssize_t new_block = - theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); - if (new_block >= 0) { - block = new_block; + ApkAssetsCookie new_cookie = + theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; } if (kDebugStyles) { @@ -352,14 +343,15 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s } } else if (value.data != Res_value::DATA_NULL_EMPTY) { // If we still don't have a value for this attribute, try to find it in the theme! - ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); - if (new_block >= 0) { + ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); + if (new_cookie != kInvalidCookie) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); - if (new_block >= 0) { - block = new_block; + new_cookie = + assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; } if (kDebugStyles) { @@ -375,7 +367,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = kXmlBlock; + cookie = kInvalidCookie; } if (kDebugStyles) { @@ -385,9 +377,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != kXmlBlock ? static_cast(res.getTableCookie(block)) - : static_cast(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -402,36 +392,28 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s out_values += STYLE_NUM_ENTRIES; } - res.unlock(); - // out_indices must NOT be nullptr. out_indices[0] = indices_idx; } -bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, - uint32_t* attrs, size_t attrs_length, - uint32_t* out_values, uint32_t* out_indices) { +bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { ResTable_config config; Res_value value; int indices_idx = 0; - // Now lock down the resource object and start pulling stuff from it. - res->lock(); - // Retrieve the XML attributes, if requested. const size_t xml_attr_count = xml_parser->getAttributeCount(); size_t ix = 0; uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix); - static const ssize_t kXmlBlock = 0x10000000; - // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - ssize_t block = kXmlBlock; - uint32_t type_set_flags = 0; + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0u; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -450,28 +432,27 @@ bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, cur_xml_attr = xml_parser->getAttributeNameResID(ix); } - uint32_t resid = 0; + uint32_t resid = 0u; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - // printf("Resolving attribute reference\n"); - ssize_t new_block = res->resolveReference(&value, block, &resid, - &type_set_flags, &config); - if (new_block >= 0) block = new_block; + ApkAssetsCookie new_cookie = + assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } } // Deal with the special @null value -- it turns back to TYPE_NULL. if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = kXmlBlock; + cookie = kInvalidCookie; } // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != kXmlBlock ? static_cast(res->getTableCookie(block)) - : static_cast(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -485,8 +466,6 @@ bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, out_values += STYLE_NUM_ENTRIES; } - res->unlock(); - if (out_indices != nullptr) { out_indices[0] = indices_idx; } diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 28548e27baf0..04d506a2d71c 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -44,44 +44,6 @@ namespace android { constexpr const static int kAppPackageId = 0x7f; -// Element of a TypeSpec array. See TypeSpec. -struct Type { - // The configuration for which this type defines entries. - // This is already converted to host endianness. - ResTable_config configuration; - - // Pointer to the mmapped data where entry definitions are kept. - const ResTable_type* type; -}; - -// TypeSpec is going to be immediately proceeded by -// an array of Type structs, all in the same block of memory. -struct TypeSpec { - // Pointer to the mmapped data where flags are kept. - // Flags denote whether the resource entry is public - // and under which configurations it varies. - const ResTable_typeSpec* type_spec; - - // Pointer to the mmapped data where the IDMAP mappings for this type - // exist. May be nullptr if no IDMAP exists. - const IdmapEntry_header* idmap_entries; - - // The number of types that follow this struct. - // There is a type for each configuration - // that entries are defined for. - size_t type_count; - - // Trick to easily access a variable number of Type structs - // proceeding this struct, and to ensure their alignment. - const Type types[0]; -}; - -// TypeSpecPtr points to the block of memory that holds -// a TypeSpec struct, followed by an array of Type structs. -// TypeSpecPtr is a managed pointer that knows how to delete -// itself. -using TypeSpecPtr = util::unique_cptr; - namespace { // Builder that helps accumulate Type structs and then create a single @@ -95,21 +57,22 @@ class TypeSpecPtrBuilder { } void AddType(const ResTable_type* type) { - ResTable_config config; - config.copyFromDtoH(type->config); - types_.push_back(Type{config, type}); + types_.push_back(type); } TypeSpecPtr Build() { // Check for overflow. - if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) { + using ElementType = const ResTable_type*; + if ((std::numeric_limits::max() - sizeof(TypeSpec)) / sizeof(ElementType) < + types_.size()) { return {}; } - TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type))); + TypeSpec* type_spec = + (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType))); type_spec->type_spec = header_; type_spec->idmap_entries = idmap_header_; type_spec->type_count = types_.size(); - memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type)); + memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType)); return TypeSpecPtr(type_spec); } @@ -118,7 +81,7 @@ class TypeSpecPtrBuilder { const ResTable_typeSpec* header_; const IdmapEntry_header* idmap_header_; - std::vector types_; + std::vector types_; }; } // namespace @@ -162,18 +125,17 @@ static bool VerifyResTableType(const ResTable_type* header) { return true; } -static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset, - size_t entry_idx) { +static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) { // Check that the offset is aligned. if (entry_offset & 0x03) { - LOG(ERROR) << "Entry offset at index " << entry_idx << " is not 4-byte aligned."; + LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned."; return false; } // Check that the offset doesn't overflow. if (entry_offset > std::numeric_limits::max() - dtohl(type->entriesStart)) { // Overflow in offset. - LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large."; + LOG(ERROR) << "Entry at offset " << entry_offset << " is too large."; return false; } @@ -181,7 +143,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset entry_offset += dtohl(type->entriesStart); if (entry_offset > chunk_size - sizeof(ResTable_entry)) { - LOG(ERROR) << "Entry offset at index " << entry_idx + LOG(ERROR) << "Entry at offset " << entry_offset << " is too large. No room for ResTable_entry."; return false; } @@ -191,13 +153,13 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t entry_size = dtohs(entry->size); if (entry_size < sizeof(*entry)) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx + LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too small."; return false; } if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx + LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too large."; return false; } @@ -205,7 +167,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset if (entry_size < sizeof(ResTable_map_entry)) { // There needs to be room for one Res_value struct. if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) { - LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << entry_idx + LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset << " for type " << (int)type->id << "."; return false; } @@ -214,12 +176,12 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset reinterpret_cast(reinterpret_cast(entry) + entry_size); const size_t value_size = dtohs(value->size); if (value_size < sizeof(Res_value)) { - LOG(ERROR) << "Res_value at index " << entry_idx << " is too small."; + LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small."; return false; } if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) { - LOG(ERROR) << "Res_value size " << value_size << " at index " << entry_idx + LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset << " is too large."; return false; } @@ -228,119 +190,76 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t map_entry_count = dtohl(map->count); size_t map_entries_start = entry_offset + entry_size; if (map_entries_start & 0x03) { - LOG(ERROR) << "Map entries at index " << entry_idx << " start at unaligned offset."; + LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset."; return false; } // Each entry is sizeof(ResTable_map) big. if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) { - LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << entry_idx << "."; + LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << "."; return false; } } return true; } -bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_idx, - const ResTable_config& config, FindEntryResult* out_entry) const { - const ResTable_config* best_config = nullptr; - const ResTable_type* best_type = nullptr; - uint32_t best_offset = 0; - - for (uint32_t i = 0; i < type_spec_ptr->type_count; i++) { - const Type* type = &type_spec_ptr->types[i]; - const ResTable_type* type_chunk = type->type; - - if (type->configuration.match(config) && - (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const size_t entry_count = dtohl(type_chunk->entryCount); - const size_t offsets_offset = dtohs(type_chunk->header.headerSize); - - // Check if there is the desired entry in this type. - - if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { - // This is encoded as a sparse map, so perform a binary search. - const ResTable_sparseTypeEntry* sparse_indices = - reinterpret_cast( - reinterpret_cast(type_chunk) + offsets_offset); - const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; - const ResTable_sparseTypeEntry* result = - std::lower_bound(sparse_indices, sparse_indices_end, entry_idx, - [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { - return dtohs(entry.idx) < entry_idx; - }); - - if (result == sparse_indices_end || dtohs(result->idx) != entry_idx) { - // No entry found. - continue; - } - - // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as - // the real offset divided by 4. - best_offset = uint32_t{dtohs(result->offset)} * 4u; - } else { - if (entry_idx >= entry_count) { - // This entry cannot be here. - continue; - } +const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk, + uint16_t entry_index) { + uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index); + if (entry_offset == ResTable_type::NO_ENTRY) { + return nullptr; + } + return GetEntryFromOffset(type_chunk, entry_offset); +} - const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type_chunk) + offsets_offset); - const uint32_t offset = dtohl(entry_offsets[entry_idx]); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } +uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const size_t entry_count = dtohl(type_chunk->entryCount); + const size_t offsets_offset = dtohs(type_chunk->header.headerSize); - // There is an entry for this resource, record it. - best_offset = offset; - } + // Check if there is the desired entry in this type. - best_config = &type->configuration; - best_type = type_chunk; + if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { + // This is encoded as a sparse map, so perform a binary search. + const ResTable_sparseTypeEntry* sparse_indices = + reinterpret_cast( + reinterpret_cast(type_chunk) + offsets_offset); + const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; + const ResTable_sparseTypeEntry* result = + std::lower_bound(sparse_indices, sparse_indices_end, entry_index, + [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { + return dtohs(entry.idx) < entry_idx; + }); + + if (result == sparse_indices_end || dtohs(result->idx) != entry_index) { + // No entry found. + return ResTable_type::NO_ENTRY; } - } - if (best_type == nullptr) { - return false; + // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as + // the real offset divided by 4. + return uint32_t{dtohs(result->offset)} * 4u; } - if (UNLIKELY(!VerifyResTableEntry(best_type, best_offset, entry_idx))) { - return false; + // This type is encoded as a dense array. + if (entry_index >= entry_count) { + // This entry cannot be here. + return ResTable_type::NO_ENTRY; } - const ResTable_entry* best_entry = reinterpret_cast( - reinterpret_cast(best_type) + best_offset + dtohl(best_type->entriesStart)); - - const uint32_t* flags = reinterpret_cast(type_spec_ptr->type_spec + 1); - out_entry->type_flags = dtohl(flags[entry_idx]); - out_entry->entry = best_entry; - out_entry->config = best_config; - out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1); - out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index)); - return true; + const uint32_t* entry_offsets = reinterpret_cast( + reinterpret_cast(type_chunk) + offsets_offset); + return dtohl(entry_offsets[entry_index]); } -bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, - FindEntryResult* out_entry) const { - ATRACE_CALL(); - - // If the type IDs are offset in this package, we need to take that into account when searching - // for a type. - const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; - if (UNLIKELY(ptr == nullptr)) { - return false; +const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk, + uint32_t offset) { + if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) { + return nullptr; } - - // If there is an IDMAP supplied with this package, translate the entry ID. - if (ptr->idmap_entries != nullptr) { - if (!LoadedIdmap::Lookup(ptr->idmap_entries, entry_idx, &entry_idx)) { - // There is no mapping, so the resource is not meant to be in this overlay package. - return false; - } - } - return FindEntry(ptr, entry_idx, config, out_entry); + return reinterpret_cast(reinterpret_cast(type_chunk) + + offset + dtohl(type_chunk->entriesStart)); } void LoadedPackage::CollectConfigurations(bool exclude_mipmap, @@ -348,7 +267,7 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, const static std::u16string kMipMap = u"mipmap"; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const util::unique_cptr& type_spec = type_specs_[i]; + const TypeSpecPtr& type_spec = type_specs_[i]; if (type_spec != nullptr) { if (exclude_mipmap) { const int type_idx = type_spec->type_spec->id - 1; @@ -369,8 +288,11 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, } } - for (size_t j = 0; j < type_spec->type_count; j++) { - out_configs->insert(type_spec->types[j].configuration); + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config config; + config.copyFromDtoH((*iter)->config); + out_configs->insert(config); } } } @@ -380,10 +302,12 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set* out char temp_locale[RESTABLE_MAX_LOCALE_LEN]; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const util::unique_cptr& type_spec = type_specs_[i]; + const TypeSpecPtr& type_spec = type_specs_[i]; if (type_spec != nullptr) { - for (size_t j = 0; j < type_spec->type_count; j++) { - const ResTable_config& configuration = type_spec->types[j].configuration; + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config configuration; + configuration.copyFromDtoH((*iter)->config); if (configuration.locale != 0) { configuration.getBcp47Locale(temp_locale, canonicalize); std::string locale(temp_locale); @@ -411,17 +335,17 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } - for (size_t ti = 0; ti < type_spec->type_count; ti++) { - const Type* type = &type_spec->types[ti]; - size_t entry_count = dtohl(type->type->entryCount); + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + const ResTable_type* type = *iter; + size_t entry_count = dtohl(type->entryCount); for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) { const uint32_t* entry_offsets = reinterpret_cast( - reinterpret_cast(type->type) + dtohs(type->type->header.headerSize)); + reinterpret_cast(type) + dtohs(type->header.headerSize)); const uint32_t offset = dtohl(entry_offsets[entry_idx]); if (offset != ResTable_type::NO_ENTRY) { - const ResTable_entry* entry = - reinterpret_cast(reinterpret_cast(type->type) + - dtohl(type->type->entriesStart) + offset); + const ResTable_entry* entry = reinterpret_cast( + reinterpret_cast(type) + dtohl(type->entriesStart) + offset); if (dtohl(entry->key.index) == static_cast(key_idx)) { // The package ID will be overridden by the caller (due to runtime assignment of package // IDs for shared libraries). @@ -433,8 +357,7 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } -const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const { - const uint8_t package_id = get_package_id(resid); +const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { for (const auto& loaded_package : packages_) { if (loaded_package->GetPackageId() == package_id) { return loaded_package.get(); @@ -446,7 +369,7 @@ const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const { std::unique_ptr LoadedPackage::Load(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool system, bool load_as_shared_library) { - ATRACE_CALL(); + ATRACE_NAME("LoadedPackage::Load"); std::unique_ptr loaded_package(new LoadedPackage()); // typeIdOffset was added at some point, but we still must recognize apps built before this @@ -486,14 +409,10 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, util::ReadUtf16StringFromDevice(header->name, arraysize(header->name), &loaded_package->package_name_); - // A TypeSpec builder. We use this to accumulate the set of Types - // available for a TypeSpec, and later build a single, contiguous block - // of memory that holds all the Types together with the TypeSpec. - std::unique_ptr types_builder; - - // Keep track of the last seen type index. Since type IDs are 1-based, - // this records their index, which is 0-based (type ID - 1). - uint8_t last_type_idx = 0; + // A map of TypeSpec builders, each associated with an type index. + // We use these to accumulate the set of Types available for a TypeSpec, and later build a single, + // contiguous block of memory that holds all the Types together with the TypeSpec. + std::unordered_map> type_builder_map; ChunkIterator iter(chunk.data_ptr(), chunk.data_size()); while (iter.HasNext()) { @@ -525,30 +444,6 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_TYPE_SPEC_TYPE: { - ATRACE_NAME("LoadTableTypeSpec"); - - // Starting a new TypeSpec, so finish the old one if there was one. - if (types_builder) { - TypeSpecPtr type_spec_ptr = types_builder->Build(); - if (type_spec_ptr == nullptr) { - LOG(ERROR) << "Too many type configurations, overflow detected."; - return {}; - } - - // We only add the type to the package if there is no IDMAP, or if the type is - // overlaying something. - if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) { - // If this is an overlay, insert it at the target type ID. - if (type_spec_ptr->idmap_entries != nullptr) { - last_type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; - } - loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); - } - - types_builder = {}; - last_type_idx = 0; - } - const ResTable_typeSpec* type_spec = child_chunk.header(); if (type_spec == nullptr) { LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small."; @@ -583,8 +478,6 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, return {}; } - last_type_idx = type_spec->id - 1; - // If this is an overlay, associate the mapping of this type to the target type // from the IDMAP. const IdmapEntry_header* idmap_entry_header = nullptr; @@ -592,7 +485,13 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, idmap_entry_header = loaded_idmap->GetEntryMapForType(type_spec->id); } - types_builder = util::make_unique(type_spec, idmap_entry_header); + std::unique_ptr& builder_ptr = type_builder_map[type_spec->id - 1]; + if (builder_ptr == nullptr) { + builder_ptr = util::make_unique(type_spec, idmap_entry_header); + } else { + LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x", + type_spec->id); + } } break; case RES_TABLE_TYPE_TYPE: { @@ -607,12 +506,15 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } // Type chunks must be preceded by their TypeSpec chunks. - if (!types_builder || type->id - 1 != last_type_idx) { - LOG(ERROR) << "RES_TABLE_TYPE_TYPE found without preceding RES_TABLE_TYPE_SPEC_TYPE."; + std::unique_ptr& builder_ptr = type_builder_map[type->id - 1]; + if (builder_ptr != nullptr) { + builder_ptr->AddType(type); + } else { + LOG(ERROR) << StringPrintf( + "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.", + type->id); return {}; } - - types_builder->AddType(type); } break; case RES_TABLE_LIBRARY_TYPE: { @@ -638,7 +540,7 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, arraysize(entry_iter->packageName), &package_name); if (dtohl(entry_iter->packageId) >= std::numeric_limits::max()) { - LOG(ERROR) << base::StringPrintf( + LOG(ERROR) << StringPrintf( "Package ID %02x in RES_TABLE_LIBRARY_TYPE too large for package '%s'.", dtohl(entry_iter->packageId), package_name.c_str()); return {}; @@ -651,14 +553,20 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, } break; default: - LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type()); break; } } - // Finish the last TypeSpec. - if (types_builder) { - TypeSpecPtr type_spec_ptr = types_builder->Build(); + if (iter.HadError()) { + LOG(ERROR) << iter.GetLastError(); + return {}; + } + + // Flatten and construct the TypeSpecs. + for (auto& entry : type_builder_map) { + uint8_t type_idx = static_cast(entry.first); + TypeSpecPtr type_spec_ptr = entry.second->Build(); if (type_spec_ptr == nullptr) { LOG(ERROR) << "Too many type configurations, overflow detected."; return {}; @@ -669,43 +577,17 @@ std::unique_ptr LoadedPackage::Load(const Chunk& chunk, if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) { // If this is an overlay, insert it at the target type ID. if (type_spec_ptr->idmap_entries != nullptr) { - last_type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; + type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; } - loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); + loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr); } } - if (iter.HadError()) { - LOG(ERROR) << iter.GetLastError(); - return {}; - } return std::move(loaded_package); } -bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, - FindEntryResult* out_entry) const { - ATRACE_CALL(); - - const uint8_t package_id = get_package_id(resid); - const uint8_t type_id = get_type_id(resid); - const uint16_t entry_id = get_entry_id(resid); - - if (UNLIKELY(type_id == 0)) { - LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); - return false; - } - - for (const auto& loaded_package : packages_) { - if (loaded_package->GetPackageId() == package_id) { - return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry); - } - } - return false; -} - bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library) { - ATRACE_CALL(); const ResTable_header* header = chunk.header(); if (header == nullptr) { LOG(ERROR) << "RES_TABLE_TYPE too small."; @@ -752,7 +634,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, } break; default: - LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type()); break; } } @@ -767,7 +649,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, std::unique_ptr LoadedArsc::Load(const StringPiece& data, const LoadedIdmap* loaded_idmap, bool system, bool load_as_shared_library) { - ATRACE_CALL(); + ATRACE_NAME("LoadedArsc::LoadTable"); // Not using make_unique because the constructor is private. std::unique_ptr loaded_arsc(new LoadedArsc()); @@ -784,7 +666,7 @@ std::unique_ptr LoadedArsc::Load(const StringPiece& data, break; default: - LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type()); break; } } diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index b033137b4764..ef08897d997a 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -69,6 +69,8 @@ struct ResolvedBag { Entry entries[0]; }; +struct FindEntryResult; + // AssetManager2 is the main entry point for accessing assets and resources. // AssetManager2 provides caching of resources retrieved via the underlying ApkAssets. class AssetManager2 { @@ -127,7 +129,7 @@ class AssetManager2 { // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap' // will be excluded from the list. std::set GetResourceConfigurations(bool exclude_system = false, - bool exclude_mipmap = false); + bool exclude_mipmap = false) const; // Returns all the locales for which there are resources defined. This includes resource // locales in all the ApkAssets set for this AssetManager. @@ -136,24 +138,24 @@ class AssetManager2 { // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized // and de-duped in the resulting list. std::set GetResourceLocales(bool exclude_system = false, - bool merge_equivalent_languages = false); + bool merge_equivalent_languages = false) const; // Searches the set of APKs loaded by this AssetManager and opens the first one found located // in the assets/ directory. // `mode` controls how the file is opened. // // NOTE: The loaded APKs are searched in reverse order. - std::unique_ptr Open(const std::string& filename, Asset::AccessMode mode); + std::unique_ptr Open(const std::string& filename, Asset::AccessMode mode) const; // Opens a file within the assets/ directory of the APK specified by `cookie`. // `mode` controls how the file is opened. std::unique_ptr Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode); + Asset::AccessMode mode) const; // Opens the directory specified by `dirname`. The result is an AssetDir that is the combination // of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded. // The entries are sorted by their ASCII name. - std::unique_ptr OpenDir(const std::string& dirname); + std::unique_ptr OpenDir(const std::string& dirname) const; // Searches the set of APKs loaded by this AssetManager and opens the first one found. // `mode` controls how the file is opened. @@ -161,24 +163,24 @@ class AssetManager2 { // // NOTE: The loaded APKs are searched in reverse order. std::unique_ptr OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie = nullptr); + ApkAssetsCookie* out_cookie = nullptr) const; // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened. // This is typically used to open a specific AndroidManifest.xml, or a binary XML file // referenced by a resource lookup with GetResource(). std::unique_ptr OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode); + Asset::AccessMode mode) const; // Populates the `out_name` parameter with resource name information. // Utf8 strings are preferred, and only if they are unavailable are // the Utf16 variants populated. // Returns false if the resource was not found or the name was missing/corrupt. - bool GetResourceName(uint32_t resid, ResourceName* out_name); + bool GetResourceName(uint32_t resid, ResourceName* out_name) const; // Populates `out_flags` with the bitmask of configuration axis that this resource varies with. // See ResTable_config for the list of configuration axis. // Returns false if the resource was not found. - bool GetResourceFlags(uint32_t resid, uint32_t* out_flags); + bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const; // Finds the resource ID assigned to `resource_name`. // `resource_name` must be of the form '[package:][type/]entry'. @@ -186,7 +188,7 @@ class AssetManager2 { // If no type is specified in `resource_name`, then `fallback_type` is used as the type. // Returns 0x0 if no resource by that name was found. uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {}, - const std::string& fallback_package = {}); + const std::string& fallback_package = {}) const; // Retrieves the best matching resource with ID `resid`. The resource value is filled into // `out_value` and the configuration for the selected value is populated in `out_selected_config`. @@ -199,7 +201,7 @@ class AssetManager2 { // this function logs if the resource was a map/bag type before returning kInvalidCookie. ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags); + uint32_t* out_flags) const; // Resolves the resource reference in `in_out_value` if the data type is // Res_value::TYPE_REFERENCE. @@ -215,7 +217,7 @@ class AssetManager2 { // it was not found. ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference); + uint32_t* out_last_reference) const; // Retrieves the best matching bag/map resource with ID `resid`. // This method will resolve all parent references for this bag and merge keys with the child. @@ -233,9 +235,9 @@ class AssetManager2 { std::unique_ptr NewTheme(); template - void ForEachPackage(Func func) { + void ForEachPackage(Func func) const { for (const PackageGroup& package_group : package_groups_) { - func(package_group.packages_.front()->GetPackageName(), + func(package_group.packages_.front().loaded_package_->GetPackageName(), package_group.dynamic_ref_table.mAssignedPackageId); } } @@ -260,7 +262,7 @@ class AssetManager2 { // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds. ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, - FindEntryResult* out_entry); + FindEntryResult* out_entry) const; // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. @@ -270,13 +272,43 @@ class AssetManager2 { // bitmask `diff`. void InvalidateCaches(uint32_t diff); + // Triggers the re-construction of lists of types that match the set configuration. + // This should always be called when mutating the AssetManager's configuration or ApkAssets set. + void RebuildFilterList(); + // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector apk_assets_; + // A collection of configurations and their associated ResTable_type that match the current + // AssetManager configuration. + struct FilteredConfigGroup { + std::vector configurations; + std::vector types; + }; + + // Represents an single package. + struct ConfiguredPackage { + // A pointer to the immutable, loaded package info. + const LoadedPackage* loaded_package_; + + // A mutable AssetManager-specific list of configurations that match the AssetManager's + // current configuration. This is used as an optimization to avoid checking every single + // candidate configuration when looking up resources. + ByteBucketArray filtered_configs_; + }; + + // Represents a logical package, which can be made up of many individual packages. Each package + // in a PackageGroup shares the same package name and package ID. struct PackageGroup { - std::vector packages_; + // The set of packages that make-up this group. + std::vector packages_; + + // The cookies associated with each package in the group. They share the same order as + // packages_. std::vector cookies_; + + // A library reference table that contains build-package ID to runtime-package ID mappings. DynamicRefTable dynamic_ref_table; }; @@ -350,7 +382,7 @@ class Theme { ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config = nullptr, uint32_t* in_out_type_spec_flags = nullptr, - uint32_t* out_last_ref = nullptr); + uint32_t* out_last_ref = nullptr) const; private: DISALLOW_COPY_AND_ASSIGN(Theme); diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h index f281921824e7..03fad4947dfe 100644 --- a/libs/androidfw/include/androidfw/AttributeFinder.h +++ b/libs/androidfw/include/androidfw/AttributeFinder.h @@ -58,6 +58,7 @@ class BackTrackingAttributeFinder { BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end); Iterator Find(uint32_t attr); + inline Iterator end(); private: void JumpToClosestAttribute(uint32_t package_id); @@ -201,6 +202,11 @@ Iterator BackTrackingAttributeFinder::Find(uint32_t attr) { return end_; } +template +Iterator BackTrackingAttributeFinder::end() { + return end_; +} + } // namespace android #endif // ANDROIDFW_ATTRIBUTE_FINDER_H diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h index 69b760414846..35ef98d8c704 100644 --- a/libs/androidfw/include/androidfw/AttributeResolution.h +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -17,7 +17,8 @@ #ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H #define ANDROIDFW_ATTRIBUTERESOLUTION_H -#include +#include "androidfw/AssetManager2.h" +#include "androidfw/ResourceTypes.h" namespace android { @@ -42,19 +43,19 @@ enum { // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, +bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid, uint32_t* src_values, size_t src_values_length, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` is NOT optional and must NOT be nullptr. -void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs, +bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); } // namespace android diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 965e2dbd2fb2..35ae5fcd9e7b 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -41,32 +41,40 @@ class DynamicPackageEntry { int package_id = 0; }; -struct FindEntryResult { - // A pointer to the resource table entry for this resource. - // If the size of the entry is > sizeof(ResTable_entry), it can be cast to - // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry = nullptr; - - // The configuration for which the resulting entry was defined. - const ResTable_config* config = nullptr; - - // Stores the resulting bitmask of configuration axis with which the resource value varies. - uint32_t type_flags = 0u; - - // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table = nullptr; - - // The string pool reference to the type's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef type_string_ref; - - // The string pool reference to the entry's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef entry_string_ref; +// TypeSpec is going to be immediately proceeded by +// an array of Type structs, all in the same block of memory. +struct TypeSpec { + // Pointer to the mmapped data where flags are kept. + // Flags denote whether the resource entry is public + // and under which configurations it varies. + const ResTable_typeSpec* type_spec; + + // Pointer to the mmapped data where the IDMAP mappings for this type + // exist. May be nullptr if no IDMAP exists. + const IdmapEntry_header* idmap_entries; + + // The number of types that follow this struct. + // There is a type for each configuration that entries are defined for. + size_t type_count; + + // Trick to easily access a variable number of Type structs + // proceeding this struct, and to ensure their alignment. + const ResTable_type* types[0]; + + inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const { + if (entry_index >= dtohl(type_spec->entryCount)) { + return 0u; + } + + const uint32_t* flags = reinterpret_cast(type_spec + 1); + return flags[entry_index]; + } }; -struct TypeSpec; -class LoadedArsc; +// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of +// ResTable_type pointers. +// TypeSpecPtr is a managed pointer that knows how to delete itself. +using TypeSpecPtr = util::unique_cptr; class LoadedPackage { public: @@ -76,9 +84,6 @@ class LoadedPackage { ~LoadedPackage(); - bool FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, - FindEntryResult* out_entry) const; - // Finds the entry with the specified type name and entry name. The names are in UTF-16 because // the underlying ResStringPool API expects this. For now this is acceptable, but since // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. @@ -86,6 +91,12 @@ class LoadedPackage { // for patching the correct package ID to the resource ID. uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; + static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index); + + static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index); + + static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset); + // Returns the string pool where type names are stored. inline const ResStringPool* GetTypeStringPool() const { return &type_string_pool_; @@ -135,14 +146,32 @@ class LoadedPackage { // before being inserted into the set. This may cause some equivalent locales to de-dupe. void CollectLocales(bool canonicalize, std::set* out_locales) const; + // type_idx is TT - 1 from 0xPPTTEEEE. + inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const { + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + return type_specs_[type_index - type_id_offset_].get(); + } + + template + void ForEachTypeSpec(Func f) const { + for (size_t i = 0; i < type_specs_.size(); i++) { + const TypeSpecPtr& ptr = type_specs_[i]; + if (ptr != nullptr) { + uint8_t type_id = ptr->type_spec->id; + if (ptr->idmap_entries != nullptr) { + type_id = ptr->idmap_entries->target_type_id; + } + f(ptr.get(), type_id - 1); + } + } + } + private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); LoadedPackage(); - bool FindEntry(const util::unique_cptr& type_spec_ptr, uint16_t entry_idx, - const ResTable_config& config, FindEntryResult* out_entry) const; - ResStringPool type_string_pool_; ResStringPool key_string_pool_; std::string package_name_; @@ -152,7 +181,7 @@ class LoadedPackage { bool system_ = false; bool overlay_ = false; - ByteBucketArray> type_specs_; + ByteBucketArray type_specs_; std::vector dynamic_package_map_; }; @@ -180,25 +209,20 @@ class LoadedArsc { return &global_string_pool_; } - // Finds the resource with ID `resid` with the best value for configuration `config`. - // The parameter `out_entry` will be filled with the resulting resource entry. - // The resource entry can be a simple entry (ResTable_entry) or a complex bag - // (ResTable_entry_map). - bool FindEntry(uint32_t resid, const ResTable_config& config, FindEntryResult* out_entry) const; + // Gets a pointer to the package with the specified package ID, or nullptr if no such package + // exists. + const LoadedPackage* GetPackageById(uint8_t package_id) const; - // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist. - const LoadedPackage* GetPackageForId(uint32_t resid) const; + // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. + inline const std::vector>& GetPackages() const { + return packages_; + } // Returns true if this is a system provided resource. inline bool IsSystem() const { return system_; } - // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. - inline const std::vector>& GetPackages() const { - return packages_; - } - private: DISALLOW_COPY_AND_ASSIGN(LoadedArsc); diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h new file mode 100644 index 000000000000..64924f433245 --- /dev/null +++ b/libs/androidfw/include/androidfw/MutexGuard.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROIDFW_MUTEXGUARD_H +#define ANDROIDFW_MUTEXGUARD_H + +#include +#include + +#include "android-base/macros.h" + +namespace android { + +template +class ScopedLock; + +// Owns the guarded object and protects access to it via a mutex. +// The guarded object is inaccessible via this class. +// The mutex is locked and the object accessed via the ScopedLock class. +// +// NOTE: The template parameter T should not be a raw pointer, since ownership +// is ambiguous and error-prone. Instead use an std::unique_ptr<>. +// +// Example use: +// +// Guarded shared_string("hello"); +// { +// ScopedLock locked_string(shared_string); +// *locked_string += " world"; +// } +// +template +class Guarded { + static_assert(!std::is_pointer::value, "T must not be a raw pointer"); + + public: + explicit Guarded() : guarded_() { + } + + template + explicit Guarded(const T& guarded, + typename std::enable_if::value>::type = void()) + : guarded_(guarded) { + } + + template + explicit Guarded(T&& guarded, + typename std::enable_if::value>::type = void()) + : guarded_(std::move(guarded)) { + } + + private: + friend class ScopedLock; + + DISALLOW_COPY_AND_ASSIGN(Guarded); + + std::mutex lock_; + T guarded_; +}; + +template +class ScopedLock { + public: + explicit ScopedLock(Guarded& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) { + } + + T& operator*() { + return guarded_; + } + + T* operator->() { + return &guarded_; + } + + T* get() { + return &guarded_; + } + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedLock); + + std::lock_guard lock_; + T& guarded_; +}; + +} // namespace android + +#endif // ANDROIDFW_MUTEXGUARD_H diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h index c2eae855bb7b..d94779bf5225 100644 --- a/libs/androidfw/include/androidfw/ResourceUtils.h +++ b/libs/androidfw/include/androidfw/ResourceUtils.h @@ -28,7 +28,7 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin StringPiece* out_entry); inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) { - return resid | (static_cast(package_id) << 24); + return (resid & 0x00ffffffu) | (static_cast(package_id) << 24); } inline uint8_t get_package_id(uint32_t resid) { diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index 6c43a67e602f..e2b9f0040989 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -26,58 +26,56 @@ using ::android::base::unique_fd; using ::com::android::basic::R; +using ::testing::Eq; +using ::testing::Ge; +using ::testing::NotNull; +using ::testing::SizeIs; +using ::testing::StrEq; namespace android { TEST(ApkAssetsTest, LoadApk) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - - const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); - ASSERT_NE(nullptr, loaded_package); - - std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); + ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkFromFd) { const std::string path = GetTestDataPath() + "/basic/basic.apk"; unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY)); - ASSERT_GE(fd.get(), 0); + ASSERT_THAT(fd.get(), Ge(0)); std::unique_ptr loaded_apk = ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - - const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); - ASSERT_NE(nullptr, loaded_package); - - std::unique_ptr asset = loaded_apk->Open("res/layout/main.xml"); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); + ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); + const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic()); loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic()); } @@ -86,19 +84,22 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { ResTable target_table; const std::string target_path = GetTestDataPath() + "/basic/basic.apk"; ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, target_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); + ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/), + Eq(NO_ERROR)); ResTable overlay_table; const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk"; ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); + ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/), + Eq(NO_ERROR)); util::unique_cptr idmap_data; void* temp_data; size_t idmap_len; - ASSERT_EQ(NO_ERROR, target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), - overlay_path.c_str(), &temp_data, &idmap_len)); + ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), + overlay_path.c_str(), &temp_data, &idmap_len), + Eq(NO_ERROR)); idmap_data.reset(temp_data); TemporaryFile tf; @@ -108,37 +109,30 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { // Open something so that the destructor of TemporaryFile closes a valid fd. tf.fd = open("/dev/null", O_WRONLY); - std::unique_ptr loaded_overlay_apk = ApkAssets::LoadOverlay(tf.path); - ASSERT_NE(nullptr, loaded_overlay_apk); + ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull()); } TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); - { - std::unique_ptr assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, assets); - } + { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } - { - std::unique_ptr assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, assets); - } + { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } } TEST(ApkAssetsTest, OpenUncompressedAssetFd) { std::unique_ptr loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(asset, NotNull()); off64_t start, length; unique_fd fd(asset->openFileDescriptor(&start, &length)); - EXPECT_GE(fd.get(), 0); + ASSERT_THAT(fd.get(), Ge(0)); lseek64(fd.get(), start, SEEK_SET); @@ -146,7 +140,7 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) { buffer.resize(length); ASSERT_TRUE(base::ReadFully(fd.get(), &*buffer.begin(), length)); - EXPECT_EQ("This should be uncompressed.\n\n", buffer); + EXPECT_THAT(buffer, StrEq("This should be uncompressed.\n\n")); } } // namespace android diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp index 85e8f25394e9..437e14772964 100644 --- a/libs/androidfw/tests/AssetManager2_bench.cpp +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -81,17 +81,18 @@ static void BM_AssetManagerLoadFrameworkAssetsOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerLoadFrameworkAssetsOld); -static void BM_AssetManagerGetResource(benchmark::State& state) { - GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, - basic::R::integer::number1, state); +static void BM_AssetManagerGetResource(benchmark::State& state, uint32_t resid) { + GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid, state); } -BENCHMARK(BM_AssetManagerGetResource); +BENCHMARK_CAPTURE(BM_AssetManagerGetResource, number1, basic::R::integer::number1); +BENCHMARK_CAPTURE(BM_AssetManagerGetResource, deep_ref, basic::R::integer::deep_ref); -static void BM_AssetManagerGetResourceOld(benchmark::State& state) { - GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, - basic::R::integer::number1, state); +static void BM_AssetManagerGetResourceOld(benchmark::State& state, uint32_t resid) { + GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid, + state); } -BENCHMARK(BM_AssetManagerGetResourceOld); +BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, number1, basic::R::integer::number1); +BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, deep_ref, basic::R::integer::deep_ref); static void BM_AssetManagerGetLibraryResource(benchmark::State& state) { GetResourceBenchmark( @@ -196,7 +197,7 @@ BENCHMARK(BM_AssetManagerGetResourceLocales); static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) { AssetManager assets; if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/, - false /*isSystemAssets*/)) { + true /*isSystemAssets*/)) { state.SkipWithError("Failed to load assets"); return; } @@ -211,4 +212,44 @@ static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerGetResourceLocalesOld); +static void BM_AssetManagerSetConfigurationFramework(benchmark::State& state) { + std::unique_ptr apk = ApkAssets::Load(kFrameworkPath); + if (apk == nullptr) { + state.SkipWithError("Failed to load assets"); + return; + } + + AssetManager2 assets; + assets.SetApkAssets({apk.get()}); + + ResTable_config config; + memset(&config, 0, sizeof(config)); + + while (state.KeepRunning()) { + config.sdkVersion = ~config.sdkVersion; + assets.SetConfiguration(config); + } +} +BENCHMARK(BM_AssetManagerSetConfigurationFramework); + +static void BM_AssetManagerSetConfigurationFrameworkOld(benchmark::State& state) { + AssetManager assets; + if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/, + true /*isSystemAssets*/)) { + state.SkipWithError("Failed to load assets"); + return; + } + + const ResTable& table = assets.getResources(true); + + ResTable_config config; + memset(&config, 0, sizeof(config)); + + while (state.KeepRunning()) { + config.sdkVersion = ~config.sdkVersion; + assets.setConfiguration(config); + } +} +BENCHMARK(BM_AssetManagerSetConfigurationFrameworkOld); + } // namespace android diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 92462a6cfadf..7cac2b3417b5 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -36,6 +36,10 @@ namespace lib_one = com::android::lib_one; namespace lib_two = com::android::lib_two; namespace libclient = com::android::libclient; +using ::testing::Eq; +using ::testing::NotNull; +using ::testing::StrEq; + namespace android { class AssetManager2Test : public ::testing::Test { @@ -59,11 +63,14 @@ class AssetManager2Test : public ::testing::Test { libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk"); ASSERT_NE(nullptr, libclient_assets_); - appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); + appaslib_assets_ = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); ASSERT_NE(nullptr, appaslib_assets_); system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/); ASSERT_NE(nullptr, system_assets_); + + app_assets_ = ApkAssets::Load(GetTestDataPath() + "/app/app.apk"); + ASSERT_THAT(app_assets_, NotNull()); } protected: @@ -75,6 +82,7 @@ class AssetManager2Test : public ::testing::Test { std::unique_ptr libclient_assets_; std::unique_ptr appaslib_assets_; std::unique_ptr system_assets_; + std::unique_ptr app_assets_; }; TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) { @@ -228,6 +236,25 @@ TEST_F(AssetManager2Test, FindsBagResourceFromMultipleApkAssets) {} TEST_F(AssetManager2Test, FindsBagResourceFromSharedLibrary) { AssetManager2 assetmanager; + // libclient is built with lib_one and then lib_two in order. + // Reverse the order to test that proper package ID re-assignment is happening. + assetmanager.SetApkAssets( + {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); + + const ResolvedBag* bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03)); + ASSERT_NE(nullptr, bag); + ASSERT_GE(bag->entry_count, 2u); + + // First two attributes come from lib_one. + EXPECT_EQ(1, bag->entries[0].cookie); + EXPECT_EQ(0x03, get_package_id(bag->entries[0].key)); + EXPECT_EQ(1, bag->entries[1].cookie); + EXPECT_EQ(0x03, get_package_id(bag->entries[1].key)); +} + +TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) { + AssetManager2 assetmanager; + // libclient is built with lib_one and then lib_two in order. // Reverse the order to test that proper package ID re-assignment is happening. assetmanager.SetApkAssets( @@ -446,8 +473,68 @@ TEST_F(AssetManager2Test, GetResourceId) { assetmanager.GetResourceId("main", "layout", "com.android.basic")); } -TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {} +TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({system_assets_.get()}); + + std::unique_ptr asset = assetmanager.Open("file.txt", Asset::ACCESS_BUFFER); + ASSERT_THAT(asset, NotNull()); + + const char* data = reinterpret_cast(asset->getBuffer(false /*wordAligned*/)); + ASSERT_THAT(data, NotNull()); + EXPECT_THAT(std::string(data, asset->getLength()), StrEq("file\n")); +} + +TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({system_assets_.get(), app_assets_.get()}); + + std::unique_ptr asset = assetmanager.Open("file.txt", Asset::ACCESS_BUFFER); + ASSERT_THAT(asset, NotNull()); + + const char* data = reinterpret_cast(asset->getBuffer(false /*wordAligned*/)); + ASSERT_THAT(data, NotNull()); + EXPECT_THAT(std::string(data, asset->getLength()), StrEq("app override file\n")); +} + +TEST_F(AssetManager2Test, OpenDir) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({system_assets_.get()}); + + std::unique_ptr asset_dir = assetmanager.OpenDir(""); + ASSERT_THAT(asset_dir, NotNull()); + ASSERT_THAT(asset_dir->getFileCount(), Eq(2u)); + + EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("file.txt"))); + EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular)); -TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {} + EXPECT_THAT(asset_dir->getFileName(1), Eq(String8("subdir"))); + EXPECT_THAT(asset_dir->getFileType(1), Eq(FileType::kFileTypeDirectory)); + + asset_dir = assetmanager.OpenDir("subdir"); + ASSERT_THAT(asset_dir, NotNull()); + ASSERT_THAT(asset_dir->getFileCount(), Eq(1u)); + + EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("subdir_file.txt"))); + EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular)); +} + +TEST_F(AssetManager2Test, OpenDirFromManyApks) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({system_assets_.get(), app_assets_.get()}); + + std::unique_ptr asset_dir = assetmanager.OpenDir(""); + ASSERT_THAT(asset_dir, NotNull()); + ASSERT_THAT(asset_dir->getFileCount(), Eq(3u)); + + EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("app_file.txt"))); + EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular)); + + EXPECT_THAT(asset_dir->getFileName(1), Eq(String8("file.txt"))); + EXPECT_THAT(asset_dir->getFileType(1), Eq(FileType::kFileTypeRegular)); + + EXPECT_THAT(asset_dir->getFileName(2), Eq(String8("subdir"))); + EXPECT_THAT(asset_dir->getFileType(2), Eq(FileType::kFileTypeDirectory)); +} } // namespace android diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp new file mode 100644 index 000000000000..fa300c50218a --- /dev/null +++ b/libs/androidfw/tests/AttributeResolution_bench.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "benchmark/benchmark.h" + +//#include "android-base/stringprintf.h" +#include "androidfw/ApkAssets.h" +#include "androidfw/AssetManager.h" +#include "androidfw/AssetManager2.h" +#include "androidfw/AttributeResolution.h" +#include "androidfw/ResourceTypes.h" + +#include "BenchmarkHelpers.h" +#include "data/basic/R.h" +#include "data/styles/R.h" + +namespace app = com::android::app; +namespace basic = com::android::basic; + +namespace android { + +constexpr const static char* kFrameworkPath = "/system/framework/framework-res.apk"; +constexpr const static uint32_t Theme_Material_Light = 0x01030237u; + +static void BM_ApplyStyle(benchmark::State& state) { + std::unique_ptr styles_apk = + ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + if (styles_apk == nullptr) { + state.SkipWithError("failed to load assets"); + return; + } + + AssetManager2 assetmanager; + assetmanager.SetApkAssets({styles_apk.get()}); + + std::unique_ptr asset = + assetmanager.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); + if (asset == nullptr) { + state.SkipWithError("failed to load layout"); + return; + } + + ResXMLTree xml_tree; + if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) { + state.SkipWithError("corrupt xml layout"); + return; + } + + // Skip to the first tag. + while (xml_tree.next() != ResXMLParser::START_TAG) { + } + + std::unique_ptr theme = assetmanager.NewTheme(); + theme->ApplyStyle(app::R::style::StyleTwo); + + std::array attrs{{app::R::attr::attr_one, app::R::attr::attr_two, + app::R::attr::attr_three, app::R::attr::attr_four, + app::R::attr::attr_five, app::R::attr::attr_empty}}; + std::array values; + std::array indices; + + while (state.KeepRunning()) { + ApplyStyle(theme.get(), &xml_tree, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), + attrs.size(), values.data(), indices.data()); + } +} +BENCHMARK(BM_ApplyStyle); + +static void BM_ApplyStyleFramework(benchmark::State& state) { + std::unique_ptr framework_apk = ApkAssets::Load(kFrameworkPath); + if (framework_apk == nullptr) { + state.SkipWithError("failed to load framework assets"); + return; + } + + std::unique_ptr basic_apk = + ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); + if (basic_apk == nullptr) { + state.SkipWithError("failed to load assets"); + return; + } + + AssetManager2 assetmanager; + assetmanager.SetApkAssets({framework_apk.get(), basic_apk.get()}); + + ResTable_config device_config; + memset(&device_config, 0, sizeof(device_config)); + device_config.language[0] = 'e'; + device_config.language[1] = 'n'; + device_config.country[0] = 'U'; + device_config.country[1] = 'S'; + device_config.orientation = ResTable_config::ORIENTATION_PORT; + device_config.smallestScreenWidthDp = 700; + device_config.screenWidthDp = 700; + device_config.screenHeightDp = 1024; + device_config.sdkVersion = 27; + + Res_value value; + ResTable_config config; + uint32_t flags = 0u; + ApkAssetsCookie cookie = + assetmanager.GetResource(basic::R::layout::layoutt, false /*may_be_bag*/, + 0u /*density_override*/, &value, &config, &flags); + if (cookie == kInvalidCookie) { + state.SkipWithError("failed to find R.layout.layout"); + return; + } + + size_t len = 0u; + const char* layout_path = + assetmanager.GetStringPoolForCookie(cookie)->string8At(value.data, &len); + if (layout_path == nullptr || len == 0u) { + state.SkipWithError("failed to lookup layout path"); + return; + } + + std::unique_ptr asset = assetmanager.OpenNonAsset( + StringPiece(layout_path, len).to_string(), cookie, Asset::ACCESS_BUFFER); + if (asset == nullptr) { + state.SkipWithError("failed to load layout"); + return; + } + + ResXMLTree xml_tree; + if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) { + state.SkipWithError("corrupt xml layout"); + return; + } + + // Skip to the first tag. + while (xml_tree.next() != ResXMLParser::START_TAG) { + } + + std::unique_ptr theme = assetmanager.NewTheme(); + theme->ApplyStyle(Theme_Material_Light); + + std::array attrs{ + {0x0101000e, 0x01010034, 0x01010095, 0x01010096, 0x01010097, 0x01010098, 0x01010099, + 0x0101009a, 0x0101009b, 0x010100ab, 0x010100af, 0x010100b0, 0x010100b1, 0x0101011f, + 0x01010120, 0x0101013f, 0x01010140, 0x0101014e, 0x0101014f, 0x01010150, 0x01010151, + 0x01010152, 0x01010153, 0x01010154, 0x01010155, 0x01010156, 0x01010157, 0x01010158, + 0x01010159, 0x0101015a, 0x0101015b, 0x0101015c, 0x0101015d, 0x0101015e, 0x0101015f, + 0x01010160, 0x01010161, 0x01010162, 0x01010163, 0x01010164, 0x01010165, 0x01010166, + 0x01010167, 0x01010168, 0x01010169, 0x0101016a, 0x0101016b, 0x0101016c, 0x0101016d, + 0x0101016e, 0x0101016f, 0x01010170, 0x01010171, 0x01010217, 0x01010218, 0x0101021d, + 0x01010220, 0x01010223, 0x01010224, 0x01010264, 0x01010265, 0x01010266, 0x010102c5, + 0x010102c6, 0x010102c7, 0x01010314, 0x01010315, 0x01010316, 0x0101035e, 0x0101035f, + 0x01010362, 0x01010374, 0x0101038c, 0x01010392, 0x01010393, 0x010103ac, 0x0101045d, + 0x010104b6, 0x010104b7, 0x010104d6, 0x010104d7, 0x010104dd, 0x010104de, 0x010104df, + 0x01010535, 0x01010536, 0x01010537, 0x01010538, 0x01010546, 0x01010567, 0x011100c9, + 0x011100ca}}; + + std::array values; + std::array indices; + while (state.KeepRunning()) { + ApplyStyle(theme.get(), &xml_tree, 0x01010084u /*def_style_attr*/, 0u /*def_style_res*/, + attrs.data(), attrs.size(), values.data(), indices.data()); + } +} +BENCHMARK(BM_ApplyStyleFramework); + +} // namespace android diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index 2d73ce8f8ee3..c8dbe205fee2 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -21,6 +21,8 @@ #include "android-base/file.h" #include "android-base/logging.h" #include "android-base/macros.h" +#include "androidfw/AssetManager2.h" +#include "androidfw/ResourceUtils.h" #include "TestHelpers.h" #include "data/styles/R.h" @@ -32,15 +34,14 @@ namespace android { class AttributeResolutionTest : public ::testing::Test { public: virtual void SetUp() override { - std::string contents; - ASSERT_TRUE(ReadFileFromZipToString( - GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, table_.add(contents.data(), contents.size(), - 1 /*cookie*/, true /*copyData*/)); + styles_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + ASSERT_NE(nullptr, styles_assets_); + assetmanager_.SetApkAssets({styles_assets_.get()}); } protected: - ResTable table_; + std::unique_ptr styles_assets_; + AssetManager2 assetmanager_; }; class AttributeResolutionXmlTest : public AttributeResolutionTest { @@ -48,13 +49,12 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { virtual void SetUp() override { AttributeResolutionTest::SetUp(); - std::string contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", - "res/layout/layout.xml", &contents)); + std::unique_ptr asset = + assetmanager_.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); + ASSERT_NE(nullptr, asset); - ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(), - true /*copyData*/)); + ASSERT_EQ(NO_ERROR, + xml_parser_.setTo(asset->getBuffer(true), asset->getLength(), true /*copyData*/)); // Skip to the first tag. while (xml_parser_.next() != ResXMLParser::START_TAG) { @@ -65,15 +65,50 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { ResXMLTree xml_parser_; }; +TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) { + AssetManager2 assetmanager; + auto apk_assets = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/styles/styles.apk"); + ASSERT_NE(nullptr, apk_assets); + assetmanager.SetApkAssets({apk_assets.get()}); + + std::unique_ptr theme = assetmanager.NewTheme(); + + std::array attrs{ + {fix_package_id(R::attr::attr_one, 0x02), fix_package_id(R::attr::attr_two, 0x02)}}; + std::array values; + std::array indices; + ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/, + fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(), values.data(), + indices.data()); + + const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; + + const uint32_t* values_cursor = values.data(); + EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]); + EXPECT_EQ(1u, values_cursor[STYLE_DATA]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); + + values_cursor += STYLE_NUM_ENTRIES; + EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]); + EXPECT_EQ(2u, values_cursor[STYLE_DATA]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); +} + TEST_F(AttributeResolutionTest, Theme) { - ResTable::Theme theme(table_); - ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); + std::unique_ptr theme = assetmanager_.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_empty}}; std::array values; - ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/, + ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/, nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(), attrs.size(), values.data(), nullptr /*out_indices*/)); @@ -126,8 +161,8 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { R::attr::attr_four, R::attr::attr_empty}}; std::array values; - ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(), - nullptr /*out_indices*/)); + ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(), + values.data(), nullptr /*out_indices*/)); uint32_t* values_cursor = values.data(); EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]); @@ -171,15 +206,15 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { } TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) { - ResTable::Theme theme(table_); - ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); + std::unique_ptr theme = assetmanager_.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}}; std::array values; std::array indices; - ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(), + ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), attrs.size(), values.data(), indices.data()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index 7149beef797f..faddfe599af4 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -33,19 +33,21 @@ void GetResourceBenchmarkOld(const std::vector& paths, const ResTab } } + // Make sure to force creation of the ResTable first, or else the configuration doesn't get set. + const ResTable& table = assetmanager.getResources(true); if (config != nullptr) { assetmanager.setConfiguration(*config); } - const ResTable& table = assetmanager.getResources(true); - Res_value value; ResTable_config selected_config; uint32_t flags; + uint32_t last_ref = 0u; while (state.KeepRunning()) { - table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags, - &selected_config); + ssize_t block = table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags, + &selected_config); + table.resolveReference(&value, block, &last_ref, &flags, &selected_config); } } @@ -72,10 +74,12 @@ void GetResourceBenchmark(const std::vector& paths, const ResTable_ Res_value value; ResTable_config selected_config; uint32_t flags; + uint32_t last_id = 0u; while (state.KeepRunning()) { - assetmanager.GetResource(resid, false /* may_be_bag */, 0u /* density_override */, &value, - &selected_config, &flags); + ApkAssetsCookie cookie = assetmanager.GetResource( + resid, false /* may_be_bag */, 0u /* density_override */, &value, &selected_config, &flags); + assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_id); } } diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 37ddafb14fd3..cae632ddea30 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -16,6 +16,9 @@ #include "androidfw/LoadedArsc.h" +#include "android-base/file.h" +#include "androidfw/ResourceUtils.h" + #include "TestHelpers.h" #include "data/basic/R.h" #include "data/libclient/R.h" @@ -27,6 +30,14 @@ namespace basic = com::android::basic; namespace libclient = com::android::libclient; namespace sparse = com::android::sparse; +using ::android::base::ReadFileToString; +using ::testing::Eq; +using ::testing::Ge; +using ::testing::IsNull; +using ::testing::NotNull; +using ::testing::SizeIs; +using ::testing::StrEq; + namespace android { TEST(LoadedArscTest, LoadSinglePackageArsc) { @@ -35,39 +46,24 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); - - const std::vector>& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - EXPECT_EQ(std::string("com.android.app"), packages[0]->GetPackageName()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); - - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.sdkVersion = 24; + ASSERT_THAT(loaded_arsc, NotNull()); - FindEntryResult entry; - - ASSERT_TRUE(loaded_arsc->FindEntry(app::R::string::string_one, config, &entry)); - ASSERT_NE(nullptr, entry.entry); -} - -TEST(LoadedArscTest, FindDefaultEntry) { - std::string contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(app::R::string::string_one)); + ASSERT_THAT(package, NotNull()); + EXPECT_THAT(package->GetPackageName(), StrEq("com.android.app")); + EXPECT_THAT(package->GetPackageId(), Eq(0x7f)); - std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + const uint8_t type_index = get_type_id(app::R::string::string_one) - 1; + const uint16_t entry_index = get_entry_id(app::R::string::string_one); - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); - desired_config.language[0] = 'd'; - desired_config.language[1] = 'e'; + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry)); - ASSERT_NE(nullptr, entry.entry); + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); } TEST(LoadedArscTest, LoadSparseEntryApp) { @@ -76,15 +72,22 @@ TEST(LoadedArscTest, LoadSparseEntryApp) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); + + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9)); + ASSERT_THAT(package, NotNull()); + + const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1; + const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9); - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.sdkVersion = 26; + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(sparse::R::integer::foo_9, config, &entry)); - ASSERT_NE(nullptr, entry.entry); + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); } TEST(LoadedArscTest, LoadSharedLibrary) { @@ -93,14 +96,13 @@ TEST(LoadedArscTest, LoadSharedLibrary) { &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_EQ(std::string("com.android.lib_one"), packages[0]->GetPackageName()); - EXPECT_EQ(0, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.lib_one")); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0)); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); @@ -114,25 +116,23 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) { "resources.arsc", &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_FALSE(packages[0]->IsDynamic()); - EXPECT_EQ(std::string("com.android.libclient"), packages[0]->GetPackageName()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.libclient")); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); // The library has two dependencies. - ASSERT_EQ(2u, dynamic_pkg_map.size()); + ASSERT_THAT(dynamic_pkg_map, SizeIs(2u)); + EXPECT_THAT(dynamic_pkg_map[0].package_name, StrEq("com.android.lib_one")); + EXPECT_THAT(dynamic_pkg_map[0].package_id, Eq(0x02)); - EXPECT_EQ(std::string("com.android.lib_one"), dynamic_pkg_map[0].package_name); - EXPECT_EQ(0x02, dynamic_pkg_map[0].package_id); - - EXPECT_EQ(std::string("com.android.lib_two"), dynamic_pkg_map[1].package_name); - EXPECT_EQ(0x03, dynamic_pkg_map[1].package_id); + EXPECT_THAT(dynamic_pkg_map[1].package_name, StrEq("com.android.lib_two")); + EXPECT_THAT(dynamic_pkg_map[1].package_id, Eq(0x03)); } TEST(LoadedArscTest, LoadAppAsSharedLibrary) { @@ -143,13 +143,12 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, true /*load_as_shared_library*/); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); } TEST(LoadedArscTest, LoadFeatureSplit) { @@ -157,21 +156,67 @@ TEST(LoadedArscTest, LoadFeatureSplit) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc", &contents)); std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(basic::R::string::test3)); + ASSERT_THAT(package, NotNull()); - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry)); + uint8_t type_index = get_type_id(basic::R::string::test3) - 1; + uint8_t entry_index = get_entry_id(basic::R::string::test3); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); size_t len; - const char16_t* type_name16 = entry.type_string_ref.string16(&len); - ASSERT_NE(nullptr, type_name16); - ASSERT_NE(0u, len); + const char16_t* type_name16 = + package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len); + ASSERT_THAT(type_name16, NotNull()); + EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string")); - std::string type_name = util::Utf16ToUtf8(StringPiece16(type_name16, len)); - EXPECT_EQ(std::string("string"), type_name); + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull()); +} + +// AAPT(2) generates resource tables with chunks in a certain order. The rule is that +// a RES_TABLE_TYPE_TYPE with id `i` must always be preceded by a RES_TABLE_TYPE_SPEC_TYPE with +// id `i`. The RES_TABLE_TYPE_SPEC_TYPE does not need to be directly preceding, however. +// +// AAPT(2) generates something like: +// RES_TABLE_TYPE_SPEC_TYPE id=1 +// RES_TABLE_TYPE_TYPE id=1 +// RES_TABLE_TYPE_SPEC_TYPE id=2 +// RES_TABLE_TYPE_TYPE id=2 +// +// But the following is valid too: +// RES_TABLE_TYPE_SPEC_TYPE id=1 +// RES_TABLE_TYPE_SPEC_TYPE id=2 +// RES_TABLE_TYPE_TYPE id=1 +// RES_TABLE_TYPE_TYPE id=2 +// +TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) { + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/out_of_order_types/out_of_order_types.apk", + "resources.arsc", &contents)); + + std::unique_ptr loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + ASSERT_THAT(loaded_arsc, NotNull()); + + ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); + const auto& package = loaded_arsc->GetPackages()[0]; + ASSERT_THAT(package, NotNull()); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); + + type_spec = package->GetTypeSpecByTypeIndex(1); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); } class MockLoadedIdmap : public LoadedIdmap { @@ -199,23 +244,33 @@ class MockLoadedIdmap : public LoadedIdmap { }; TEST(LoadedArscTest, LoadOverlay) { - std::string contents, overlay_contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); + std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc", - &overlay_contents)); + &contents)); MockLoadedIdmap loaded_idmap; std::unique_ptr loaded_arsc = - LoadedArsc::Load(StringPiece(overlay_contents), &loaded_idmap); - ASSERT_NE(nullptr, loaded_arsc); - - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); - - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(0x08030001u, desired_config, &entry)); + LoadedArsc::Load(StringPiece(contents), &loaded_idmap); + ASSERT_THAT(loaded_arsc, NotNull()); + + const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u); + ASSERT_THAT(package, NotNull()); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); + + // The entry being overlaid doesn't exist at the original entry index. + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull()); + + // Since this is an overlay, the actual entry ID must be mapped. + ASSERT_THAT(type_spec->idmap_entries, NotNull()); + uint16_t target_entry_id = 0u; + ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id)); + ASSERT_THAT(target_entry_id, Eq(0x0u)); + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull()); } // structs with size fields (like Res_value, ResTable_entry) should be diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h index 43a995536d89..df0c642f4565 100644 --- a/libs/androidfw/tests/TestHelpers.h +++ b/libs/androidfw/tests/TestHelpers.h @@ -20,6 +20,7 @@ #include #include "androidfw/ResourceTypes.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "CommonHelpers.h" diff --git a/libs/androidfw/tests/data/app/app.apk b/libs/androidfw/tests/data/app/app.apk index ccb08242a656..c8ad86ded851 100644 Binary files a/libs/androidfw/tests/data/app/app.apk and b/libs/androidfw/tests/data/app/app.apk differ diff --git a/libs/androidfw/tests/data/app/assets/app_file.txt b/libs/androidfw/tests/data/app/assets/app_file.txt new file mode 100644 index 000000000000..b214e06d6ece --- /dev/null +++ b/libs/androidfw/tests/data/app/assets/app_file.txt @@ -0,0 +1 @@ +app file diff --git a/libs/androidfw/tests/data/app/assets/file.txt b/libs/androidfw/tests/data/app/assets/file.txt new file mode 100644 index 000000000000..081154272520 --- /dev/null +++ b/libs/androidfw/tests/data/app/assets/file.txt @@ -0,0 +1 @@ +app override file diff --git a/libs/androidfw/tests/data/app/build b/libs/androidfw/tests/data/app/build index d418158c547b..09af842e70fb 100755 --- a/libs/androidfw/tests/data/app/build +++ b/libs/androidfw/tests/data/app/build @@ -17,4 +17,11 @@ set -e -aapt package -I ../system/system.apk -M AndroidManifest.xml -S res -F app.apk -f +aapt2 compile --dir res -o compiled.flata +aapt2 link \ + --manifest AndroidManifest.xml \ + -I ../system/system.apk \ + -A assets \ + -o app.apk \ + compiled.flata +rm compiled.flata diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h index 94a2a14ced87..b7e814fea079 100644 --- a/libs/androidfw/tests/data/basic/R.h +++ b/libs/androidfw/tests/data/basic/R.h @@ -34,6 +34,7 @@ struct R { struct layout { enum : uint32_t { main = 0x7f020000, + layoutt = 0x7f020001, }; }; @@ -55,6 +56,7 @@ struct R { number2 = 0x7f040001, ref1 = 0x7f040002, ref2 = 0x7f040003, + deep_ref = 0x7f040004, // From feature number3 = 0x80030000, diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk index 18ef75e91ded..1733b6a16546 100644 Binary files a/libs/androidfw/tests/data/basic/basic.apk and b/libs/androidfw/tests/data/basic/basic.apk differ diff --git a/libs/androidfw/tests/data/basic/res/layout/layout.xml b/libs/androidfw/tests/data/basic/res/layout/layout.xml new file mode 100644 index 000000000000..045ede454bca --- /dev/null +++ b/libs/androidfw/tests/data/basic/res/layout/layout.xml @@ -0,0 +1,25 @@ + + +