diff options
| -rw-r--r-- | libs/androidfw/Android.bp | 1 | ||||
| -rw-r--r-- | libs/androidfw/ApkAssets.cpp | 97 | ||||
| -rw-r--r-- | libs/androidfw/AssetManager2.cpp | 13 | ||||
| -rw-r--r-- | libs/androidfw/Idmap.cpp | 190 | ||||
| -rw-r--r-- | libs/androidfw/LoadedArsc.cpp | 102 | ||||
| -rw-r--r-- | libs/androidfw/include/androidfw/ApkAssets.h | 39 | ||||
| -rw-r--r-- | libs/androidfw/include/androidfw/AssetManager2.h | 13 | ||||
| -rw-r--r-- | libs/androidfw/include/androidfw/Idmap.h | 76 | ||||
| -rw-r--r-- | libs/androidfw/include/androidfw/LoadedArsc.h | 49 | ||||
| -rw-r--r-- | libs/androidfw/include/androidfw/ResourceTypes.h | 27 | ||||
| -rw-r--r-- | libs/androidfw/tests/ApkAssets_test.cpp | 35 | ||||
| -rw-r--r-- | libs/androidfw/tests/Idmap_test.cpp | 2 | ||||
| -rw-r--r-- | libs/androidfw/tests/LoadedArsc_test.cpp | 68 |
13 files changed, 633 insertions, 79 deletions
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 987096fd6658..5484cf048468 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -43,6 +43,7 @@ cc_library { "AssetManager2.cpp", "AttributeResolution.cpp", "ChunkIterator.cpp", + "Idmap.cpp", "LoadedArsc.cpp", "LocaleData.cpp", "misc.cpp", diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 0e577d1c9e3c..158df136534a 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -20,64 +20,126 @@ #include <algorithm> +#include "android-base/errors.h" +#include "android-base/file.h" #include "android-base/logging.h" +#include "android-base/unique_fd.h" +#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" +#include "androidfw/Idmap.h" +#include "androidfw/ResourceTypes.h" #include "androidfw/Util.h" namespace android { -ApkAssets::ApkAssets() : zip_handle_(nullptr, ::CloseArchive) {} +using base::SystemErrorCodeToString; +using base::unique_fd; + +static const std::string kResourcesArsc("resources.arsc"); + +ApkAssets::ApkAssets(void* unmanaged_handle, const std::string& path) + : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path) { +} std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) { - return ApkAssets::LoadImpl(path, system, false /*load_as_shared_library*/); + return ApkAssets::LoadImpl(path, nullptr, nullptr, system, false /*load_as_shared_library*/); } std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path, bool system) { - return ApkAssets::LoadImpl(path, system, true /*load_as_shared_library*/); + return ApkAssets::LoadImpl(path, nullptr, nullptr, system, true /*load_as_shared_library*/); +} + +std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path, + bool system) { + std::unique_ptr<Asset> idmap_asset = CreateAssetFromFile(idmap_path); + if (idmap_asset == nullptr) { + return {}; + } + + const StringPiece idmap_data( + reinterpret_cast<const char*>(idmap_asset->getBuffer(true /*wordAligned*/)), + static_cast<size_t>(idmap_asset->getLength())); + std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(idmap_data); + if (loaded_idmap == nullptr) { + LOG(ERROR) << "failed to load IDMAP " << idmap_path; + return {}; + } + return LoadImpl(loaded_idmap->OverlayApkPath(), std::move(idmap_asset), std::move(loaded_idmap), + system, false /*load_as_shared_library*/); +} + +std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) { + unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC)); + if (fd == -1) { + LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno); + return {}; + } + + const off64_t file_len = lseek64(fd, 0, SEEK_END); + if (file_len < 0) { + LOG(ERROR) << "Failed to get size of file '" << path << "': " << SystemErrorCodeToString(errno); + return {}; + } + + std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>(); + if (!file_map->create(path.c_str(), fd, 0, static_cast<size_t>(file_len), true /*readOnly*/)) { + LOG(ERROR) << "Failed to mmap file '" << path << "': " << SystemErrorCodeToString(errno); + return {}; + } + return Asset::createFromUncompressedMap(std::move(file_map), Asset::AccessMode::ACCESS_RANDOM); } -std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(const std::string& path, bool system, - bool load_as_shared_library) { +std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl( + const std::string& path, std::unique_ptr<Asset> idmap_asset, + std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library) { ATRACE_CALL(); ::ZipArchiveHandle unmanaged_handle; int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle); if (result != 0) { - LOG(ERROR) << ::ErrorCodeString(result); + LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); return {}; } // Wrap the handle in a unique_ptr so it gets automatically closed. - std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets()); - loaded_apk->zip_handle_.reset(unmanaged_handle); + std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(unmanaged_handle, path)); - ::ZipString entry_name("resources.arsc"); + // Find the resource table. + ::ZipString entry_name(kResourcesArsc.c_str()); ::ZipEntry entry; result = ::FindEntry(loaded_apk->zip_handle_.get(), entry_name, &entry); if (result != 0) { - LOG(ERROR) << ::ErrorCodeString(result); - return {}; + // There is no resources.arsc, so create an empty LoadedArsc and return. + loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty(); + return std::move(loaded_apk); } if (entry.method == kCompressDeflated) { - LOG(WARNING) << "resources.arsc is compressed."; + LOG(WARNING) << kResourcesArsc << " in APK '" << path << "' is compressed."; } - loaded_apk->path_ = path; - loaded_apk->resources_asset_ = - loaded_apk->Open("resources.arsc", Asset::AccessMode::ACCESS_BUFFER); + // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open. + loaded_apk->resources_asset_ = loaded_apk->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER); if (loaded_apk->resources_asset_ == nullptr) { + LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'."; return {}; } + // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid. + loaded_apk->idmap_asset_ = std::move(idmap_asset); + + const StringPiece data( + reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), + loaded_apk->resources_asset_->getLength()); loaded_apk->loaded_arsc_ = - LoadedArsc::Load(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/), - loaded_apk->resources_asset_->getLength(), system, load_as_shared_library); + LoadedArsc::Load(data, loaded_idmap.get(), system, load_as_shared_library); if (loaded_apk->loaded_arsc_ == nullptr) { + LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'."; return {}; } @@ -93,7 +155,6 @@ std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMod ::ZipEntry entry; int32_t result = ::FindEntry(zip_handle_.get(), name, &entry); if (result != 0) { - LOG(ERROR) << "No entry '" << path << "' found in APK '" << path_ << "'"; return {}; } diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index f1f2e2d1417e..d4d9dcbafa5b 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -35,7 +35,9 @@ namespace android { -AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } +AssetManager2::AssetManager2() { + memset(&configuration_, 0, sizeof(configuration_)); +} bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches) { @@ -55,9 +57,9 @@ void AssetManager2::BuildDynamicRefTable() { int next_package_id = 0x02; const size_t apk_assets_count = apk_assets_.size(); for (size_t i = 0; i < apk_assets_count; i++) { - const ApkAssets* apk_asset = apk_assets_[i]; - for (const std::unique_ptr<const LoadedPackage>& package : - apk_asset->GetLoadedArsc()->GetPackages()) { + const LoadedArsc* loaded_arsc = apk_assets_[i]->GetLoadedArsc(); + + for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) { // Get the package ID or assign one if a shared library. int package_id; if (package->IsDynamic()) { @@ -312,7 +314,8 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri cumulated_flags |= current_flags; - if (best_cookie == kInvalidCookie || current_config.isBetterThan(best_config, desired_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]; diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp new file mode 100644 index 000000000000..7c1ee5cd7cfa --- /dev/null +++ b/libs/androidfw/Idmap.cpp @@ -0,0 +1,190 @@ +/* + * 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 "androidfw/Idmap.h" + +#include "android-base/logging.h" +#include "android-base/stringprintf.h" +#include "utils/ByteOrder.h" +#include "utils/Trace.h" + +#ifdef _WIN32 +#ifdef ERROR +#undef ERROR +#endif +#endif + +#include "androidfw/ResourceTypes.h" + +using ::android::base::StringPrintf; + +namespace android { + +constexpr static inline bool is_valid_package_id(uint16_t id) { + return id != 0 && id <= 255; +} + +constexpr static inline bool is_valid_type_id(uint16_t id) { + // Type IDs and package IDs have the same constraints in the IDMAP. + return is_valid_package_id(id); +} + +bool LoadedIdmap::Lookup(const IdmapEntry_header* header, uint16_t input_entry_id, + uint16_t* output_entry_id) { + if (input_entry_id < dtohs(header->entry_id_offset)) { + // After applying the offset, the entry is not present. + return false; + } + + input_entry_id -= dtohs(header->entry_id_offset); + if (input_entry_id >= dtohs(header->entry_count)) { + // The entry is not present. + return false; + } + + uint32_t result = dtohl(header->entries[input_entry_id]); + if (result == 0xffffffffu) { + return false; + } + *output_entry_id = static_cast<uint16_t>(result); + return true; +} + +static bool is_word_aligned(const void* data) { + return (reinterpret_cast<uintptr_t>(data) & 0x03) == 0; +} + +static bool IsValidIdmapHeader(const StringPiece& data) { + if (!is_word_aligned(data.data())) { + LOG(ERROR) << "Idmap header is not word aligned."; + return false; + } + + if (data.size() < sizeof(Idmap_header)) { + LOG(ERROR) << "Idmap header is too small."; + return false; + } + + const Idmap_header* header = reinterpret_cast<const Idmap_header*>(data.data()); + if (dtohl(header->magic) != kIdmapMagic) { + LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)", + dtohl(header->magic), kIdmapMagic); + return false; + } + + if (dtohl(header->version) != kIdmapCurrentVersion) { + // We are strict about versions because files with this format are auto-generated and don't need + // backwards compatibility. + LOG(ERROR) << StringPrintf("Version mismatch in Idmap (was 0x%08x, expected 0x%08x)", + dtohl(header->version), kIdmapCurrentVersion); + return false; + } + + if (!is_valid_package_id(dtohs(header->target_package_id))) { + LOG(ERROR) << StringPrintf("Target package ID in Idmap is invalid: 0x%02x", + dtohs(header->target_package_id)); + return false; + } + + if (dtohs(header->type_count) > 255) { + LOG(ERROR) << StringPrintf("Idmap has too many type mappings (was %d, max 255)", + (int)dtohs(header->type_count)); + return false; + } + return true; +} + +LoadedIdmap::LoadedIdmap(const Idmap_header* header) : header_(header) { + size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path), + arraysize(header_->overlay_path)); + overlay_apk_path_.assign(reinterpret_cast<const char*>(header_->overlay_path), length); +} + +std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_data) { + ATRACE_CALL(); + if (!IsValidIdmapHeader(idmap_data)) { + return {}; + } + + const Idmap_header* header = reinterpret_cast<const Idmap_header*>(idmap_data.data()); + + // Can't use make_unique because LoadedImpl constructor is private. + std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(new LoadedIdmap(header)); + + const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + sizeof(*header); + size_t data_size = idmap_data.size() - sizeof(*header); + + size_t type_maps_encountered = 0u; + while (data_size >= sizeof(IdmapEntry_header)) { + if (!is_word_aligned(data_ptr)) { + LOG(ERROR) << "Type mapping in Idmap is not word aligned"; + return {}; + } + + // Validate the type IDs. + const IdmapEntry_header* entry_header = reinterpret_cast<const IdmapEntry_header*>(data_ptr); + if (!is_valid_type_id(dtohs(entry_header->target_type_id)) || !is_valid_type_id(dtohs(entry_header->overlay_type_id))) { + LOG(ERROR) << StringPrintf("Invalid type map (0x%02x -> 0x%02x)", + dtohs(entry_header->target_type_id), + dtohs(entry_header->overlay_type_id)); + return {}; + } + + // Make sure there is enough space for the entries declared in the header. + if ((data_size - sizeof(*entry_header)) / sizeof(uint32_t) < + static_cast<size_t>(dtohs(entry_header->entry_count))) { + LOG(ERROR) << StringPrintf("Idmap too small for the number of entries (%d)", + (int)dtohs(entry_header->entry_count)); + return {}; + } + + // Only add a non-empty overlay. + if (dtohs(entry_header->entry_count != 0)) { + loaded_idmap->type_map_[static_cast<uint8_t>(dtohs(entry_header->overlay_type_id))] = + entry_header; + } + + const size_t entry_size_bytes = + sizeof(*entry_header) + (dtohs(entry_header->entry_count) * sizeof(uint32_t)); + data_ptr += entry_size_bytes; + data_size -= entry_size_bytes; + type_maps_encountered++; + } + + // Verify that we parsed all the type maps. + if (type_maps_encountered != static_cast<size_t>(dtohs(header->type_count))) { + LOG(ERROR) << "Parsed " << type_maps_encountered << " type maps but expected " + << (int)dtohs(header->type_count); + return {}; + } + return std::move(loaded_idmap); +} + +uint8_t LoadedIdmap::TargetPackageId() const { + return static_cast<uint8_t>(dtohs(header_->target_package_id)); +} + +const IdmapEntry_header* LoadedIdmap::GetEntryMapForType(uint8_t type_id) const { + auto iter = type_map_.find(type_id); + if (iter != type_map_.end()) { + return iter->second; + } + return nullptr; +} + +} // namespace android 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<Type> 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<const uint8_t*>(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<const uint8_t*>(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> LoadedPackage::Load(const Chunk& chunk) { +std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk, + const LoadedIdmap* loaded_idmap) { ATRACE_CALL(); std::unique_ptr<LoadedPackage> 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<ResTable_package, kMinPackageSize>(); @@ -430,6 +452,12 @@ std::unique_ptr<LoadedPackage> 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<uint8_t>::max()) { @@ -490,7 +518,16 @@ std::unique_ptr<LoadedPackage> 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> LoadedPackage::Load(const Chunk& chunk) { } last_type_idx = type_spec->id - 1; - types_builder = util::make_unique<TypeSpecPtrBuilder>(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<TypeSpecPtrBuilder>(type_spec, idmap_entry_header); } break; case RES_TABLE_TYPE_TYPE: { @@ -608,7 +653,16 @@ std::unique_ptr<LoadedPackage> 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> 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<ResTable_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<LoadedPackage> loaded_package = LoadedPackage::Load(child_chunk); + std::unique_ptr<LoadedPackage> 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<const LoadedArsc> LoadedArsc::Load(const void* data, size_t len, bool system, +std::unique_ptr<const LoadedArsc> 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<const LoadedArsc> LoadedArsc::Load(const void* data, size_t len, std::unique_ptr<LoadedArsc> 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<const LoadedArsc> LoadedArsc::Load(const void* data, size_t len, return std::move(loaded_arsc); } +std::unique_ptr<const LoadedArsc> LoadedArsc::CreateEmpty() { + return std::unique_ptr<LoadedArsc>(new LoadedArsc()); +} + } // namespace android diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h index 2e392d57b1c8..3a307fc9ea0a 100644 --- a/libs/androidfw/include/androidfw/ApkAssets.h +++ b/libs/androidfw/include/androidfw/ApkAssets.h @@ -28,36 +28,63 @@ namespace android { +class LoadedIdmap; + // Holds an APK. class ApkAssets { public: + // Creates an ApkAssets. + // If `system` is true, the package is marked as a system package, and allows some functions to + // filter out this package when computing what configurations/resources are available. static std::unique_ptr<const ApkAssets> Load(const std::string& path, bool system = false); + + // Creates an ApkAssets, but forces any package with ID 0x7f to be loaded as a shared library. + // If `system` is true, the package is marked as a system package, and allows some functions to + // filter out this package when computing what configurations/resources are available. static std::unique_ptr<const ApkAssets> LoadAsSharedLibrary(const std::string& path, bool system = false); + // Creates an ApkAssets from an IDMAP, which contains the original APK path, and the overlay + // data. + // If `system` is true, the package is marked as a system package, and allows some functions to + // filter out this package when computing what configurations/resources are available. + static std::unique_ptr<const ApkAssets> LoadOverlay(const std::string& idmap_path, + bool system = false); + std::unique_ptr<Asset> Open(const std::string& path, Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const; bool ForEachFile(const std::string& path, const std::function<void(const StringPiece&, FileType)>& f) const; - inline const std::string& GetPath() const { return path_; } + inline const std::string& GetPath() const { + return path_; + } - inline const LoadedArsc* GetLoadedArsc() const { return loaded_arsc_.get(); } + // This is never nullptr. + inline const LoadedArsc* GetLoadedArsc() const { + return loaded_arsc_.get(); + } private: DISALLOW_COPY_AND_ASSIGN(ApkAssets); - static std::unique_ptr<const ApkAssets> LoadImpl(const std::string& path, bool system, - bool load_as_shared_library); + static std::unique_ptr<const ApkAssets> LoadImpl(const std::string& path, + std::unique_ptr<Asset> idmap_asset, + std::unique_ptr<const LoadedIdmap> loaded_idmap, + bool system, bool load_as_shared_library); + + // Creates an Asset from any file on the file system. + static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path); - ApkAssets(); + ApkAssets(void* unmanaged_handle, const std::string& path); using ZipArchivePtr = std::unique_ptr<void, void(*)(void*)>; ZipArchivePtr zip_handle_; - std::string path_; + const std::string path_; std::unique_ptr<Asset> resources_asset_; + std::unique_ptr<Asset> idmap_asset_; std::unique_ptr<const LoadedArsc> loaded_arsc_; }; diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index fd94144544a8..ef40a09d2e03 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -96,24 +96,29 @@ class AssetManager2 { // new resource IDs. bool SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches = true); - inline const std::vector<const ApkAssets*> GetApkAssets() const { return apk_assets_; } + inline const std::vector<const ApkAssets*> GetApkAssets() const { + return apk_assets_; + } // Returns the string pool for the given asset cookie. - // Use the string pool returned here with a valid Res_value object of - // type Res_value::TYPE_STRING. + // Use the string pool returned here with a valid Res_value object of type Res_value::TYPE_STRING. const ResStringPool* GetStringPoolForCookie(ApkAssetsCookie cookie) const; // Returns the DynamicRefTable for the given package ID. + // This may be nullptr if the APK represented by `cookie` has no resource table. const DynamicRefTable* GetDynamicRefTableForPackage(uint32_t package_id) const; // Returns the DynamicRefTable for the ApkAssets represented by the cookie. + // This may be nullptr if the APK represented by `cookie` has no resource table. const DynamicRefTable* GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const; // Sets/resets the configuration for this AssetManager. This will cause all // caches that are related to the configuration change to be invalidated. void SetConfiguration(const ResTable_config& configuration); - inline const ResTable_config& GetConfiguration() const { return configuration_; } + inline const ResTable_config& GetConfiguration() const { + return configuration_; + } // Returns all configurations for which there are resources defined. This includes resource // configurations in all the ApkAssets set for this AssetManager. diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h new file mode 100644 index 000000000000..fd02e6f63b74 --- /dev/null +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -0,0 +1,76 @@ +/* + * 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 IDMAP_H_ +#define IDMAP_H_ + +#include <memory> +#include <string> +#include <unordered_map> + +#include "android-base/macros.h" + +#include "androidfw/StringPiece.h" + +namespace android { + +struct Idmap_header; +struct IdmapEntry_header; + +// Represents a loaded/parsed IDMAP for a Runtime Resource Overlay (RRO). +// An RRO and its target APK have different resource IDs assigned to their resources. Overlaying +// a resource is done by resource name. An IDMAP is a generated mapping between the resource IDs +// of the RRO and the target APK for each resource with the same name. +// A LoadedIdmap can be set alongside the overlay's LoadedArsc to allow the overlay ApkAssets to +// masquerade as the target ApkAssets resources. +class LoadedIdmap { + public: + // Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed. + static std::unique_ptr<const LoadedIdmap> Load(const StringPiece& idmap_data); + + // Performs a lookup of the expected entry ID for the given IDMAP entry header. + // Returns true if the mapping exists and fills `output_entry_id` with the result. + static bool Lookup(const IdmapEntry_header* header, uint16_t input_entry_id, + uint16_t* output_entry_id); + + // Returns the package ID for which this overlay should apply. + uint8_t TargetPackageId() const; + + // Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated. + inline const std::string& OverlayApkPath() const { + return overlay_apk_path_; + } + + // Returns the mapping of target entry ID to overlay entry ID for the given target type. + const IdmapEntry_header* GetEntryMapForType(uint8_t type_id) const; + + protected: + // Exposed as protected so that tests can subclass and mock this class out. + LoadedIdmap() = default; + + const Idmap_header* header_ = nullptr; + std::string overlay_apk_path_; + std::unordered_map<uint8_t, const IdmapEntry_header*> type_map_; + + private: + DISALLOW_COPY_AND_ASSIGN(LoadedIdmap); + + explicit LoadedIdmap(const Idmap_header* header); +}; + +} // namespace android + +#endif // IDMAP_H_ diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index f30b158084eb..1f272e8f84e9 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -25,6 +25,7 @@ #include "androidfw/ByteBucketArray.h" #include "androidfw/Chunk.h" +#include "androidfw/Idmap.h" #include "androidfw/ResourceTypes.h" #include "androidfw/Util.h" @@ -70,20 +71,36 @@ class LoadedPackage { uint32_t* out_flags) const; // Returns the string pool where type names are stored. - inline const ResStringPool* GetTypeStringPool() const { return &type_string_pool_; } + inline const ResStringPool* GetTypeStringPool() const { + return &type_string_pool_; + } // Returns the string pool where the names of resource entries are stored. - inline const ResStringPool* GetKeyStringPool() const { return &key_string_pool_; } + inline const ResStringPool* GetKeyStringPool() const { + return &key_string_pool_; + } - inline const std::string& GetPackageName() const { return package_name_; } + inline const std::string& GetPackageName() const { + return package_name_; + } - inline int GetPackageId() const { return package_id_; } + inline int GetPackageId() const { + return package_id_; + } // Returns true if this package is dynamic (shared library) and needs to have an ID assigned. - inline bool IsDynamic() const { return dynamic_; } + inline bool IsDynamic() const { + return dynamic_; + } // Returns true if this package originates from a system provided resource. - inline bool IsSystem() const { return system_; } + inline bool IsSystem() const { + return system_; + } + + inline bool IsOverlay() const { + return overlay_; + } // 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 @@ -111,7 +128,7 @@ class LoadedPackage { private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); - static std::unique_ptr<LoadedPackage> Load(const Chunk& chunk); + static std::unique_ptr<LoadedPackage> Load(const Chunk& chunk, const LoadedIdmap* loaded_idmap); LoadedPackage() = default; @@ -122,6 +139,7 @@ class LoadedPackage { int type_id_offset_ = 0; bool dynamic_ = false; bool system_ = false; + bool overlay_ = false; ByteBucketArray<util::unique_cptr<TypeSpec>> type_specs_; std::vector<DynamicPackageEntry> dynamic_package_map_; @@ -137,14 +155,21 @@ class LoadedArsc { // If `load_as_shared_library` is set to true, the application package (0x7f) is treated // as a shared library (0x00). When loaded into an AssetManager, the package will be assigned an // ID. - static std::unique_ptr<const LoadedArsc> Load(const void* data, size_t len, bool system = false, + static std::unique_ptr<const LoadedArsc> Load(const StringPiece& data, + const LoadedIdmap* loaded_idmap = nullptr, + bool system = false, bool load_as_shared_library = false); + // Create an empty LoadedArsc. This is used when an APK has no resources.arsc. + static std::unique_ptr<const LoadedArsc> 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 { return &global_string_pool_; } + inline const ResStringPool* GetStringPool() const { + 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. @@ -157,7 +182,9 @@ class LoadedArsc { const LoadedPackage* GetPackageForId(uint32_t resid) const; // Returns true if this is a system provided resource. - inline bool IsSystem() const { return system_; } + inline bool IsSystem() const { + return system_; + } // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. inline const std::vector<std::unique_ptr<const LoadedPackage>>& GetPackages() const { @@ -168,7 +195,7 @@ class LoadedArsc { DISALLOW_COPY_AND_ASSIGN(LoadedArsc); LoadedArsc() = default; - bool LoadTable(const Chunk& chunk, bool load_as_shared_library); + bool LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library); ResStringPool global_string_pool_; std::vector<std::unique_ptr<const LoadedPackage>> packages_; diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 66c66c251d9b..8f858b6fd7fd 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -38,6 +38,9 @@ namespace android { +constexpr const static uint32_t kIdmapMagic = 0x504D4449u; +constexpr const static uint32_t kIdmapCurrentVersion = 0x00000001u; + /** * In C++11, char16_t is defined as *at least* 16 bits. We do a lot of * casting on raw data and expect char16_t to be exactly 16 bits. @@ -1583,6 +1586,30 @@ struct ResTable_lib_entry uint16_t packageName[128]; }; +struct alignas(uint32_t) Idmap_header { + // Always 0x504D4449 ('IDMP') + uint32_t magic; + + uint32_t version; + + uint32_t target_crc32; + uint32_t overlay_crc32; + + uint8_t target_path[256]; + uint8_t overlay_path[256]; + + uint16_t target_package_id; + uint16_t type_count; +} __attribute__((packed)); + +struct alignas(uint32_t) IdmapEntry_header { + uint16_t target_type_id; + uint16_t overlay_type_id; + uint16_t entry_count; + uint16_t entry_id_offset; + uint32_t entries[0]; +} __attribute__((packed)); + class AssetManager2; /** diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index c85b0b98ca5e..2766ce127cf0 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -17,12 +17,14 @@ #include "androidfw/ApkAssets.h" #include "android-base/file.h" +#include "android-base/test_utils.h" #include "android-base/unique_fd.h" +#include "androidfw/Util.h" #include "TestHelpers.h" #include "data/basic/R.h" -using com::android::basic::R; +using ::com::android::basic::R; namespace android { @@ -54,6 +56,37 @@ TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic()); } +TEST(ApkAssetsTest, LoadApkWithIdmap) { + std::string contents; + 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*/)); + + 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*/)); + + util::unique_cptr<void> 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)); + idmap_data.reset(temp_data); + + TemporaryFile tf; + ASSERT_TRUE(base::WriteFully(tf.fd, idmap_data.get(), idmap_len)); + close(tf.fd); + + // Open something so that the destructor of TemporaryFile closes a valid fd. + tf.fd = open("/dev/null", O_WRONLY); + + std::unique_ptr<const ApkAssets> loaded_overlay_apk = ApkAssets::LoadOverlay(tf.path); + ASSERT_NE(nullptr, loaded_overlay_apk); +} + TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp index d12be184745c..9eb4a13f34d1 100644 --- a/libs/androidfw/tests/Idmap_test.cpp +++ b/libs/androidfw/tests/Idmap_test.cpp @@ -22,7 +22,7 @@ #include "TestHelpers.h" #include "data/basic/R.h" -using com::android::basic::R; +using ::com::android::basic::R; namespace android { diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 756869f6377d..2b72d146f83a 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -32,8 +32,7 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(contents.data(), contents.size()); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); ASSERT_NE(nullptr, loaded_arsc); const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages(); @@ -59,8 +58,7 @@ TEST(LoadedArscTest, FindDefaultEntry) { ASSERT_TRUE( ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(contents.data(), contents.size()); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); ASSERT_NE(nullptr, loaded_arsc); ResTable_config desired_config; @@ -82,8 +80,7 @@ TEST(LoadedArscTest, LoadSharedLibrary) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(contents.data(), contents.size()); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); @@ -104,8 +101,7 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/libclient/libclient.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(contents.data(), contents.size()); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); @@ -132,8 +128,9 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load( - contents.data(), contents.size(), false /*system*/, true /*load_as_shared_library*/); + std::unique_ptr<const LoadedArsc> loaded_arsc = + LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, + true /*load_as_shared_library*/); ASSERT_NE(nullptr, loaded_arsc); const auto& packages = loaded_arsc->GetPackages(); @@ -147,8 +144,7 @@ TEST(LoadedArscTest, LoadFeatureSplit) { std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(contents.data(), contents.size()); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); ASSERT_NE(nullptr, loaded_arsc); ResTable_config desired_config; @@ -174,6 +170,54 @@ TEST(LoadedArscTest, LoadFeatureSplit) { EXPECT_EQ(std::string("string"), type_name); } +class MockLoadedIdmap : public LoadedIdmap { + public: + MockLoadedIdmap() : LoadedIdmap() { + local_header_.magic = kIdmapMagic; + local_header_.version = kIdmapCurrentVersion; + local_header_.target_package_id = 0x08; + local_header_.type_count = 1; + header_ = &local_header_; + + entry_header = util::unique_cptr<IdmapEntry_header>( + (IdmapEntry_header*)::malloc(sizeof(IdmapEntry_header) + sizeof(uint32_t))); + entry_header->target_type_id = 0x03; + entry_header->overlay_type_id = 0x02; + entry_header->entry_id_offset = 1; + entry_header->entry_count = 1; + entry_header->entries[0] = 0x00000000u; + type_map_[entry_header->overlay_type_id] = entry_header.get(); + } + + private: + Idmap_header local_header_; + util::unique_cptr<IdmapEntry_header> entry_header; +}; + +TEST(LoadedArscTest, LoadOverlay) { + std::string contents, overlay_contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc", + &overlay_contents)); + + MockLoadedIdmap loaded_idmap; + + std::unique_ptr<const LoadedArsc> 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)); + + LoadedArscEntry entry; + ResTable_config selected_config; + uint32_t flags; + + ASSERT_TRUE( + loaded_arsc->FindEntry(0x08030001u, desired_config, &entry, &selected_config, &flags)); +} + // structs with size fields (like Res_value, ResTable_entry) should be // backwards and forwards compatible (aka checking the size field against // sizeof(Res_value) might not be backwards compatible. |