Add encoding of name and actor overlayable fields

Encode the actor and name attributes of <overlayable> elements in the
overlayable chunks. Two chunks cannot have the same name.

Bug: 110869880
Bug: 119390855
Test: aapt2_tests and libandroidfw_tests
Change-Id: I0ed95efef640b3927046b6155d1840f0d96030dd
This commit is contained in:
Ryan Mitchell
2018-12-12 18:45:34 -08:00
parent 56ac01ebb4
commit ef5673a4c4
9 changed files with 248 additions and 91 deletions

View File

@@ -593,7 +593,12 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
return {};
}
// Iterate over the overlayable policy chunks
std::string name;
util::ReadUtf16StringFromDevice(header->name, arraysize(header->name), &name);
std::string actor;
util::ReadUtf16StringFromDevice(header->actor, arraysize(header->actor), &actor);
// Iterate over the overlayable policy chunks contained within the overlayable chunk data
ChunkIterator overlayable_iter(child_chunk.data_ptr(), child_chunk.data_size());
while (overlayable_iter.HasNext()) {
const Chunk overlayable_child_chunk = overlayable_iter.Next();
@@ -613,7 +618,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
return {};
}
// Retrieve all the ids belonging to this policy
// Retrieve all the resource ids belonging to this policy chunk
std::unordered_set<uint32_t> ids;
const auto ids_begin =
reinterpret_cast<const ResTable_ref*>(overlayable_child_chunk.data_ptr());
@@ -622,8 +627,10 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
ids.insert(dtohl(id_iter->ident));
}
// Add the pairing of overlayable properties to resource ids to the package
// Add the pairing of overlayable properties and resource ids to the package
OverlayableInfo overlayable_info{};
overlayable_info.name = name;
overlayable_info.actor = actor;
overlayable_info.policy_flags = policy_header->policy_flags;
loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids));
break;
@@ -636,7 +643,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
}
if (overlayable_iter.HadError()) {
LOG(ERROR) << StringPrintf("Error parsing RES_TABLE_OVERLAYABLE_POLICY_TYPE: %s",
LOG(ERROR) << StringPrintf("Error parsing RES_TABLE_OVERLAYABLE_TYPE: %s",
overlayable_iter.GetLastError().c_str());
if (overlayable_iter.HadFatalError()) {
return {};

View File

@@ -78,6 +78,8 @@ struct TypeSpec {
using TypeSpecPtr = util::unique_cptr<TypeSpec>;
struct OverlayableInfo {
std::string name;
std::string actor;
uint32_t policy_flags;
};

View File

@@ -1611,6 +1611,12 @@ struct ResTable_lib_entry
struct ResTable_overlayable_header
{
struct ResChunk_header header;
// The name of the overlayable set of resources that overlays target.
uint16_t name[256];
// The component responsible for enabling and disabling overlays targeting this chunk.
uint16_t actor[256];
};
/**

View File

@@ -294,22 +294,30 @@ TEST(LoadedArscTest, LoadOverlayable) {
info = package->GetOverlayableInfo(overlayable::R::string::overlayable1);
ASSERT_THAT(info, NotNull());
EXPECT_THAT(info->name, Eq("OverlayableResources1"));
EXPECT_THAT(info->actor, Eq("overlay://theme"));
EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
info = package->GetOverlayableInfo(overlayable::R::string::overlayable2);
ASSERT_THAT(info, NotNull());
EXPECT_THAT(info->name, Eq("OverlayableResources1"));
EXPECT_THAT(info->actor, Eq("overlay://theme"));
EXPECT_THAT(info->policy_flags,
Eq(ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION
| ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
info = package->GetOverlayableInfo(overlayable::R::string::overlayable3);
ASSERT_THAT(info, NotNull());
EXPECT_THAT(info->name, Eq("OverlayableResources2"));
EXPECT_THAT(info->actor, Eq("overlay://com.android.overlayable"));
EXPECT_THAT(info->policy_flags,
Eq(ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION
| ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION
| ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
info = package->GetOverlayableInfo(overlayable::R::string::overlayable4);
EXPECT_THAT(info->name, Eq("OverlayableResources1"));
EXPECT_THAT(info->actor, Eq("overlay://theme"));
ASSERT_THAT(info, NotNull());
EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
}

View File

@@ -15,7 +15,7 @@
-->
<resources>
<overlayable>
<overlayable name="OverlayableResources1" actor="overlay://theme">
<!-- Any overlay can overlay the value of @string/overlayable1 -->
<item type="string" name="overlayable1" />
@@ -31,9 +31,9 @@
</policy>
</overlayable>
<overlayable>
<overlayable name="OverlayableResources2" actor="overlay://com.android.overlayable">
<!-- Any overlay on the product_services, vendor, or product partition can overlay the value of
@string/overlayable3 -->
@string/overlayable3 -->
<policy type="product_services|vendor|product">
<item type="string" name="overlayable3" />
</policy>

View File

@@ -42,6 +42,19 @@ namespace aapt {
namespace {
static std::u16string strcpy16_dtoh(const char16_t* src, size_t len) {
size_t utf16_len = strnlen16(src, len);
if (utf16_len == 0) {
return {};
}
std::u16string dst;
dst.resize(utf16_len);
for (size_t i = 0; i < utf16_len; i++) {
dst[i] = util::DeviceToHost16(src[i]);
}
return dst;
}
// Visitor that converts a reference's resource ID to a resource name, given a mapping from
// resource ID to resource name.
class ReferenceIdToNameVisitor : public DescendingValueVisitor {
@@ -176,12 +189,8 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) {
}
// Extract the package name.
size_t len = strnlen16((const char16_t*)package_header->name, arraysize(package_header->name));
std::u16string package_name;
package_name.resize(len);
for (size_t i = 0; i < len; i++) {
package_name[i] = util::DeviceToHost16(package_header->name[i]);
}
std::u16string package_name = strcpy16_dtoh((const char16_t*)package_header->name,
arraysize(package_header->name));
ResourceTablePackage* package =
table_->CreatePackage(util::Utf16ToUtf8(package_name), static_cast<uint8_t>(package_id));
@@ -435,6 +444,11 @@ bool BinaryResourceParser::ParseOverlayable(const ResChunk_header* chunk) {
}
auto overlayable = std::make_shared<Overlayable>();
overlayable->name = util::Utf16ToUtf8(strcpy16_dtoh((const char16_t*)header->name,
arraysize(header->name)));
overlayable->actor = util::Utf16ToUtf8(strcpy16_dtoh((const char16_t*)header->actor,
arraysize(header->name)));
overlayable->source = source_.WithLine(0);
ResChunkPullParser parser(GetChunkData(chunk),
GetChunkDataLen(chunk));

View File

@@ -217,9 +217,10 @@ class MapFlattenVisitor : public ValueVisitor {
size_t entry_count_ = 0;
};
struct PolicyChunk {
uint32_t policy_flags;
std::set<ResourceId> ids;
struct OverlayableChunk {
std::string actor;
Source source;
std::map<OverlayableItem::PolicyFlags, std::set<ResourceId>> policy_ids;
};
class PackageFlattener {
@@ -421,8 +422,9 @@ class PackageFlattener {
return sorted_entries;
}
void FlattenOverlayable(BigBuffer* buffer) {
std::vector<PolicyChunk> policies;
bool FlattenOverlayable(BigBuffer* buffer) {
std::set<ResourceId> seen_ids;
std::map<std::string, OverlayableChunk> overlayable_chunks;
CHECK(bool(package_->id)) << "package must have an ID set when flattening <overlayable>";
for (auto& type : package_->types) {
@@ -433,79 +435,119 @@ class PackageFlattener {
continue;
}
OverlayableItem& overlayable = entry->overlayable_item.value();
uint32_t policy_flags = OverlayableItem::Policy::kNone;
if (overlayable.policies & OverlayableItem::Policy::kPublic) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
}
if (overlayable.policies & OverlayableItem::Policy::kSystem) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
}
if (overlayable.policies & OverlayableItem::Policy::kVendor) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
}
if (overlayable.policies & OverlayableItem::Policy::kProduct) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
}
if (overlayable.policies & OverlayableItem::Policy::kProductServices) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION;
OverlayableItem& item = entry->overlayable_item.value();
// Resource ids should only appear once in the resource table
ResourceId id = android::make_resid(package_->id.value(), type->id.value(),
entry->id.value());
CHECK(seen_ids.find(id) == seen_ids.end())
<< "multiple overlayable definitions found for resource "
<< ResourceName(package_->name, type->type, entry->name).to_string();
seen_ids.insert(id);
// Find the overlayable chunk with the specified name
OverlayableChunk* overlayable_chunk = nullptr;
auto iter = overlayable_chunks.find(item.overlayable->name);
if (iter == overlayable_chunks.end()) {
OverlayableChunk chunk{item.overlayable->actor, item.overlayable->source};
overlayable_chunk =
&overlayable_chunks.insert({item.overlayable->name, chunk}).first->second;
} else {
OverlayableChunk& chunk = iter->second;
if (!(chunk.source == item.overlayable->source)) {
// The name of an overlayable set of resources must be unique
context_->GetDiagnostics()->Error(DiagMessage(item.overlayable->source)
<< "duplicate overlayable name"
<< item.overlayable->name << "'");
context_->GetDiagnostics()->Error(DiagMessage(chunk.source)
<< "previous declaration here");
return false;
}
CHECK(chunk.actor == item.overlayable->actor);
overlayable_chunk = &chunk;
}
if (overlayable.policies == OverlayableItem::Policy::kNone) {
uint32_t policy_flags = 0;
if (item.policies == OverlayableItem::Policy::kNone) {
// Encode overlayable entries defined without a policy as publicly overlayable
policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
}
// Find the overlayable policy chunk with the same policies as the entry
PolicyChunk* policy_chunk = nullptr;
for (PolicyChunk& policy : policies) {
if (policy.policy_flags == policy_flags) {
policy_chunk = &policy;
break;
} else {
if (item.policies & OverlayableItem::Policy::kPublic) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
}
if (item.policies & OverlayableItem::Policy::kSystem) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
}
if (item.policies & OverlayableItem::Policy::kVendor) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
}
if (item.policies & OverlayableItem::Policy::kProduct) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
}
if (item.policies & OverlayableItem::Policy::kProductServices) {
policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION;
}
}
// Create a new policy chunk if an existing one with the same policy cannot be found
if (policy_chunk == nullptr) {
PolicyChunk p;
p.policy_flags = policy_flags;
policies.push_back(p);
policy_chunk = &policies.back();
auto policy = overlayable_chunk->policy_ids.find(policy_flags);
if (policy != overlayable_chunk->policy_ids.end()) {
policy->second.insert(id);
} else {
overlayable_chunk->policy_ids.insert(
std::make_pair(policy_flags, std::set<ResourceId>{id}));
}
policy_chunk->ids.insert(android::make_resid(package_->id.value(), type->id.value(),
entry->id.value()));
}
}
if (policies.empty()) {
// Only write the overlayable chunk if the APK has overlayable entries
return;
}
for (auto& overlayable_pair : overlayable_chunks) {
std::string name = overlayable_pair.first;
OverlayableChunk& overlayable = overlayable_pair.second;
ChunkWriter writer(buffer);
writer.StartChunk<ResTable_overlayable_header>(RES_TABLE_OVERLAYABLE_TYPE);
// Write each policy block for the overlayable
for (PolicyChunk& policy : policies) {
ChunkWriter policy_writer(buffer);
ResTable_overlayable_policy_header* policy_type =
policy_writer.StartChunk<ResTable_overlayable_policy_header>(
RES_TABLE_OVERLAYABLE_POLICY_TYPE);
policy_type->policy_flags = util::HostToDevice32(policy.policy_flags);
policy_type->entry_count = util::HostToDevice32(static_cast<uint32_t>(policy.ids.size()));
// Write the ids after the policy header
ResTable_ref* id_block = policy_writer.NextBlock<ResTable_ref>(policy.ids.size());
for (const ResourceId& id : policy.ids) {
id_block->ident = util::HostToDevice32(id.id);
id_block++;
// Write the header of the overlayable chunk
ChunkWriter overlayable_writer(buffer);
auto* overlayable_type =
overlayable_writer.StartChunk<ResTable_overlayable_header>(RES_TABLE_OVERLAYABLE_TYPE);
if (name.size() >= arraysize(overlayable_type->name)) {
diag_->Error(DiagMessage() << "overlayable name '" << name
<< "' exceeds maximum length ("
<< arraysize(overlayable_type->name)
<< " utf16 characters)");
return false;
}
strcpy16_htod(overlayable_type->name, arraysize(overlayable_type->name),
util::Utf8ToUtf16(name));
policy_writer.Finish();
if (overlayable.actor.size() >= arraysize(overlayable_type->actor)) {
diag_->Error(DiagMessage() << "overlayable name '" << overlayable.actor
<< "' exceeds maximum length ("
<< arraysize(overlayable_type->actor)
<< " utf16 characters)");
return false;
}
strcpy16_htod(overlayable_type->actor, arraysize(overlayable_type->actor),
util::Utf8ToUtf16(overlayable.actor));
// Write each policy block for the overlayable
for (auto& policy_ids : overlayable.policy_ids) {
ChunkWriter policy_writer(buffer);
auto* policy_type = policy_writer.StartChunk<ResTable_overlayable_policy_header>(
RES_TABLE_OVERLAYABLE_POLICY_TYPE);
policy_type->policy_flags = util::HostToDevice32(static_cast<uint32_t>(policy_ids.first));
policy_type->entry_count = util::HostToDevice32(static_cast<uint32_t>(
policy_ids.second.size()));
// Write the ids after the policy header
auto* id_block = policy_writer.NextBlock<ResTable_ref>(policy_ids.second.size());
for (const ResourceId& id : policy_ids.second) {
id_block->ident = util::HostToDevice32(id.id);
id_block++;
}
policy_writer.Finish();
}
overlayable_writer.Finish();
}
writer.Finish();
return true;
}
bool FlattenTypeSpec(ResourceTableType* type, std::vector<ResourceEntry*>* sorted_entries,

View File

@@ -657,36 +657,36 @@ TEST_F(TableFlattenerTest, FlattenOverlayable) {
TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
auto overlayable = std::make_shared<Overlayable>("TestName", "overlay://theme");
std::string name_zero = "com.app.test:integer/overlayable_zero_item";
OverlayableItem overlayable_zero_item(overlayable);
overlayable_zero_item.policies |= OverlayableItem::Policy::kProduct;
overlayable_zero_item.policies |= OverlayableItem::Policy::kSystem;
overlayable_zero_item.policies |= OverlayableItem::Policy::kProductServices;
OverlayableItem overlayable_item_zero(overlayable);
overlayable_item_zero.policies |= OverlayableItem::Policy::kProduct;
overlayable_item_zero.policies |= OverlayableItem::Policy::kSystem;
overlayable_item_zero.policies |= OverlayableItem::Policy::kProductServices;
std::string name_one = "com.app.test:integer/overlayable_one_item";
OverlayableItem overlayable_one_item(overlayable);
overlayable_one_item.policies |= OverlayableItem::Policy::kPublic;
overlayable_one_item.policies |= OverlayableItem::Policy::kProductServices;
OverlayableItem overlayable_item_one(overlayable);
overlayable_item_one.policies |= OverlayableItem::Policy::kPublic;
overlayable_item_one.policies |= OverlayableItem::Policy::kProductServices;
std::string name_two = "com.app.test:integer/overlayable_two_item";
OverlayableItem overlayable_two_item(overlayable);
overlayable_two_item.policies |= OverlayableItem::Policy::kProduct;
overlayable_two_item.policies |= OverlayableItem::Policy::kSystem;
overlayable_two_item.policies |= OverlayableItem::Policy::kVendor;
OverlayableItem overlayable_item_two(overlayable);
overlayable_item_two.policies |= OverlayableItem::Policy::kProduct;
overlayable_item_two.policies |= OverlayableItem::Policy::kSystem;
overlayable_item_two.policies |= OverlayableItem::Policy::kVendor;
std::string name_three = "com.app.test:integer/overlayable_three_item";
OverlayableItem overlayable_three_item(overlayable);
OverlayableItem overlayable_item_three(overlayable);
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("com.app.test", 0x7f)
.AddSimple(name_zero, ResourceId(0x7f020000))
.SetOverlayable(name_zero, overlayable_zero_item)
.SetOverlayable(name_zero, overlayable_item_zero)
.AddSimple(name_one, ResourceId(0x7f020001))
.SetOverlayable(name_one, overlayable_one_item)
.SetOverlayable(name_one, overlayable_item_one)
.AddSimple(name_two, ResourceId(0x7f020002))
.SetOverlayable(name_two, overlayable_two_item)
.SetOverlayable(name_two, overlayable_item_two)
.AddSimple(name_three, ResourceId(0x7f020003))
.SetOverlayable(name_three, overlayable_three_item)
.SetOverlayable(name_three, overlayable_item_three)
.Build();
ResourceTable output_table;
@@ -724,6 +724,84 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
ASSERT_TRUE(search_result.value().entry->overlayable_item);
overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
EXPECT_EQ(overlayable_item.overlayable->name, "TestName");
EXPECT_EQ(overlayable_item.overlayable->actor, "overlay://theme");
EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
}
TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) {
auto group = std::make_shared<Overlayable>("TestName", "overlay://theme");
std::string name_zero = "com.app.test:integer/overlayable_zero";
OverlayableItem overlayable_item_zero(group);
overlayable_item_zero.policies |= OverlayableItem::Policy::kProduct;
overlayable_item_zero.policies |= OverlayableItem::Policy::kSystem;
overlayable_item_zero.policies |= OverlayableItem::Policy::kProductServices;
auto group_one = std::make_shared<Overlayable>("OtherName", "overlay://customization");
std::string name_one = "com.app.test:integer/overlayable_one";
OverlayableItem overlayable_item_one(group_one);
overlayable_item_one.policies |= OverlayableItem::Policy::kPublic;
overlayable_item_one.policies |= OverlayableItem::Policy::kProductServices;
std::string name_two = "com.app.test:integer/overlayable_two";
OverlayableItem overlayable_item_two(group);
overlayable_item_two.policies |= OverlayableItem::Policy::kProduct;
overlayable_item_two.policies |= OverlayableItem::Policy::kSystem;
overlayable_item_two.policies |= OverlayableItem::Policy::kVendor;
std::string name_three = "com.app.test:integer/overlayable_three";
OverlayableItem overlayable_item_three(group_one);
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("com.app.test", 0x7f)
.AddSimple(name_zero, ResourceId(0x7f020000))
.SetOverlayable(name_zero, overlayable_item_zero)
.AddSimple(name_one, ResourceId(0x7f020001))
.SetOverlayable(name_one, overlayable_item_one)
.AddSimple(name_two, ResourceId(0x7f020002))
.SetOverlayable(name_two, overlayable_item_two)
.AddSimple(name_three, ResourceId(0x7f020003))
.SetOverlayable(name_three, overlayable_item_three)
.Build();
ResourceTable output_table;
ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &output_table));
auto search_result = output_table.FindResource(test::ParseNameOrDie(name_zero));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
ASSERT_TRUE(search_result.value().entry->overlayable_item);
OverlayableItem& result_overlayable = search_result.value().entry->overlayable_item.value();
EXPECT_EQ(result_overlayable.overlayable->name, "TestName");
EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://theme");
EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kSystem
| OverlayableItem::Policy::kProduct
| OverlayableItem::Policy::kProductServices);
search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable = search_result.value().entry->overlayable_item.value();
EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kPublic
| OverlayableItem::Policy::kProductServices);
search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable = search_result.value().entry->overlayable_item.value();
EXPECT_EQ(result_overlayable.overlayable->name, "TestName");
EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://theme");
EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kSystem
| OverlayableItem::Policy::kProduct
| OverlayableItem::Policy::kVendor);
search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable = search_result.value().entry->overlayable_item.value();
EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kPublic);
}
} // namespace aapt