Store frro configs in idmap file

This also includes the change to use the configs to decide which (if
any) frros to use at runtime

Bug: 243066074
Test: Manual, updated and created automated tests
Change-Id: I3f1d23e2958ad170799880b9f5eb5bd8ceb1fa67
This commit is contained in:
Jeremy Meyer
2022-08-23 17:42:50 +00:00
parent 4e0d271b00
commit be2b779746
20 changed files with 399 additions and 143 deletions

View File

@@ -21,42 +21,51 @@
* header := magic version target_crc overlay_crc fulfilled_policies
* enforce_overlayable target_path overlay_path overlay_name
* debug_info
* data := data_header target_entry* target_inline_entry* overlay_entry*
* string_pool
* data_header := target_entry_count target_inline_entry_count overlay_entry_count
* data := data_header target_entry* target_inline_entry*
target_inline_entry_value* config* overlay_entry* string_pool
* data_header := target_entry_count target_inline_entry_count
target_inline_entry_value_count config_count overlay_entry_count
* string_pool_index
* target_entry := target_id overlay_id
* target_inline_entry := target_id Res_value::size padding(1) Res_value::type
* target_inline_entry := target_id start_value_index value_count
* target_inline_entry_value := config_index Res_value::size padding(1) Res_value::type
* Res_value::value
* config := target_id Res_value::size padding(1) Res_value::type
* Res_value::value
* overlay_entry := overlay_id target_id
*
* debug_info := string
* enforce_overlayable := <uint32_t>
* fulfilled_policies := <uint32_t>
* magic := <uint32_t>
* overlay_crc := <uint32_t>
* overlay_entry_count := <uint32_t>
* overlay_id := <uint32_t>
* overlay_package_id := <uint8_t>
* overlay_name := string
* overlay_path := string
* padding(n) := <uint8_t>[n]
* Res_value::size := <uint16_t>
* Res_value::type := <uint8_t>
* Res_value::value := <uint32_t>
* string := <uint32_t> <uint8_t>+ padding(n)
* string_pool := string
* string_pool_index := <uint32_t>
* string_pool_length := <uint32_t>
* target_crc := <uint32_t>
* target_entry_count := <uint32_t>
* target_inline_entry_count := <uint32_t>
* target_id := <uint32_t>
* target_package_id := <uint8_t>
* target_path := string
* value_type := <uint8_t>
* value_data := <uint32_t>
* version := <uint32_t>
* debug_info := string
* enforce_overlayable := <uint32_t>
* fulfilled_policies := <uint32_t>
* magic := <uint32_t>
* overlay_crc := <uint32_t>
* overlay_entry_count := <uint32_t>
* overlay_id := <uint32_t>
* overlay_package_id := <uint8_t>
* overlay_name := string
* overlay_path := string
* padding(n) := <uint8_t>[n]
* Res_value::size := <uint16_t>
* Res_value::type := <uint8_t>
* Res_value::value := <uint32_t>
* string := <uint32_t> <uint8_t>+ padding(n)
* string_pool := string
* string_pool_index := <uint32_t>
* string_pool_length := <uint32_t>
* target_crc := <uint32_t>
* target_entry_count := <uint32_t>
* target_inline_entry_count := <uint32_t>
* target_inline_entry_value_count := <uint32_t>
* config_count := <uint32_t>
* config_index := <uint32_t>
* start_value_index := <uint32_t>
* value_count := <uint32_t>
* target_id := <uint32_t>
* target_package_id := <uint8_t>
* target_path := string
* value_type := <uint8_t>
* value_data := <uint32_t>
* version := <uint32_t>
*/
#ifndef IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
@@ -70,6 +79,7 @@
#include "android-base/macros.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
#include "androidfw/ConfigDescription.h"
#include "idmap2/ResourceContainer.h"
#include "idmap2/ResourceMapping.h"
@@ -165,19 +175,27 @@ class IdmapData {
public:
static std::unique_ptr<const Header> FromBinaryStream(std::istream& stream);
inline uint32_t GetTargetEntryCount() const {
[[nodiscard]] inline uint32_t GetTargetEntryCount() const {
return target_entry_count;
}
inline uint32_t GetTargetInlineEntryCount() const {
[[nodiscard]] inline uint32_t GetTargetInlineEntryCount() const {
return target_entry_inline_count;
}
inline uint32_t GetOverlayEntryCount() const {
[[nodiscard]] inline uint32_t GetTargetInlineEntryValueCount() const {
return target_entry_inline_value_count;
}
[[nodiscard]] inline uint32_t GetConfigCount() const {
return config_count;
}
[[nodiscard]] inline uint32_t GetOverlayEntryCount() const {
return overlay_entry_count;
}
inline uint32_t GetStringPoolIndexOffset() const {
[[nodiscard]] inline uint32_t GetStringPoolIndexOffset() const {
return string_pool_index_offset;
}
@@ -186,6 +204,8 @@ class IdmapData {
private:
uint32_t target_entry_count;
uint32_t target_entry_inline_count;
uint32_t target_entry_inline_value_count;
uint32_t config_count;
uint32_t overlay_entry_count;
uint32_t string_pool_index_offset;
Header() = default;
@@ -202,7 +222,7 @@ class IdmapData {
struct TargetInlineEntry {
ResourceId target_id;
TargetValue value;
std::map<ConfigDescription, TargetValue> values;
};
struct OverlayEntry {
@@ -227,11 +247,11 @@ class IdmapData {
return target_inline_entries_;
}
const std::vector<OverlayEntry>& GetOverlayEntries() const {
[[nodiscard]] const std::vector<OverlayEntry>& GetOverlayEntries() const {
return overlay_entries_;
}
const std::string& GetStringPoolData() const {
[[nodiscard]] const std::string& GetStringPoolData() const {
return string_pool_data_;
}

View File

@@ -33,7 +33,8 @@
using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
namespace android::idmap2 {
using TargetResourceMap = std::map<ResourceId, std::variant<ResourceId, TargetValue>>;
using ConfigMap = std::unordered_map<std::string, TargetValue>;
using TargetResourceMap = std::map<ResourceId, std::variant<ResourceId, ConfigMap>>;
using OverlayResourceMap = std::map<ResourceId, ResourceId>;
class ResourceMapping {

View File

@@ -46,6 +46,10 @@ struct TargetValue {
struct TargetValueWithConfig {
TargetValue value;
std::string config;
[[nodiscard]] std::pair<std::string, TargetValue> to_pair() const {
return std::make_pair(config, value);
}
};
namespace utils {

View File

@@ -70,12 +70,35 @@ void BinaryStreamVisitor::visit(const IdmapData& data) {
}
static constexpr uint16_t kValueSize = 8U;
std::vector<std::pair<ConfigDescription, TargetValue>> target_values;
target_values.reserve(data.GetHeader()->GetTargetInlineEntryValueCount());
for (const auto& target_entry : data.GetTargetInlineEntries()) {
Write32(target_entry.target_id);
Write32(target_values.size());
Write32(target_entry.values.size());
target_values.insert(
target_values.end(), target_entry.values.begin(), target_entry.values.end());
}
std::vector<ConfigDescription> configs;
configs.reserve(data.GetHeader()->GetConfigCount());
for (const auto& target_entry_value : target_values) {
auto config_it = find(configs.begin(), configs.end(), target_entry_value.first);
if (config_it != configs.end()) {
Write32(config_it - configs.begin());
} else {
Write32(configs.size());
configs.push_back(target_entry_value.first);
}
Write16(kValueSize);
Write8(0U); // padding
Write8(target_entry.value.data_type);
Write32(target_entry.value.data_value);
Write8(target_entry_value.second.data_type);
Write32(target_entry_value.second.data_value);
}
for( auto& cd : configs) {
cd.swapHtoD();
stream_.write(reinterpret_cast<char*>(&cd), sizeof(cd));
}
for (const auto& overlay_entry : data.GetOverlayEntries()) {
@@ -89,6 +112,8 @@ void BinaryStreamVisitor::visit(const IdmapData& data) {
void BinaryStreamVisitor::visit(const IdmapData::Header& header) {
Write32(header.GetTargetEntryCount());
Write32(header.GetTargetInlineEntryCount());
Write32(header.GetTargetInlineEntryValueCount());
Write32(header.GetConfigCount());
Write32(header.GetOverlayEntryCount());
Write32(header.GetStringPoolIndexOffset());
}

View File

@@ -186,6 +186,8 @@ std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std
std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header());
if (!Read32(stream, &idmap_data_header->target_entry_count) ||
!Read32(stream, &idmap_data_header->target_entry_inline_count) ||
!Read32(stream, &idmap_data_header->target_entry_inline_value_count) ||
!Read32(stream, &idmap_data_header->config_count) ||
!Read32(stream, &idmap_data_header->overlay_entry_count) ||
!Read32(stream, &idmap_data_header->string_pool_index_offset)) {
return nullptr;
@@ -207,20 +209,59 @@ std::unique_ptr<const IdmapData> IdmapData::FromBinaryStream(std::istream& strea
if (!Read32(stream, &target_entry.target_id) || !Read32(stream, &target_entry.overlay_id)) {
return nullptr;
}
data->target_entries_.push_back(target_entry);
data->target_entries_.emplace_back(target_entry);
}
// Read the mapping of target resource id to inline overlay values.
uint8_t unused1;
uint16_t unused2;
std::vector<std::tuple<TargetInlineEntry, uint32_t, uint32_t>> target_inline_entries;
for (size_t i = 0; i < data->header_->GetTargetInlineEntryCount(); i++) {
TargetInlineEntry target_entry{};
if (!Read32(stream, &target_entry.target_id) || !Read16(stream, &unused2) ||
!Read8(stream, &unused1) || !Read8(stream, &target_entry.value.data_type) ||
!Read32(stream, &target_entry.value.data_value)) {
uint32_t entry_offset;
uint32_t entry_count;
if (!Read32(stream, &target_entry.target_id) || !Read32(stream, &entry_offset)
|| !Read32(stream, &entry_count)) {
return nullptr;
}
data->target_inline_entries_.push_back(target_entry);
target_inline_entries.emplace_back(std::make_tuple(target_entry, entry_offset, entry_count));
}
// Read the inline overlay resource values
std::vector<std::pair<uint32_t, TargetValue>> target_values;
uint8_t unused1;
uint16_t unused2;
for (size_t i = 0; i < data->header_->GetTargetInlineEntryValueCount(); i++) {
uint32_t config_index;
if (!Read32(stream, &config_index)) {
return nullptr;
}
TargetValue value;
if (!Read16(stream, &unused2)
|| !Read8(stream, &unused1)
|| !Read8(stream, &value.data_type)
|| !Read32(stream, &value.data_value)) {
return nullptr;
}
target_values.emplace_back(std::make_pair(config_index, value));
}
// Read the configurations
std::vector<ConfigDescription> configurations;
for (size_t i = 0; i < data->header_->GetConfigCount(); i++) {
ConfigDescription cd;
if (!stream.read(reinterpret_cast<char*>(&cd), sizeof(ConfigDescription))) {
return nullptr;
}
configurations.emplace_back(cd);
}
// Construct complete target inline entries
for (auto [target_entry, entry_offset, entry_count] : target_inline_entries) {
for(size_t i = 0; i < entry_count; i++) {
const auto& target_value = target_values[entry_offset + i];
const auto& config = configurations[target_value.first];
target_entry.values[config] = target_value.second;
}
data->target_inline_entries_.emplace_back(target_entry);
}
// Read the mapping of overlay resource id to target resource id.
@@ -278,12 +319,21 @@ Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
std::unique_ptr<IdmapData> data(new IdmapData());
data->string_pool_data_ = resource_mapping.GetStringPoolData().to_string();
uint32_t inline_value_count = 0;
std::set<std::string> config_set;
for (const auto& mapping : resource_mapping.GetTargetToOverlayMap()) {
if (auto overlay_resource = std::get_if<ResourceId>(&mapping.second)) {
data->target_entries_.push_back({mapping.first, *overlay_resource});
} else {
data->target_inline_entries_.push_back(
{mapping.first, std::get<TargetValue>(mapping.second)});
std::map<ConfigDescription, TargetValue> values;
for (const auto& [config, value] : std::get<ConfigMap>(mapping.second)) {
config_set.insert(config);
ConfigDescription cd;
ConfigDescription::Parse(config, &cd);
values[cd] = value;
inline_value_count++;
}
data->target_inline_entries_.push_back({mapping.first, values});
}
}
@@ -295,6 +345,8 @@ Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
data_header->target_entry_count = static_cast<uint32_t>(data->target_entries_.size());
data_header->target_entry_inline_count =
static_cast<uint32_t>(data->target_inline_entries_.size());
data_header->target_entry_inline_value_count = inline_value_count;
data_header->config_count = config_set.size();
data_header->overlay_entry_count = static_cast<uint32_t>(data->overlay_entries_.size());
data_header->string_pool_index_offset = resource_mapping.GetStringPoolOffset();
data->header_ = std::move(data_header);

View File

@@ -94,14 +94,17 @@ void PrettyPrintVisitor::visit(const IdmapData& data) {
}
for (auto& target_entry : data.GetTargetInlineEntries()) {
stream_ << TAB << base::StringPrintf("0x%08x -> ", target_entry.target_id)
<< utils::DataTypeToString(target_entry.value.data_type);
for(auto iter = target_entry.values.begin(); iter != target_entry.values.end(); ++iter) {
auto value = iter->second;
stream_ << TAB << base::StringPrintf("0x%08x -> ", target_entry.target_id)
<< utils::DataTypeToString(value.data_type);
if (target_entry.value.data_type == Res_value::TYPE_STRING) {
auto str = string_pool.stringAt(target_entry.value.data_value - string_pool_offset);
stream_ << " \"" << str.value_or(StringPiece16(u"")) << "\"";
} else {
stream_ << " " << base::StringPrintf("0x%08x", target_entry.value.data_value);
if (value.data_type == Res_value::TYPE_STRING) {
auto str = string_pool.stringAt(value.data_value - string_pool_offset);
stream_ << " \"" << str.value_or(StringPiece16(u"")) << "\"";
} else {
stream_ << " " << base::StringPrintf("0x%08x", value.data_value);
}
}
std::string target_name = kUnknownResourceName;

View File

@@ -89,22 +89,30 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
print(target_entry.target_id, "target id");
}
pad(sizeof(Res_value::size) + sizeof(Res_value::res0));
print(target_entry.value.data_type, "type: %s",
utils::DataTypeToString(target_entry.value.data_type).data());
for (auto& target_entry_value : target_entry.values) {
auto value = target_entry_value.second;
Result<std::string> overlay_name(Error(""));
if (overlay_ != nullptr &&
(target_entry.value.data_value == Res_value::TYPE_REFERENCE ||
target_entry.value.data_value == Res_value::TYPE_DYNAMIC_REFERENCE)) {
overlay_name = overlay_->GetResourceName(target_entry.value.data_value);
}
print(target_entry_value.first.to_string(), false, "config: %s",
target_entry_value.first.toString().string());
if (overlay_name) {
print(target_entry.value.data_value, "data: %s", overlay_name->c_str());
} else {
print(target_entry.value.data_value, "data");
print(value.data_type, "type: %s",
utils::DataTypeToString(value.data_type).data());
Result<std::string> overlay_name(Error(""));
if (overlay_ != nullptr &&
(value.data_value == Res_value::TYPE_REFERENCE ||
value.data_value == Res_value::TYPE_DYNAMIC_REFERENCE)) {
overlay_name = overlay_->GetResourceName(value.data_value);
}
if (overlay_name) {
print(value.data_value, "data: %s", overlay_name->c_str());
} else {
print(value.data_value, "data");
}
}
}
@@ -138,6 +146,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
void RawPrintVisitor::visit(const IdmapData::Header& header) {
print(header.GetTargetEntryCount(), "target entry count");
print(header.GetTargetInlineEntryCount(), "target inline entry count");
print(header.GetTargetInlineEntryValueCount(), "target inline entry value count");
print(header.GetConfigCount(), "config count");
print(header.GetOverlayEntryCount(), "overlay entry count");
print(header.GetStringPoolIndexOffset(), "string pool index offset");
}

View File

@@ -161,14 +161,13 @@ Result<ResourceMapping> ResourceMapping::FromContainers(const TargetResourceCont
Result<Unit> ResourceMapping::AddMapping(
ResourceId target_resource,
const std::variant<OverlayData::ResourceIdValue, TargetValueWithConfig>& value) {
if (target_map_.find(target_resource) != target_map_.end()) {
return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
}
// TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
// runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
if (auto overlay_resource = std::get_if<OverlayData::ResourceIdValue>(&value)) {
if (target_map_.find(target_resource) != target_map_.end()) {
return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
}
target_map_.insert(std::make_pair(target_resource, overlay_resource->overlay_id));
if (overlay_resource->rewrite_id) {
// An overlay resource can override multiple target resources at once. Rewrite the overlay
@@ -176,8 +175,18 @@ Result<Unit> ResourceMapping::AddMapping(
overlay_map_.insert(std::make_pair(overlay_resource->overlay_id, target_resource));
}
} else {
auto overlay_value = std::get<TargetValueWithConfig>(value);
target_map_.insert(std::make_pair(target_resource, overlay_value.value));
auto[iter, inserted] = target_map_.try_emplace(target_resource, ConfigMap());
auto& resource_value = iter->second;
if (!inserted && std::holds_alternative<ResourceId>(resource_value)) {
return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
}
auto& config_map = std::get<ConfigMap>(resource_value);
const auto& config_value = std::get<TargetValueWithConfig>(value);
if (config_map.find(config_value.config) != config_map.end()) {
return Error(R"(target resource id "0x%08x" mapped to multiple values with the same config)",
target_resource);
}
config_map.insert(config_value.to_pair());
}
return Unit{};

View File

@@ -84,8 +84,10 @@ TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) {
const auto& target_inline_entries2 = data2->GetTargetInlineEntries();
ASSERT_EQ(target_inline_entries1.size(), target_inline_entries2.size());
ASSERT_EQ(target_inline_entries1[0].target_id, target_inline_entries2[0].target_id);
ASSERT_EQ(target_inline_entries1[0].value.data_type, target_inline_entries2[0].value.data_type);
ASSERT_EQ(target_inline_entries1[0].value.data_value, target_inline_entries2[0].value.data_value);
ASSERT_EQ(target_inline_entries1[0].values.begin()->second.data_type,
target_inline_entries2[0].values.begin()->second.data_type);
ASSERT_EQ(target_inline_entries1[0].values.begin()->second.data_value,
target_inline_entries2[0].values.begin()->second.data_value);
const auto& overlay_entries1 = data1->GetOverlayEntries();
const auto& overlay_entries2 = data2->GetOverlayEntries();

View File

@@ -47,10 +47,11 @@ namespace android::idmap2 {
ASSERT_EQ((entry).target_id, (target_resid)); \
ASSERT_EQ((entry).overlay_id, (overlay_resid))
#define ASSERT_TARGET_INLINE_ENTRY(entry, target_resid, expected_type, expected_value) \
ASSERT_EQ((entry).target_id, target_resid); \
ASSERT_EQ((entry).value.data_type, (expected_type)); \
ASSERT_EQ((entry).value.data_value, (expected_value))
#define ASSERT_TARGET_INLINE_ENTRY(entry, target_resid, ex_config, expected_type, expected_value) \
ASSERT_EQ((entry).target_id, target_resid); \
ASSERT_EQ((entry).values.begin()->first.to_string(), (ex_config)); \
ASSERT_EQ((entry).values.begin()->second.data_type, (expected_type)); \
ASSERT_EQ((entry).values.begin()->second.data_value, (expected_value))
#define ASSERT_OVERLAY_ENTRY(entry, overlay_resid, target_resid) \
ASSERT_EQ((entry).overlay_id, (overlay_resid)); \
@@ -67,7 +68,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) {
std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
ASSERT_EQ(header->GetMagic(), 0x504d4449U);
ASSERT_EQ(header->GetVersion(), 0x08U);
ASSERT_EQ(header->GetVersion(), 0x09U);
ASSERT_EQ(header->GetTargetCrc(), 0x1234U);
ASSERT_EQ(header->GetOverlayCrc(), 0x5678U);
ASSERT_EQ(header->GetFulfilledPolicies(), 0x11);
@@ -122,8 +123,8 @@ TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
const auto& target_inline_entries = data->GetTargetInlineEntries();
ASSERT_EQ(target_inline_entries.size(), 1U);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000, Res_value::TYPE_INT_HEX,
0x12345678);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000, "land-xxhdpi-v7",
Res_value::TYPE_INT_HEX, 0x12345678);
const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(target_entries.size(), 3U);
@@ -142,7 +143,7 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) {
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x08U);
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x09U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U);
ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), kIdmapRawDataPolicies);
@@ -165,8 +166,8 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) {
const auto& target_inline_entries = data->GetTargetInlineEntries();
ASSERT_EQ(target_inline_entries.size(), 1U);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000, Res_value::TYPE_INT_HEX,
0x12345678);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000, "land-xxhdpi-v7",
Res_value::TYPE_INT_HEX, 0x12345678);
const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(target_entries.size(), 3U);
@@ -203,7 +204,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) {
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x08U);
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x09U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC);
ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC);
@@ -261,9 +262,9 @@ TEST(IdmapTests, FabricatedOverlay) {
auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target")
.SetOverlayable("TestResources")
.SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U, "")
.SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000, "")
.SetResourceValue("string/str2", Res_value::TYPE_STRING, "foobar", "")
.SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U, "land-xxhdpi-v7")
.SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000, "land")
.SetResourceValue("string/str2", Res_value::TYPE_STRING, "foobar", "xxhdpi-v7")
.Build();
ASSERT_TRUE(frro);
@@ -295,11 +296,11 @@ TEST(IdmapTests, FabricatedOverlay) {
const auto& target_inline_entries = data->GetTargetInlineEntries();
ASSERT_EQ(target_inline_entries.size(), 3U);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1,
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1, "land-xxhdpi-v7",
Res_value::TYPE_INT_DEC, 2U);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1,
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1, "land",
Res_value::TYPE_REFERENCE, 0x7f010000);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[2], R::target::string::str2,
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[2], R::target::string::str2, "xxhdpi-v7",
Res_value::TYPE_STRING,
(uint32_t) (string_pool.indexOfString(u"foobar", 6)).value_or(-1));
}
@@ -442,9 +443,9 @@ TEST(IdmapTests, CreateIdmapDataInlineResources) {
constexpr size_t overlay_string_pool_size = 10U;
const auto& target_inline_entries = data->GetTargetInlineEntries();
ASSERT_EQ(target_inline_entries.size(), 2U);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1,
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1, std::string(),
Res_value::TYPE_INT_DEC, 73U); // -> 73
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1,
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1, std::string(),
Res_value::TYPE_STRING,
overlay_string_pool_size + 0U); // -> "Hello World"

View File

@@ -64,7 +64,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
(*idmap)->accept(&visitor);
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000008 version\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000009 version\n", stream.str());
ASSERT_CONTAINS_REGEX(
StringPrintf(ADDRESS "%s target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING),
stream.str());
@@ -75,6 +75,8 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 enforce overlayable\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 target entry count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000000 target inline entry count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000000 target inline entry value count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000000 config count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "0000000a string pool index offset", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1", stream.str());
@@ -111,7 +113,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
(*idmap)->accept(&visitor);
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000008 version\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000009 version\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00001234 target crc\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00005678 overlay crc\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000011 fulfilled policies: public|signature\n", stream.str());
@@ -124,17 +126,25 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
ASSERT_CONTAINS_REGEX(ADDRESS "........ overlay name: OverlayName\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000003 target entry count\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 target inline entry count\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 target inline entry value count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 config count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000003 overlay entry count\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000000 string pool index offset\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 overlay id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f030000 target id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f030000 overlay id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f030002 target id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f030001 overlay id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f040000 target id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "0000000e config: land-xxhdpi-v7 size\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "........ config: land-xxhdpi-v7\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 11 type: integer\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "12345678 data\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 overlay id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f030002 target id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 string pool size\n", stream.str());
ASSERT_CONTAINS_REGEX("000000a4: ........ string pool\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "........ string pool\n", stream.str());
}
} // namespace android::idmap2

View File

@@ -111,19 +111,20 @@ Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& tar
return Error("Failed to find mapping for target resource");
}
auto actual_overlay_value = std::get_if<TargetValue>(&entry_map->second);
if (actual_overlay_value == nullptr) {
auto config_map = std::get_if<ConfigMap>(&entry_map->second);
if (config_map == nullptr || config_map->empty()) {
return Error("Target resource is not mapped to an inline value");
}
auto actual_overlay_value = config_map->begin()->second;
if (actual_overlay_value->data_type != type) {
if (actual_overlay_value.data_type != type) {
return Error(R"(Expected type: "0x%02x" Actual type: "0x%02x")", type,
actual_overlay_value->data_type);
actual_overlay_value.data_type);
}
if (actual_overlay_value->data_value != value) {
if (actual_overlay_value.data_value != value) {
return Error(R"(Expected value: "0x%08x" Actual value: "0x%08x")", type,
actual_overlay_value->data_value);
actual_overlay_value.data_value);
}
return Result<Unit>({});

View File

@@ -30,7 +30,7 @@ const unsigned char kIdmapRawData[] = {
0x49, 0x44, 0x4d, 0x50,
// 0x4: version
0x08, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00,
// 0x8: target crc
0x34, 0x12, 0x00, 0x00,
@@ -76,66 +76,123 @@ const unsigned char kIdmapRawData[] = {
// 0x58: target_inline_entry_count
0x01, 0x00, 0x00, 0x00,
// 0x5c: overlay_entry_count
// 0x5c: target_inline_entry_value_count
0x01, 0x00, 0x00, 0x00,
// 0x60: config_count
0x01, 0x00, 0x00, 0x00,
// 0x64: overlay_entry_count
0x03, 0x00, 0x00, 0x00,
// 0x60: string_pool_offset
// 0x68: string_pool_offset
0x00, 0x00, 0x00, 0x00,
// TARGET ENTRIES
// 0x64: target id (0x7f020000)
// 0x6c: target id (0x7f020000)
0x00, 0x00, 0x02, 0x7f,
// 0x68: overlay_id (0x7f020000)
// 0x70: overlay_id (0x7f020000)
0x00, 0x00, 0x02, 0x7f,
// 0x6c: target id (0x7f030000)
// 0x74: target id (0x7f030000)
0x00, 0x00, 0x03, 0x7f,
// 0x70: overlay_id (0x7f030000)
// 0x78: overlay_id (0x7f030000)
0x00, 0x00, 0x03, 0x7f,
// 0x74: target id (0x7f030002)
// 0x7c: target id (0x7f030002)
0x02, 0x00, 0x03, 0x7f,
// 0x78: overlay_id (0x7f030001)
// 0x80: overlay_id (0x7f030001)
0x01, 0x00, 0x03, 0x7f,
// INLINE TARGET ENTRIES
// 0x7c: target_id
// 0x84: target_id
0x00, 0x00, 0x04, 0x7f,
// 0x80: Res_value::size (value ignored by idmap)
// 0x88: start value index
0x00, 0x00, 0x00, 0x00,
// 0x8c: value count
0x01, 0x00, 0x00, 0x00,
// INLINE TARGET ENTRY VALUES
// 0x90: config index
0x00, 0x00, 0x00, 0x00,
// 0x94: Res_value::size (value ignored by idmap)
0x08, 0x00,
// 0x82: Res_value::res0 (value ignored by idmap)
// 0x98: Res_value::res0 (value ignored by idmap)
0x00,
// 0x83: Res_value::dataType (TYPE_INT_HEX)
// 0x9c: Res_value::dataType (TYPE_INT_HEX)
0x11,
// 0x84: Res_value::data
// 0xa0: Res_value::data
0x78, 0x56, 0x34, 0x12,
// CONFIGURATIONS
// 0xa4: ConfigDescription
// size
0x40, 0x00, 0x00, 0x00,
// 0xa8: imsi
0x00, 0x00, 0x00, 0x00,
// 0xac: locale
0x00, 0x00, 0x00, 0x00,
// 0xb0: screenType
0x02, 0x00, 0xe0, 0x01,
// 0xb4: input
0x00, 0x00, 0x00, 0x00,
// 0xb8: screenSize
0x00, 0x00, 0x00, 0x00,
// 0xbc: version
0x07, 0x00, 0x00, 0x00,
// 0xc0: screenConfig
0x00, 0x00, 0x00, 0x00,
// 0xc4: screenSizeDp
0x00, 0x00, 0x00, 0x00,
// 0xc8: localeScript
0x00, 0x00, 0x00, 0x00,
// 0xcc: localVariant(1)
0x00, 0x00, 0x00, 0x00,
// 0xd0: localVariant(2)
0x00, 0x00, 0x00, 0x00,
// 0xd4: screenConfig2
0x00, 0x00, 0x00, 0x00,
// 0xd8: localeScriptWasComputed
0x00,
// 0xd9: localeNumberingSystem(1)
0x00, 0x00, 0x00, 0x00,
// 0xdd: localeNumberingSystem(2)
0x00, 0x00, 0x00, 0x00,
// 0xe1: padding
0x00, 0x00, 0x00,
// OVERLAY ENTRIES
// 0x88: 0x7f020000 -> 0x7f020000
// 0xe4: 0x7f020000 -> 0x7f020000
0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f,
// 0x90: 0x7f030000 -> 0x7f030000
// 0xec: 0x7f030000 -> 0x7f030000
0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f,
// 0x98: 0x7f030001 -> 0x7f030002
// 0xf4: 0x7f030001 -> 0x7f030002
0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f,
// 0xa0: string pool
// 0xfc: string pool
// string length,
0x04, 0x00, 0x00, 0x00,
// 0xa4 string contents "test"
// 0x100 string contents "test"
0x74, 0x65, 0x73, 0x74};
const unsigned int kIdmapRawDataLen = 0xa8;
const unsigned int kIdmapRawDataLen = 0x104;
const unsigned int kIdmapRawDataOffset = 0x54;
const unsigned int kIdmapRawDataTargetCrc = 0x1234;
const unsigned int kIdmapRawOverlayCrc = 0x5678;

View File

@@ -601,10 +601,6 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
return base::unexpected(result.error());
}
if (type_idx == 0x1c) {
LOG(ERROR) << base::StringPrintf("foobar first result %s", result->package_name->c_str());
}
bool overlaid = false;
if (!stop_at_first_match && !ignore_configuration && !apk_assets_[result->cookie]->IsLoader()) {
for (const auto& id_map : package_group.overlays_) {
@@ -615,7 +611,21 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
}
if (overlay_entry.IsInlineValue()) {
// The target resource is overlaid by an inline value not represented by a resource.
result->entry = overlay_entry.GetInlineValue();
ConfigDescription best_frro_config;
Res_value best_frro_value;
bool frro_found = false;
for( const auto& [config, value] : overlay_entry.GetInlineValue()) {
if ((!frro_found || config.isBetterThan(best_frro_config, desired_config))
&& config.match(*desired_config)) {
frro_found = true;
best_frro_config = config;
best_frro_value = value;
}
}
if (!frro_found) {
continue;
}
result->entry = best_frro_value;
result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
result->cookie = id_map.cookie;

View File

@@ -56,6 +56,8 @@ struct Idmap_header {
struct Idmap_data_header {
uint32_t target_entry_count;
uint32_t target_inline_entry_count;
uint32_t target_inline_entry_value_count;
uint32_t configuration_count;
uint32_t overlay_entry_count;
uint32_t string_pool_index_offset;
@@ -68,6 +70,12 @@ struct Idmap_target_entry {
struct Idmap_target_entry_inline {
uint32_t target_id;
uint32_t start_value_index;
uint32_t value_count;
};
struct Idmap_target_entry_inline_value {
uint32_t config_index;
Res_value value;
};
@@ -138,11 +146,15 @@ status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) cons
IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
const Idmap_target_entry* entries,
const Idmap_target_entry_inline* inline_entries,
const Idmap_target_entry_inline_value* inline_entry_values,
const ConfigDescription* configs,
uint8_t target_assigned_package_id,
const OverlayDynamicRefTable* overlay_ref_table)
: data_header_(data_header),
entries_(entries),
inline_entries_(inline_entries),
inline_entry_values_(inline_entry_values),
configurations_(configs),
target_assigned_package_id_(target_assigned_package_id),
overlay_ref_table_(overlay_ref_table) { }
@@ -183,7 +195,13 @@ IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
if (inline_entry != end_inline_entry &&
(0x00FFFFFFU & dtohl(inline_entry->target_id)) == target_res_id) {
return Result(inline_entry->value);
std::map<ConfigDescription, Res_value> values_map;
for (int i = 0; i < inline_entry->value_count; i++) {
const auto& value = inline_entry_values_[inline_entry->start_value_index + i];
const auto& config = configurations_[value.config_index];
values_map[config] = value.value;
}
return Result(values_map);
}
return {};
}
@@ -237,6 +255,8 @@ LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
const Idmap_data_header* data_header,
const Idmap_target_entry* target_entries,
const Idmap_target_entry_inline* target_inline_entries,
const Idmap_target_entry_inline_value* inline_entry_values,
const ConfigDescription* configs,
const Idmap_overlay_entry* overlay_entries,
std::unique_ptr<ResStringPool>&& string_pool,
std::string_view overlay_apk_path,
@@ -245,6 +265,8 @@ LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
data_header_(data_header),
target_entries_(target_entries),
target_inline_entries_(target_inline_entries),
inline_entry_values_(inline_entry_values),
configurations_(configs),
overlay_entries_(overlay_entries),
string_pool_(std::move(string_pool)),
idmap_path_(std::move(idmap_path)),
@@ -303,6 +325,21 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
if (target_inline_entries == nullptr) {
return {};
}
auto target_inline_entry_values = ReadType<Idmap_target_entry_inline_value>(
&data_ptr, &data_size, "target inline values",
dtohl(data_header->target_inline_entry_value_count));
if (target_inline_entry_values == nullptr) {
return {};
}
auto configurations = ReadType<ConfigDescription>(
&data_ptr, &data_size, "configurations",
dtohl(data_header->configuration_count));
if (configurations == 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) {
@@ -329,8 +366,8 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
// Can't use make_unique because LoadedIdmap constructor is private.
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));
target_inline_entries, target_inline_entry_values, configurations,
overlay_entries, std::move(idmap_string_pool), *target_path, *overlay_path));
}
bool LoadedIdmap::IsUpToDate() const {

View File

@@ -23,6 +23,7 @@
#include <variant>
#include "android-base/macros.h"
#include "androidfw/ConfigDescription.h"
#include "androidfw/StringPiece.h"
#include "androidfw/ResourceTypes.h"
#include "utils/ByteOrder.h"
@@ -35,6 +36,7 @@ struct Idmap_header;
struct Idmap_data_header;
struct Idmap_target_entry;
struct Idmap_target_entry_inline;
struct Idmap_target_entry_inline_value;
struct Idmap_overlay_entry;
// A string pool for overlay apk assets. The string pool holds the strings of the overlay resources
@@ -91,7 +93,8 @@ class IdmapResMap {
public:
Result() = default;
explicit Result(uint32_t value) : data_(value) {};
explicit Result(const Res_value& value) : data_(value) { };
explicit Result(const std::map<ConfigDescription, Res_value> &value)
: data_(value) { };
// Returns `true` if the resource is overlaid.
explicit operator bool() const {
@@ -107,15 +110,16 @@ class IdmapResMap {
}
bool IsInlineValue() const {
return std::get_if<Res_value>(&data_) != nullptr;
return std::get_if<2>(&data_) != nullptr;
}
const Res_value& GetInlineValue() const {
return std::get<Res_value>(data_);
const std::map<ConfigDescription, Res_value>& GetInlineValue() const {
return std::get<2>(data_);
}
private:
std::variant<std::monostate, uint32_t, Res_value> data_;
std::variant<std::monostate, uint32_t,
std::map<ConfigDescription, Res_value> > data_;
};
// Looks up the value that overlays the target resource id.
@@ -129,12 +133,16 @@ class IdmapResMap {
explicit IdmapResMap(const Idmap_data_header* data_header,
const Idmap_target_entry* entries,
const Idmap_target_entry_inline* inline_entries,
const Idmap_target_entry_inline_value* inline_entry_values,
const ConfigDescription* configs,
uint8_t target_assigned_package_id,
const OverlayDynamicRefTable* overlay_ref_table);
const Idmap_data_header* data_header_;
const Idmap_target_entry* entries_;
const Idmap_target_entry_inline* inline_entries_;
const Idmap_target_entry_inline_value* inline_entry_values_;
const ConfigDescription* configurations_;
const uint8_t target_assigned_package_id_;
const OverlayDynamicRefTable* overlay_ref_table_;
@@ -170,8 +178,8 @@ class LoadedIdmap {
// Returns a mapping from target resource ids to overlay values.
const IdmapResMap GetTargetResourcesMap(uint8_t target_assigned_package_id,
const OverlayDynamicRefTable* overlay_ref_table) const {
return IdmapResMap(data_header_, target_entries_, target_inline_entries_,
target_assigned_package_id, overlay_ref_table);
return IdmapResMap(data_header_, target_entries_, target_inline_entries_, inline_entry_values_,
configurations_, target_assigned_package_id, overlay_ref_table);
}
// Returns a dynamic reference table for a loaded overlay package.
@@ -191,6 +199,8 @@ class LoadedIdmap {
const Idmap_data_header* data_header_;
const Idmap_target_entry* target_entries_;
const Idmap_target_entry_inline* target_inline_entries_;
const Idmap_target_entry_inline_value* inline_entry_values_;
const ConfigDescription* configurations_;
const Idmap_overlay_entry* overlay_entries_;
const std::unique_ptr<ResStringPool> string_pool_;
@@ -207,6 +217,8 @@ class LoadedIdmap {
const Idmap_data_header* data_header,
const Idmap_target_entry* target_entries,
const Idmap_target_entry_inline* target_inline_entries,
const Idmap_target_entry_inline_value* inline_entry_values_,
const ConfigDescription* configs,
const Idmap_overlay_entry* overlay_entries,
std::unique_ptr<ResStringPool>&& string_pool,
std::string_view overlay_apk_path,

View File

@@ -45,7 +45,7 @@
namespace android {
constexpr const uint32_t kIdmapMagic = 0x504D4449u;
constexpr const uint32_t kIdmapCurrentVersion = 0x00000008u;
constexpr const uint32_t kIdmapCurrentVersion = 0x00000009u;
// This must never change.
constexpr const uint32_t kFabricatedOverlayMagic = 0x4f525246; // FRRO (big endian)
@@ -1098,7 +1098,7 @@ struct ResTable_config
SDKVERSION_ANY = 0
};
enum {
enum {
MINORVERSION_ANY = 0
};

View File

@@ -1098,6 +1098,7 @@ public final class OverlayManagerService extends SystemService {
private void enforceActor(@NonNull OverlayIdentifier overlay, @NonNull String methodName,
int realUserId) throws SecurityException {
OverlayInfo overlayInfo = mImpl.getOverlayInfo(overlay, realUserId);
if (overlayInfo == null) {
throw new IllegalArgumentException("Unable to retrieve overlay information for "
+ overlay);

View File

@@ -519,6 +519,7 @@ final class OverlayManagerServiceImpl {
throws OperationFailedException {
final OverlayIdentifier overlayIdentifier = new OverlayIdentifier(
info.packageName, info.overlayName);
final Set<PackageAndUser> updatedTargets = new ArraySet<>();
OverlayInfo oi = mSettings.getNullableOverlayInfo(overlayIdentifier, userId);
if (oi != null) {