Merge changes Ia6c335db,Id8079104
* changes: AssetManager2: Provide a method for iterating over packages AssetManager2: Implement IDMAP support
This commit is contained in:
committed by
Android (Google) Code Review
commit
8a0f0ed4d8
@@ -43,6 +43,7 @@ cc_library {
|
||||
"AssetManager2.cpp",
|
||||
"AttributeResolution.cpp",
|
||||
"ChunkIterator.cpp",
|
||||
"Idmap.cpp",
|
||||
"LoadedArsc.cpp",
|
||||
"LocaleData.cpp",
|
||||
"misc.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::LoadImpl(const std::string& path, bool system,
|
||||
bool 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, 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 {};
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
190
libs/androidfw/Idmap.cpp
Normal file
190
libs/androidfw/Idmap.cpp
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
ApkAssets();
|
||||
// Creates an Asset from any file on the file system.
|
||||
static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
|
||||
|
||||
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_;
|
||||
};
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -227,6 +232,14 @@ class AssetManager2 {
|
||||
// Creates a new Theme from this AssetManager.
|
||||
std::unique_ptr<Theme> NewTheme();
|
||||
|
||||
template <typename Func>
|
||||
void ForEachPackage(Func func) {
|
||||
for (const PackageGroup& package_group : package_groups_) {
|
||||
func(package_group.packages_.front()->GetPackageName(),
|
||||
package_group.dynamic_ref_table.mAssignedPackageId);
|
||||
}
|
||||
}
|
||||
|
||||
void DumpToLog() const;
|
||||
|
||||
private:
|
||||
|
||||
76
libs/androidfw/include/androidfw/Idmap.h
Normal file
76
libs/androidfw/include/androidfw/Idmap.h
Normal file
@@ -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_
|
||||
@@ -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_;
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "TestHelpers.h"
|
||||
#include "data/basic/R.h"
|
||||
|
||||
using com::android::basic::R;
|
||||
using ::com::android::basic::R;
|
||||
|
||||
namespace android {
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user