diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 5a267804ddf14..70ce9bc705ef3 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -593,7 +593,12 @@ std::unique_ptr 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 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 ids; const auto ids_begin = reinterpret_cast(overlayable_child_chunk.data_ptr()); @@ -622,8 +627,10 @@ std::unique_ptr 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 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 {}; diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 8c5c3b7d3858e..be62f30617bf2 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -78,6 +78,8 @@ struct TypeSpec { using TypeSpecPtr = util::unique_cptr; struct OverlayableInfo { + std::string name; + std::string actor; uint32_t policy_flags; }; diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index cf2d8fb3251c3..5947dc8be4d80 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -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]; }; /** diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 22d587a7f5c41..2e386a0831858 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -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)); } diff --git a/libs/androidfw/tests/data/overlayable/overlayable.apk b/libs/androidfw/tests/data/overlayable/overlayable.apk index 85ab4be7a2e57..863474794d00d 100644 Binary files a/libs/androidfw/tests/data/overlayable/overlayable.apk and b/libs/androidfw/tests/data/overlayable/overlayable.apk differ diff --git a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml index 11aa7354901d6..dba7b08628f1c 100644 --- a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml +++ b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml @@ -15,7 +15,7 @@ --> - + @@ -31,9 +31,9 @@ - + + @string/overlayable3 --> diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp index c496ff0e159b3..7d4c6f3484039 100644 --- a/tools/aapt2/format/binary/BinaryResourceParser.cpp +++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp @@ -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(package_id)); @@ -435,6 +444,11 @@ bool BinaryResourceParser::ParseOverlayable(const ResChunk_header* chunk) { } auto overlayable = std::make_shared(); + 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)); diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp index 931d57b1c08a3..c4ecbafc008b0 100644 --- a/tools/aapt2/format/binary/TableFlattener.cpp +++ b/tools/aapt2/format/binary/TableFlattener.cpp @@ -217,9 +217,10 @@ class MapFlattenVisitor : public ValueVisitor { size_t entry_count_ = 0; }; -struct PolicyChunk { - uint32_t policy_flags; - std::set ids; +struct OverlayableChunk { + std::string actor; + Source source; + std::map> policy_ids; }; class PackageFlattener { @@ -421,8 +422,9 @@ class PackageFlattener { return sorted_entries; } - void FlattenOverlayable(BigBuffer* buffer) { - std::vector policies; + bool FlattenOverlayable(BigBuffer* buffer) { + std::set seen_ids; + std::map overlayable_chunks; CHECK(bool(package_->id)) << "package must have an ID set when flattening "; 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{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(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( - RES_TABLE_OVERLAYABLE_POLICY_TYPE); - policy_type->policy_flags = util::HostToDevice32(policy.policy_flags); - policy_type->entry_count = util::HostToDevice32(static_cast(policy.ids.size())); - - // Write the ids after the policy header - ResTable_ref* id_block = policy_writer.NextBlock(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(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( + RES_TABLE_OVERLAYABLE_POLICY_TYPE); + policy_type->policy_flags = util::HostToDevice32(static_cast(policy_ids.first)); + policy_type->entry_count = util::HostToDevice32(static_cast( + policy_ids.second.size())); + // Write the ids after the policy header + auto* id_block = policy_writer.NextBlock(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* sorted_entries, diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp index a5fb6fd6d7aa0..18fecf60c9779 100644 --- a/tools/aapt2/format/binary/TableFlattener_test.cpp +++ b/tools/aapt2/format/binary/TableFlattener_test.cpp @@ -657,36 +657,36 @@ TEST_F(TableFlattenerTest, FlattenOverlayable) { TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) { auto overlayable = std::make_shared("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 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("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("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 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