2017-09-25 13:21:55 -07:00
|
|
|
/*
|
|
|
|
* 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"
|
2020-03-26 17:15:01 -07:00
|
|
|
#include "androidfw/misc.h"
|
2019-07-01 09:48:23 -07:00
|
|
|
#include "androidfw/ResourceTypes.h"
|
|
|
|
#include "androidfw/Util.h"
|
2017-09-25 13:21:55 -07:00
|
|
|
#include "utils/ByteOrder.h"
|
|
|
|
#include "utils/Trace.h"
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
#ifdef ERROR
|
|
|
|
#undef ERROR
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
using ::android::base::StringPrintf;
|
|
|
|
|
|
|
|
namespace android {
|
|
|
|
|
2020-12-03 15:41:42 -08:00
|
|
|
// See frameworks/base/cmds/idmap2/include/idmap2/Idmap.h for full idmap file format specification.
|
|
|
|
struct Idmap_header {
|
|
|
|
// Always 0x504D4449 ('IDMP')
|
|
|
|
uint32_t magic;
|
|
|
|
uint32_t version;
|
2017-09-25 13:21:55 -07:00
|
|
|
|
2020-12-03 15:41:42 -08:00
|
|
|
uint32_t target_crc32;
|
|
|
|
uint32_t overlay_crc32;
|
|
|
|
|
|
|
|
uint32_t fulfilled_policies;
|
|
|
|
uint32_t enforce_overlayable;
|
|
|
|
|
|
|
|
// overlay_path, target_path, and other string values encoded in the idmap header and read and
|
|
|
|
// stored in separate structures. This allows the idmap header data to be casted to this struct
|
|
|
|
// without having to read/store each header entry separately.
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Idmap_data_header {
|
|
|
|
uint32_t target_entry_count;
|
|
|
|
uint32_t target_inline_entry_count;
|
|
|
|
uint32_t overlay_entry_count;
|
|
|
|
|
|
|
|
uint32_t string_pool_index_offset;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Idmap_target_entry {
|
|
|
|
uint32_t target_id;
|
|
|
|
uint32_t overlay_id;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Idmap_target_entry_inline {
|
|
|
|
uint32_t target_id;
|
|
|
|
Res_value value;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Idmap_overlay_entry {
|
|
|
|
uint32_t overlay_id;
|
|
|
|
uint32_t target_id;
|
|
|
|
};
|
2019-10-11 08:32:04 +02:00
|
|
|
|
2019-07-01 09:48:23 -07:00
|
|
|
OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
|
2019-11-12 16:22:04 -08:00
|
|
|
: data_header_(loaded_idmap->data_header_),
|
|
|
|
idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
|
2019-07-01 09:48:23 -07:00
|
|
|
|
|
|
|
OverlayStringPool::~OverlayStringPool() {
|
|
|
|
uninit();
|
|
|
|
}
|
|
|
|
|
2020-11-16 23:08:18 +00:00
|
|
|
base::expected<StringPiece16, NullOrIOError> OverlayStringPool::stringAt(size_t idx) const {
|
2019-07-01 09:48:23 -07:00
|
|
|
const size_t offset = dtohl(data_header_->string_pool_index_offset);
|
2019-12-12 10:23:54 -08:00
|
|
|
if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
|
2020-11-16 23:08:18 +00:00
|
|
|
return idmap_string_pool_->stringAt(idx - offset);
|
2017-09-25 13:21:55 -07:00
|
|
|
}
|
|
|
|
|
2020-11-16 23:08:18 +00:00
|
|
|
return ResStringPool::stringAt(idx);
|
2019-07-01 09:48:23 -07:00
|
|
|
}
|
|
|
|
|
2020-11-16 23:08:18 +00:00
|
|
|
base::expected<StringPiece, NullOrIOError> OverlayStringPool::string8At(size_t idx) const {
|
2019-07-01 09:48:23 -07:00
|
|
|
const size_t offset = dtohl(data_header_->string_pool_index_offset);
|
2019-12-12 10:23:54 -08:00
|
|
|
if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
|
2020-11-16 23:08:18 +00:00
|
|
|
return idmap_string_pool_->string8At(idx - offset);
|
2017-09-25 13:21:55 -07:00
|
|
|
}
|
|
|
|
|
2020-11-16 23:08:18 +00:00
|
|
|
return ResStringPool::string8At(idx);
|
2019-07-01 09:48:23 -07:00
|
|
|
}
|
|
|
|
|
2019-12-12 10:23:54 -08:00
|
|
|
size_t OverlayStringPool::size() const {
|
|
|
|
return ResStringPool::size() + (idmap_string_pool_ != nullptr ? idmap_string_pool_->size() : 0U);
|
|
|
|
}
|
|
|
|
|
2019-07-01 09:48:23 -07:00
|
|
|
OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header,
|
|
|
|
const Idmap_overlay_entry* entries,
|
|
|
|
uint8_t target_assigned_package_id)
|
|
|
|
: data_header_(data_header),
|
|
|
|
entries_(entries),
|
|
|
|
target_assigned_package_id_(target_assigned_package_id) { };
|
|
|
|
|
|
|
|
status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
|
|
|
|
const Idmap_overlay_entry* first_entry = entries_;
|
|
|
|
const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
|
2020-09-29 17:22:52 -07:00
|
|
|
auto entry = std::lower_bound(first_entry, end_entry, *resId,
|
|
|
|
[](const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
|
|
|
|
return dtohl(e1.overlay_id) < overlay_id;
|
|
|
|
});
|
2019-07-01 09:48:23 -07:00
|
|
|
|
|
|
|
if (entry == end_entry || dtohl(entry->overlay_id) != *resId) {
|
|
|
|
// A mapping for the target resource id could not be found.
|
|
|
|
return DynamicRefTable::lookupResourceId(resId);
|
2017-09-25 13:21:55 -07:00
|
|
|
}
|
2019-07-01 09:48:23 -07:00
|
|
|
|
|
|
|
*resId = (0x00FFFFFFU & dtohl(entry->target_id))
|
2020-09-29 17:22:52 -07:00
|
|
|
| (((uint32_t) target_assigned_package_id_) << 24U);
|
2019-07-01 09:48:23 -07:00
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const {
|
|
|
|
return DynamicRefTable::lookupResourceId(resId);
|
|
|
|
}
|
|
|
|
|
|
|
|
IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
|
|
|
|
const Idmap_target_entry* entries,
|
2020-09-29 17:22:52 -07:00
|
|
|
const Idmap_target_entry_inline* inline_entries,
|
2019-07-01 09:48:23 -07:00
|
|
|
uint8_t target_assigned_package_id,
|
|
|
|
const OverlayDynamicRefTable* overlay_ref_table)
|
|
|
|
: data_header_(data_header),
|
|
|
|
entries_(entries),
|
2020-09-29 17:22:52 -07:00
|
|
|
inline_entries_(inline_entries),
|
2019-07-01 09:48:23 -07:00
|
|
|
target_assigned_package_id_(target_assigned_package_id),
|
2020-09-29 17:22:52 -07:00
|
|
|
overlay_ref_table_(overlay_ref_table) { }
|
2019-07-01 09:48:23 -07:00
|
|
|
|
|
|
|
IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
|
2020-09-29 17:22:52 -07:00
|
|
|
if ((target_res_id >> 24U) != target_assigned_package_id_) {
|
2019-07-01 09:48:23 -07:00
|
|
|
// The resource id must have the same package id as the target package.
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2021-01-08 13:34:28 -08:00
|
|
|
// The resource ids encoded within the idmap are build-time resource ids so do not consider the
|
|
|
|
// package id when determining if the resource in the target package is overlaid.
|
|
|
|
target_res_id &= 0x00FFFFFFU;
|
2020-09-29 17:22:52 -07:00
|
|
|
|
|
|
|
// Check if the target resource is mapped to an overlay resource.
|
|
|
|
auto first_entry = entries_;
|
|
|
|
auto end_entry = entries_ + dtohl(data_header_->target_entry_count);
|
|
|
|
auto entry = std::lower_bound(first_entry, end_entry, target_res_id,
|
2021-01-08 13:34:28 -08:00
|
|
|
[](const Idmap_target_entry& e, const uint32_t target_id) {
|
|
|
|
return (0x00FFFFFFU & dtohl(e.target_id)) < target_id;
|
2020-09-29 17:22:52 -07:00
|
|
|
});
|
|
|
|
|
2021-01-08 13:34:28 -08:00
|
|
|
if (entry != end_entry && (0x00FFFFFFU & dtohl(entry->target_id)) == target_res_id) {
|
2020-09-29 17:22:52 -07:00
|
|
|
uint32_t overlay_resource_id = dtohl(entry->overlay_id);
|
2019-07-01 09:48:23 -07:00
|
|
|
// Lookup the resource without rewriting the overlay resource id back to the target resource id
|
|
|
|
// being looked up.
|
|
|
|
overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
|
|
|
|
return Result(overlay_resource_id);
|
|
|
|
}
|
|
|
|
|
2020-09-29 17:22:52 -07:00
|
|
|
// Check if the target resources is mapped to an inline table entry.
|
|
|
|
auto first_inline_entry = inline_entries_;
|
|
|
|
auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count);
|
|
|
|
auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id,
|
2021-01-08 13:34:28 -08:00
|
|
|
[](const Idmap_target_entry_inline& e,
|
2020-09-29 17:22:52 -07:00
|
|
|
const uint32_t target_id) {
|
2021-01-08 13:34:28 -08:00
|
|
|
return (0x00FFFFFFU & dtohl(e.target_id)) < target_id;
|
2020-09-29 17:22:52 -07:00
|
|
|
});
|
|
|
|
|
2021-01-08 13:34:28 -08:00
|
|
|
if (inline_entry != end_inline_entry &&
|
|
|
|
(0x00FFFFFFU & dtohl(inline_entry->target_id)) == target_res_id) {
|
2020-09-29 17:22:52 -07:00
|
|
|
return Result(inline_entry->value);
|
|
|
|
}
|
|
|
|
return {};
|
2017-09-25 13:21:55 -07:00
|
|
|
}
|
|
|
|
|
2020-12-03 15:41:42 -08:00
|
|
|
namespace {
|
|
|
|
template <typename T>
|
|
|
|
const T* ReadType(const uint8_t** in_out_data_ptr, size_t* in_out_size, const std::string& label,
|
|
|
|
size_t count = 1) {
|
|
|
|
if (!util::IsFourByteAligned(*in_out_data_ptr)) {
|
|
|
|
LOG(ERROR) << "Idmap " << label << " is not word aligned.";
|
|
|
|
return {};
|
2017-09-25 13:21:55 -07:00
|
|
|
}
|
2020-12-03 15:41:42 -08:00
|
|
|
if ((*in_out_size / sizeof(T)) < count) {
|
|
|
|
LOG(ERROR) << "Idmap too small for the number of " << label << " entries ("
|
|
|
|
<< count << ").";
|
|
|
|
return nullptr;
|
2017-09-25 13:21:55 -07:00
|
|
|
}
|
2020-12-03 15:41:42 -08:00
|
|
|
auto data_ptr = *in_out_data_ptr;
|
|
|
|
const size_t read_size = sizeof(T) * count;
|
|
|
|
*in_out_data_ptr += read_size;
|
|
|
|
*in_out_size -= read_size;
|
|
|
|
return reinterpret_cast<const T*>(data_ptr);
|
|
|
|
}
|
2017-09-25 13:21:55 -07:00
|
|
|
|
2020-12-03 15:41:42 -08:00
|
|
|
std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size_t* in_out_size,
|
|
|
|
const std::string& label) {
|
|
|
|
const auto* len = ReadType<uint32_t>(in_out_data_ptr, in_out_size, label + " length");
|
|
|
|
if (len == nullptr) {
|
|
|
|
return {};
|
2017-09-25 13:21:55 -07:00
|
|
|
}
|
2020-12-03 15:41:42 -08:00
|
|
|
const auto* data = ReadType<char>(in_out_data_ptr, in_out_size, label, *len);
|
|
|
|
if (data == nullptr) {
|
|
|
|
return {};
|
2017-09-25 13:21:55 -07:00
|
|
|
}
|
2020-12-03 15:41:42 -08:00
|
|
|
// Strings are padded to the next 4 byte boundary.
|
|
|
|
const uint32_t padding_size = (4U - ((size_t)*in_out_data_ptr & 0x3U)) % 4U;
|
|
|
|
for (uint32_t i = 0; i < padding_size; i++) {
|
|
|
|
if (**in_out_data_ptr != 0) {
|
|
|
|
LOG(ERROR) << " Idmap padding of " << label << " is non-zero.";
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
*in_out_data_ptr += sizeof(uint8_t);
|
|
|
|
*in_out_size -= sizeof(uint8_t);
|
|
|
|
}
|
|
|
|
return std::string_view(data, *len);
|
|
|
|
}
|
2021-01-08 13:34:28 -08:00
|
|
|
} // namespace
|
2017-09-25 13:21:55 -07:00
|
|
|
|
2020-03-26 17:15:01 -07:00
|
|
|
LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
|
|
|
|
const Idmap_header* header,
|
2019-07-01 09:48:23 -07:00
|
|
|
const Idmap_data_header* data_header,
|
|
|
|
const Idmap_target_entry* target_entries,
|
2020-09-29 17:22:52 -07:00
|
|
|
const Idmap_target_entry_inline* target_inline_entries,
|
2019-07-01 09:48:23 -07:00
|
|
|
const Idmap_overlay_entry* overlay_entries,
|
2020-12-03 15:41:42 -08:00
|
|
|
std::unique_ptr<ResStringPool>&& string_pool,
|
|
|
|
std::string_view overlay_apk_path,
|
|
|
|
std::string_view target_apk_path)
|
2019-11-12 16:22:04 -08:00
|
|
|
: header_(header),
|
|
|
|
data_header_(data_header),
|
|
|
|
target_entries_(target_entries),
|
2020-09-29 17:22:52 -07:00
|
|
|
target_inline_entries_(target_inline_entries),
|
2019-11-12 16:22:04 -08:00
|
|
|
overlay_entries_(overlay_entries),
|
2020-12-03 15:41:42 -08:00
|
|
|
string_pool_(std::move(string_pool)),
|
2020-03-26 17:15:01 -07:00
|
|
|
idmap_path_(std::move(idmap_path)),
|
2020-12-03 15:41:42 -08:00
|
|
|
overlay_apk_path_(overlay_apk_path),
|
|
|
|
target_apk_path_(target_apk_path),
|
|
|
|
idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {}
|
2017-09-25 13:21:55 -07:00
|
|
|
|
2021-01-10 08:36:36 -08:00
|
|
|
std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
|
|
|
|
const StringPiece& idmap_data) {
|
2017-09-25 13:21:55 -07:00
|
|
|
ATRACE_CALL();
|
2020-12-03 15:41:42 -08:00
|
|
|
size_t data_size = idmap_data.size();
|
|
|
|
auto data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data());
|
|
|
|
|
|
|
|
// Parse the idmap header
|
|
|
|
auto header = ReadType<Idmap_header>(&data_ptr, &data_size, "header");
|
|
|
|
if (header == nullptr) {
|
2017-09-25 13:21:55 -07:00
|
|
|
return {};
|
|
|
|
}
|
2020-12-03 15:41:42 -08:00
|
|
|
if (dtohl(header->magic) != kIdmapMagic) {
|
|
|
|
LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)",
|
|
|
|
dtohl(header->magic), kIdmapMagic);
|
2019-07-01 09:48:23 -07:00
|
|
|
return {};
|
|
|
|
}
|
2020-12-03 15:41:42 -08:00
|
|
|
if (dtohl(header->version) != kIdmapCurrentVersion) {
|
|
|
|
// We are strict about versions because files with this format are generated at runtime and
|
|
|
|
// don't need backwards compatibility.
|
|
|
|
LOG(ERROR) << StringPrintf("Version mismatch in Idmap (was 0x%08x, expected 0x%08x)",
|
|
|
|
dtohl(header->version), kIdmapCurrentVersion);
|
2020-09-29 17:22:52 -07:00
|
|
|
return {};
|
|
|
|
}
|
2020-12-03 15:41:42 -08:00
|
|
|
std::optional<std::string_view> overlay_path = ReadString(&data_ptr, &data_size, "overlay path");
|
|
|
|
if (!overlay_path) {
|
2019-07-01 09:48:23 -07:00
|
|
|
return {};
|
|
|
|
}
|
2020-12-03 15:41:42 -08:00
|
|
|
std::optional<std::string_view> target_path = ReadString(&data_ptr, &data_size, "target path");
|
|
|
|
if (!target_path) {
|
|
|
|
return {};
|
|
|
|
}
|
2020-12-02 11:43:18 -08:00
|
|
|
if (!ReadString(&data_ptr, &data_size, "target name") ||
|
|
|
|
!ReadString(&data_ptr, &data_size, "debug info")) {
|
2019-07-01 09:48:23 -07:00
|
|
|
return {};
|
2017-09-25 13:21:55 -07:00
|
|
|
}
|
|
|
|
|
2020-12-03 15:41:42 -08:00
|
|
|
// Parse the idmap data blocks. Currently idmap2 can only generate one data block.
|
|
|
|
auto data_header = ReadType<Idmap_data_header>(&data_ptr, &data_size, "data header");
|
|
|
|
if (data_header == nullptr) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
auto target_entries = ReadType<Idmap_target_entry>(&data_ptr, &data_size, "target",
|
|
|
|
dtohl(data_header->target_entry_count));
|
|
|
|
if (target_entries == nullptr) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
auto target_inline_entries = ReadType<Idmap_target_entry_inline>(
|
|
|
|
&data_ptr, &data_size, "target inline", dtohl(data_header->target_inline_entry_count));
|
|
|
|
if (target_inline_entries == nullptr) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
auto overlay_entries = ReadType<Idmap_overlay_entry>(&data_ptr, &data_size, "target inline",
|
|
|
|
dtohl(data_header->overlay_entry_count));
|
|
|
|
if (overlay_entries == nullptr) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
std::optional<std::string_view> string_pool = ReadString(&data_ptr, &data_size, "string pool");
|
|
|
|
if (!string_pool) {
|
|
|
|
return {};
|
|
|
|
}
|
2019-07-01 09:48:23 -07:00
|
|
|
auto idmap_string_pool = util::make_unique<ResStringPool>();
|
2020-12-03 15:41:42 -08:00
|
|
|
if (!string_pool->empty()) {
|
|
|
|
const status_t err = idmap_string_pool->setTo(string_pool->data(), string_pool->size());
|
2019-07-01 09:48:23 -07:00
|
|
|
if (err != NO_ERROR) {
|
|
|
|
LOG(ERROR) << "idmap string pool corrupt.";
|
|
|
|
return {};
|
|
|
|
}
|
2017-09-25 13:21:55 -07:00
|
|
|
}
|
|
|
|
|
2021-01-13 10:11:18 -08:00
|
|
|
if (data_size != 0) {
|
|
|
|
LOG(ERROR) << "idmap parsed with " << data_size << "bytes remaining";
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-11-12 16:22:04 -08:00
|
|
|
// Can't use make_unique because LoadedIdmap constructor is private.
|
2020-12-03 15:41:42 -08:00
|
|
|
return std::unique_ptr<LoadedIdmap>(
|
|
|
|
new LoadedIdmap(idmap_path.to_string(), header, data_header, target_entries,
|
|
|
|
target_inline_entries, overlay_entries, std::move(idmap_string_pool),
|
|
|
|
*target_path, *overlay_path));
|
2017-09-25 13:21:55 -07:00
|
|
|
}
|
|
|
|
|
2020-03-26 17:15:01 -07:00
|
|
|
bool LoadedIdmap::IsUpToDate() const {
|
|
|
|
return idmap_last_mod_time_ == getFileModDate(idmap_path_.c_str());
|
|
|
|
}
|
|
|
|
|
2017-09-25 13:21:55 -07:00
|
|
|
} // namespace android
|