diff options
-rw-r--r-- | tools/aapt2/Debug.cpp | 25 | ||||
-rw-r--r-- | tools/aapt2/ResourceParser.cpp | 164 | ||||
-rw-r--r-- | tools/aapt2/ResourceParser.h | 4 | ||||
-rw-r--r-- | tools/aapt2/ResourceParser_test.cpp | 239 | ||||
-rw-r--r-- | tools/aapt2/ResourceTable.cpp | 39 | ||||
-rw-r--r-- | tools/aapt2/ResourceTable.h | 30 | ||||
-rw-r--r-- | tools/aapt2/ResourceTable_test.cpp | 62 | ||||
-rw-r--r-- | tools/aapt2/Resources.proto | 16 | ||||
-rw-r--r-- | tools/aapt2/format/binary/BinaryResourceParser.cpp | 2 | ||||
-rw-r--r-- | tools/aapt2/format/binary/TableFlattener.cpp | 2 | ||||
-rw-r--r-- | tools/aapt2/format/binary/TableFlattener_test.cpp | 2 | ||||
-rw-r--r-- | tools/aapt2/format/proto/ProtoDeserialize.cpp | 30 | ||||
-rw-r--r-- | tools/aapt2/format/proto/ProtoSerialize.cpp | 28 | ||||
-rw-r--r-- | tools/aapt2/format/proto/ProtoSerialize_test.cpp | 62 | ||||
-rw-r--r-- | tools/aapt2/link/TableMerger.cpp | 40 | ||||
-rw-r--r-- | tools/aapt2/link/TableMerger_test.cpp | 93 | ||||
-rw-r--r-- | tools/aapt2/test/Builders.cpp | 9 | ||||
-rw-r--r-- | tools/aapt2/test/Builders.h | 2 |
18 files changed, 717 insertions, 132 deletions
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp index 0a517ab8a2de..0bc5221245ca 100644 --- a/tools/aapt2/Debug.cpp +++ b/tools/aapt2/Debug.cpp @@ -306,8 +306,29 @@ void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions& break; } - if (entry->overlayable) { - printer->Print(" OVERLAYABLE"); + for (size_t i = 0; i < entry->overlayable_declarations.size(); i++) { + printer->Print((i == 0) ? " " : "|"); + printer->Print("OVERLAYABLE"); + + if (entry->overlayable_declarations[i].policy) { + switch (entry->overlayable_declarations[i].policy.value()) { + case Overlayable::Policy::kProduct: + printer->Print("_PRODUCT"); + break; + case Overlayable::Policy::kProductServices: + printer->Print("_PRODUCT_SERVICES"); + break; + case Overlayable::Policy::kSystem: + printer->Print("_SYSTEM"); + break; + case Overlayable::Policy::kVendor: + printer->Print("_VENDOR"); + break; + case Overlayable::Policy::kPublic: + printer->Print("_PUBLIC"); + break; + } + } } printer->Println(); diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 9a3f14c8e08e..4f25e0968c0e 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -99,7 +99,7 @@ struct ParsedResource { ResourceId id; Visibility::Level visibility_level = Visibility::Level::kUndefined; bool allow_new = false; - bool overlayable = false; + std::vector<Overlayable> overlayable_declarations; std::string comment; std::unique_ptr<Value> value; @@ -133,11 +133,8 @@ static bool AddResourcesToTable(ResourceTable* table, IDiagnostics* diag, Parsed } } - if (res->overlayable) { - Overlayable overlayable; - overlayable.source = res->source; - overlayable.comment = res->comment; - if (!table->SetOverlayable(res->name, overlayable, diag)) { + for (auto& overlayable : res->overlayable_declarations) { + if (!table->AddOverlayable(res->name, overlayable, diag)) { return false; } } @@ -673,7 +670,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, if (can_be_bag) { const auto bag_iter = elToBagMap.find(resource_type); if (bag_iter != elToBagMap.end()) { - // Ensure we have a name (unless this is a <public-group>). + // Ensure we have a name (unless this is a <public-group> or <overlayable>). if (resource_type != "public-group" && resource_type != "overlayable") { if (!maybe_name) { diag_->Error(DiagMessage(out_resource->source) @@ -1062,72 +1059,135 @@ bool ResourceParser::ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource) { if (out_resource->config != ConfigDescription::DefaultConfig()) { diag_->Warn(DiagMessage(out_resource->source) - << "ignoring configuration '" << out_resource->config << "' for <overlayable> tag"); + << "ignoring configuration '" << out_resource->config + << "' for <overlayable> tag"); } - if (Maybe<StringPiece> maybe_policy = xml::FindNonEmptyAttribute(parser, "policy")) { - const StringPiece& policy = maybe_policy.value(); - if (policy != "system") { - diag_->Error(DiagMessage(out_resource->source) - << "<overlayable> has invalid policy '" << policy << "'"); - return false; - } - } + std::string comment; + std::vector<Overlayable::Policy> policies; bool error = false; - const size_t depth = parser->depth(); - while (xml::XmlPullParser::NextChildNode(parser, depth)) { - if (parser->event() != xml::XmlPullParser::Event::kStartElement) { - // Skip text/comments. + const size_t start_depth = parser->depth(); + while (xml::XmlPullParser::IsGoodEvent(parser->Next())) { + xml::XmlPullParser::Event event = parser->event(); + if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth) { + // Break the loop when exiting the overyabale element + break; + } else if (event == xml::XmlPullParser::Event::kEndElement + && parser->depth() == start_depth + 1) { + // Clear the current policies when exiting the policy element + policies.clear(); + continue; + } else if (event == xml::XmlPullParser::Event::kComment) { + // Get the comment of individual item elements + comment = parser->comment(); + continue; + } else if (event != xml::XmlPullParser::Event::kStartElement) { + // Skip to the next element continue; } const Source item_source = source_.WithLine(parser->line_number()); - const std::string& element_namespace = parser->element_namespace(); const std::string& element_name = parser->element_name(); + const std::string& element_namespace = parser->element_namespace(); + if (element_namespace.empty() && element_name == "item") { - Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name"); - if (!maybe_name) { - diag_->Error(DiagMessage(item_source) - << "<item> within an <overlayable> tag must have a 'name' attribute"); + if (!ParseOverlayableItem(parser, policies, comment, out_resource)) { error = true; - continue; } - - Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type"); - if (!maybe_type) { - diag_->Error(DiagMessage(item_source) - << "<item> within an <overlayable> tag must have a 'type' attribute"); + } else if (element_namespace.empty() && element_name == "policy") { + if (!policies.empty()) { + // If the policy list is not empty, then we are currently inside a policy element + diag_->Error(DiagMessage(item_source) << "<policy> blocks cannot be recursively nested"); error = true; - continue; + break; + } else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) { + // Parse the polices separated by vertical bar characters to allow for specifying multiple + // policies at once + for (StringPiece part : util::Tokenize(maybe_type.value(), '|')) { + StringPiece trimmed_part = util::TrimWhitespace(part); + if (trimmed_part == "public") { + policies.push_back(Overlayable::Policy::kPublic); + } else if (trimmed_part == "product") { + policies.push_back(Overlayable::Policy::kProduct); + } else if (trimmed_part == "product_services") { + policies.push_back(Overlayable::Policy::kProductServices); + } else if (trimmed_part == "system") { + policies.push_back(Overlayable::Policy::kSystem); + } else if (trimmed_part == "vendor") { + policies.push_back(Overlayable::Policy::kVendor); + } else { + diag_->Error(DiagMessage(out_resource->source) + << "<policy> has unsupported type '" << trimmed_part << "'"); + error = true; + continue; + } + } } + } else if (!ShouldIgnoreElement(element_namespace, element_name)) { + diag_->Error(DiagMessage(item_source) << "invalid element <" << element_name << "> in " + << " <overlayable>"); + error = true; + break; + } + } - const ResourceType* type = ParseResourceType(maybe_type.value()); - if (type == nullptr) { - diag_->Error(DiagMessage(out_resource->source) + return !error; +} + +bool ResourceParser::ParseOverlayableItem(xml::XmlPullParser* parser, + const std::vector<Overlayable::Policy>& policies, + const std::string& comment, + ParsedResource* out_resource) { + const Source item_source = source_.WithLine(parser->line_number()); + + Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name"); + if (!maybe_name) { + diag_->Error(DiagMessage(item_source) + << "<item> within an <overlayable> tag must have a 'name' attribute"); + return false; + } + + Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type"); + if (!maybe_type) { + diag_->Error(DiagMessage(item_source) + << "<item> within an <overlayable> tag must have a 'type' attribute"); + return false; + } + + const ResourceType* type = ParseResourceType(maybe_type.value()); + if (type == nullptr) { + diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '" << maybe_type.value() << "' in <item> within an <overlayable>"); - error = true; - continue; - } + return false; + } - ParsedResource child_resource; - child_resource.name.type = *type; - child_resource.name.entry = maybe_name.value().to_string(); - child_resource.source = item_source; - child_resource.overlayable = true; - if (options_.visibility) { - child_resource.visibility_level = options_.visibility.value(); - } - out_resource->child_resources.push_back(std::move(child_resource)); + ParsedResource child_resource; + child_resource.name.type = *type; + child_resource.name.entry = maybe_name.value().to_string(); + child_resource.source = item_source; - xml::XmlPullParser::SkipCurrentElement(parser); - } else if (!ShouldIgnoreElement(element_namespace, element_name)) { - diag_->Error(DiagMessage(item_source) << ":" << element_name << ">"); - error = true; + if (policies.empty()) { + Overlayable overlayable; + overlayable.source = item_source; + overlayable.comment = comment; + child_resource.overlayable_declarations.push_back(overlayable); + } else { + for (Overlayable::Policy policy : policies) { + Overlayable overlayable; + overlayable.policy = policy; + overlayable.source = item_source; + overlayable.comment = comment; + child_resource.overlayable_declarations.push_back(overlayable); } } - return !error; + + if (options_.visibility) { + child_resource.visibility_level = options_.visibility.value(); + } + out_resource->child_resources.push_back(std::move(child_resource)); + return true; } bool ResourceParser::ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource) { diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h index 06bb0c9cf264..ebacd6f1280e 100644 --- a/tools/aapt2/ResourceParser.h +++ b/tools/aapt2/ResourceParser.h @@ -96,6 +96,10 @@ class ResourceParser { bool ParseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource); + bool ParseOverlayableItem(xml::XmlPullParser* parser, + const std::vector<Overlayable::Policy>& policies, + const std::string& comment, + ParsedResource* out_resource); bool ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseAttr(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, bool weak); diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp index 0dff66430532..c6f29ac53ca6 100644 --- a/tools/aapt2/ResourceParser_test.cpp +++ b/tools/aapt2/ResourceParser_test.cpp @@ -891,56 +891,162 @@ TEST_F(ResourceParserTest, ParsePlatformIndependentNewline) { ASSERT_TRUE(TestParse(R"(<string name="foo">%1$s %n %2$s</string>)")); } -TEST_F(ResourceParserTest, ParseOverlayableTagWithSystemPolicy) { - std::string input = R"( - <overlayable policy="illegal_policy"> +TEST_F(ResourceParserTest, ParseOverlayable) { + std::string input = R"(<overlayable />)"; + EXPECT_TRUE(TestParse(input)); + + input = R"( + <overlayable> <item type="string" name="foo" /> + <item type="drawable" name="bar" /> </overlayable>)"; - EXPECT_FALSE(TestParse(input)); + ASSERT_TRUE(TestParse(input)); + + auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo")); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); + EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); + EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy); + + search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar")); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); + EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); + EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy); +} + +TEST_F(ResourceParserTest, ParseOverlayablePolicy) { + std::string input = R"(<overlayable />)"; + EXPECT_TRUE(TestParse(input)); input = R"( - <overlayable policy="system"> - <item name="foo" /> + <overlayable> + <item type="string" name="foo" /> + <policy type="product"> + <item type="string" name="bar" /> + </policy> + <policy type="product_services"> + <item type="string" name="baz" /> + </policy> + <policy type="system"> + <item type="string" name="fiz" /> + </policy> + <policy type="vendor"> + <item type="string" name="fuz" /> + </policy> + <policy type="public"> + <item type="string" name="faz" /> + </policy> </overlayable>)"; - EXPECT_FALSE(TestParse(input)); + ASSERT_TRUE(TestParse(input)); - input = R"( - <overlayable policy="system"> - <item type="attr" /> + auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo")); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); + EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); + EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy); + + search_result = table_.FindResource(test::ParseNameOrDie("string/bar")); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); + EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); + EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, + Eq(Overlayable::Policy::kProduct)); + + search_result = table_.FindResource(test::ParseNameOrDie("string/baz")); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); + EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); + EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, + Eq(Overlayable::Policy::kProductServices)); + + search_result = table_.FindResource(test::ParseNameOrDie("string/fiz")); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); + EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); + EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, + Eq(Overlayable::Policy::kSystem)); + + search_result = table_.FindResource(test::ParseNameOrDie("string/fuz")); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); + EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); + EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, + Eq(Overlayable::Policy::kVendor)); + + search_result = table_.FindResource(test::ParseNameOrDie("string/faz")); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); + EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); + EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, + Eq(Overlayable::Policy::kPublic)); +} + +TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) { + std::string input = R"( + <overlayable> + <policy type="illegal_policy"> + <item type="string" name="foo" /> + </policy> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( - <overlayable policy="system"> - <item type="bad_type" name="foo" /> + <overlayable> + <policy type="product"> + <item name="foo" /> + </policy> </overlayable>)"; EXPECT_FALSE(TestParse(input)); - input = R"(<overlayable policy="system" />)"; - EXPECT_TRUE(TestParse(input)); - - input = R"(<overlayable />)"; - EXPECT_TRUE(TestParse(input)); - input = R"( - <overlayable policy="system"> - <item type="string" name="foo" /> - <item type="dimen" name="foo" /> + <overlayable> + <policy type="vendor"> + <item type="string" /> + </policy> </overlayable>)"; - ASSERT_TRUE(TestParse(input)); + EXPECT_FALSE(TestParse(input)); +} - input = R"( +TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) { + std::string input = R"( <overlayable> - <item type="string" name="bar" /> + <policy type="vendor|product_services"> + <item type="string" name="foo" /> + </policy> + <policy type="product|system"> + <item type="string" name="bar" /> + </policy> </overlayable>)"; ASSERT_TRUE(TestParse(input)); - Maybe<ResourceTable::SearchResult> search_result = - table_.FindResource(test::ParseNameOrDie("string/bar")); + auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo")); + ASSERT_TRUE(search_result); + ASSERT_THAT(search_result.value().entry, NotNull()); + EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); + EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(2)); + EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, + Eq(Overlayable::Policy::kVendor)); + EXPECT_THAT(search_result.value().entry->overlayable_declarations[1].policy, + Eq(Overlayable::Policy::kProductServices)); + + search_result = table_.FindResource(test::ParseNameOrDie("string/bar")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); - EXPECT_TRUE(search_result.value().entry->overlayable); + EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(2)); + EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, + Eq(Overlayable::Policy::kProduct)); + EXPECT_THAT(search_result.value().entry->overlayable_declarations[1].policy, + Eq(Overlayable::Policy::kSystem)); } TEST_F(ResourceParserTest, DuplicateOverlayableIsError) { @@ -950,6 +1056,85 @@ TEST_F(ResourceParserTest, DuplicateOverlayableIsError) { <item type="string" name="foo" /> </overlayable>)"; EXPECT_FALSE(TestParse(input)); + + input = R"( + <overlayable> + <item type="string" name="foo" /> + </overlayable> + <overlayable> + <item type="string" name="foo" /> + </overlayable>)"; + EXPECT_FALSE(TestParse(input)); + + input = R"( + <overlayable"> + <policy type="product"> + <item type="string" name="foo" /> + <item type="string" name="foo" /> + </policy> + </overlayable>)"; + EXPECT_FALSE(TestParse(input)); + + input = R"( + <overlayable> + <policy type="product"> + <item type="string" name="foo" /> + </policy> + </overlayable> + + <overlayable> + <policy type="product"> + <item type="string" name="foo" /> + </policy> + </overlayable>)"; + EXPECT_FALSE(TestParse(input)); +} + +TEST_F(ResourceParserTest, PolicyAndNonPolicyOverlayableError) { + std::string input = R"( + <overlayable policy="product"> + <item type="string" name="foo" /> + </overlayable> + <overlayable policy=""> + <item type="string" name="foo" /> + </overlayable>)"; + EXPECT_FALSE(TestParse(input)); + + input = R"( + <overlayable policy=""> + <item type="string" name="foo" /> + </overlayable> + <overlayable policy="product"> + <item type="string" name="foo" /> + </overlayable>)"; + EXPECT_FALSE(TestParse(input)); +} + +TEST_F(ResourceParserTest, DuplicateOverlayableMultiplePolicyError) { + std::string input = R"( + <overlayable> + <policy type="vendor|product"> + <item type="string" name="foo" /> + </policy> + </overlayable> + <overlayable> + <policy type="product_services|vendor"> + <item type="string" name="foo" /> + </policy> + </overlayable>)"; + EXPECT_FALSE(TestParse(input)); +} + +TEST_F(ResourceParserTest, NestPolicyInOverlayableError) { + std::string input = R"( + <overlayable> + <policy type="vendor|product"> + <policy type="product_services"> + <item type="string" name="foo" /> + </policy> + </policy> + </overlayable>)"; + EXPECT_FALSE(TestParse(input)); } TEST_F(ResourceParserTest, ParseIdItem) { diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp index 056a27bf011d..bc8a4d1f85b8 100644 --- a/tools/aapt2/ResourceTable.cpp +++ b/tools/aapt2/ResourceTable.cpp @@ -625,17 +625,17 @@ bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& return true; } -bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable, +bool ResourceTable::AddOverlayable(const ResourceNameRef& name, const Overlayable& overlayable, IDiagnostics* diag) { - return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag); + return AddOverlayableImpl(name, overlayable, ResourceNameValidator, diag); } -bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name, +bool ResourceTable::AddOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable, IDiagnostics* diag) { - return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag); + return AddOverlayableImpl(name, overlayable, SkipNameValidator, diag); } -bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable, +bool ResourceTable::AddOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable, NameValidator name_validator, IDiagnostics* diag) { CHECK(diag != nullptr); @@ -646,13 +646,28 @@ bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overla ResourceTablePackage* package = FindOrCreatePackage(name.package); ResourceTableType* type = package->FindOrCreateType(name.type); ResourceEntry* entry = type->FindOrCreateEntry(name.entry); - if (entry->overlayable) { - diag->Error(DiagMessage(overlayable.source) - << "duplicate overlayable declaration for resource '" << name << "'"); - diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here"); - return false; + + for (auto& overlayable_declaration : entry->overlayable_declarations) { + // An overlayable resource cannot be declared twice with the same policy + if (overlayable.policy == overlayable_declaration.policy) { + diag->Error(DiagMessage(overlayable.source) + << "duplicate overlayable declaration for resource '" << name << "'"); + diag->Error(DiagMessage(overlayable_declaration.source) << "previous declaration here"); + return false; + } + + // An overlayable resource cannot be declared once with a policy and without a policy because + // the policy becomes unused + if (!overlayable.policy || !overlayable_declaration.policy) { + diag->Error(DiagMessage(overlayable.source) + << "overlayable resource '" << name << "'" + << " declared once with a policy and once with no policy"); + diag->Error(DiagMessage(overlayable_declaration.source) << "previous declaration here"); + return false; + } } - entry->overlayable = overlayable; + + entry->overlayable_declarations.push_back(overlayable); return true; } @@ -688,7 +703,7 @@ std::unique_ptr<ResourceTable> ResourceTable::Clone() const { new_entry->id = entry->id; new_entry->visibility = entry->visibility; new_entry->allow_new = entry->allow_new; - new_entry->overlayable = entry->overlayable; + new_entry->overlayable_declarations = entry->overlayable_declarations; for (const auto& config_value : entry->values) { ResourceConfigValue* new_value = diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h index 1917d7e78c4f..3dd0a769d944 100644 --- a/tools/aapt2/ResourceTable.h +++ b/tools/aapt2/ResourceTable.h @@ -57,8 +57,27 @@ struct AllowNew { std::string comment; }; -// The policy dictating whether an entry is overlayable at runtime by RROs. +// Represents a declaration that a resource is overayable at runtime. struct Overlayable { + // Represents the types overlays that are allowed to overlay the resource. + enum class Policy { + // The resource can be overlaid by any overlay. + kPublic, + + // The resource can be overlaid by any overlay on the system partition. + kSystem, + + // The resource can be overlaid by any overlay on the vendor partition. + kVendor, + + // The resource can be overlaid by any overlay on the product partition. + kProduct, + + // The resource can be overlaid by any overlay on the product services partition. + kProductServices, + }; + + Maybe<Policy> policy; Source source; std::string comment; }; @@ -96,7 +115,8 @@ class ResourceEntry { Maybe<AllowNew> allow_new; - Maybe<Overlayable> overlayable; + // The declarations of this resource as overlayable for RROs + std::vector<Overlayable> overlayable_declarations; // The resource's values for each configuration. std::vector<std::unique_ptr<ResourceConfigValue>> values; @@ -226,9 +246,9 @@ class ResourceTable { bool SetVisibilityWithIdMangled(const ResourceNameRef& name, const Visibility& visibility, const ResourceId& res_id, IDiagnostics* diag); - bool SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable, + bool AddOverlayable(const ResourceNameRef& name, const Overlayable& overlayable, IDiagnostics* diag); - bool SetOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable, + bool AddOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable, IDiagnostics* diag); bool SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new, IDiagnostics* diag); @@ -303,7 +323,7 @@ class ResourceTable { bool SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new, NameValidator name_validator, IDiagnostics* diag); - bool SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable, + bool AddOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable, NameValidator name_validator, IDiagnostics* diag); bool SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id, diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp index 05c6f1531d34..7c28f07d0f66 100644 --- a/tools/aapt2/ResourceTable_test.cpp +++ b/tools/aapt2/ResourceTable_test.cpp @@ -242,21 +242,69 @@ TEST(ResourceTableTest, SetAllowNew) { ASSERT_THAT(result.value().entry->allow_new.value().comment, StrEq("second")); } -TEST(ResourceTableTest, SetOverlayable) { +TEST(ResourceTableTest, AddOverlayable) { ResourceTable table; const ResourceName name = test::ParseNameOrDie("android:string/foo"); Overlayable overlayable; - + overlayable.policy = Overlayable::Policy::kProduct; overlayable.comment = "first"; - ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics())); + ASSERT_TRUE(table.AddOverlayable(name, overlayable, test::GetDiagnostics())); Maybe<ResourceTable::SearchResult> result = table.FindResource(name); ASSERT_TRUE(result); - ASSERT_TRUE(result.value().entry->overlayable); - ASSERT_THAT(result.value().entry->overlayable.value().comment, StrEq("first")); + ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(1)); + ASSERT_THAT(result.value().entry->overlayable_declarations[0].comment, StrEq("first")); + ASSERT_THAT(result.value().entry->overlayable_declarations[0].policy, + Eq(Overlayable::Policy::kProduct)); + + Overlayable overlayable2; + overlayable2.comment = "second"; + overlayable2.policy = Overlayable::Policy::kProductServices; + ASSERT_TRUE(table.AddOverlayable(name, overlayable2, test::GetDiagnostics())); + result = table.FindResource(name); + ASSERT_TRUE(result); + ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2)); + ASSERT_THAT(result.value().entry->overlayable_declarations[0].comment, StrEq("first")); + ASSERT_THAT(result.value().entry->overlayable_declarations[0].policy, + Eq(Overlayable::Policy::kProduct)); + ASSERT_THAT(result.value().entry->overlayable_declarations[1].comment, StrEq("second")); + ASSERT_THAT(result.value().entry->overlayable_declarations[1].policy, + Eq(Overlayable::Policy::kProductServices)); +} + +TEST(ResourceTableTest, AddDuplicateOverlayableFail) { + ResourceTable table; + const ResourceName name = test::ParseNameOrDie("android:string/foo"); + + Overlayable overlayable; + overlayable.policy = Overlayable::Policy::kProduct; + ASSERT_TRUE(table.AddOverlayable(name, overlayable, test::GetDiagnostics())); + + Overlayable overlayable2; + overlayable2.policy = Overlayable::Policy::kProduct; + ASSERT_FALSE(table.AddOverlayable(name, overlayable2, test::GetDiagnostics())); +} + +TEST(ResourceTableTest, AddOverlayablePolicyAndNoneFirstFail) { + ResourceTable table; + const ResourceName name = test::ParseNameOrDie("android:string/foo"); + + ASSERT_TRUE(table.AddOverlayable(name, {}, test::GetDiagnostics())); + + Overlayable overlayable2; + overlayable2.policy = Overlayable::Policy::kProduct; + ASSERT_FALSE(table.AddOverlayable(name, overlayable2, test::GetDiagnostics())); +} + +TEST(ResourceTableTest, AddOverlayablePolicyAndNoneLastFail) { + ResourceTable table; + const ResourceName name = test::ParseNameOrDie("android:string/foo"); + + Overlayable overlayable; + overlayable.policy = Overlayable::Policy::kProduct; + ASSERT_TRUE(table.AddOverlayable(name, overlayable, test::GetDiagnostics())); - overlayable.comment = "second"; - ASSERT_FALSE(table.SetOverlayable(name, overlayable, test::GetDiagnostics())); + ASSERT_FALSE(table.AddOverlayable(name, {}, test::GetDiagnostics())); } TEST(ResourceTableTest, AllowDuplictaeResourcesNames) { diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto index d7a377176fc5..bf9fe49da2d6 100644 --- a/tools/aapt2/Resources.proto +++ b/tools/aapt2/Resources.proto @@ -133,13 +133,25 @@ message AllowNew { string comment = 2; } -// Whether a resource is overlayable by runtime resource overlays (RRO). +// Represents a declaration that a resource is overayable at runtime. message Overlayable { + enum Policy { + NONE = 0; + PUBLIC = 1; + SYSTEM = 2; + VENDOR = 3; + PRODUCT = 4; + PRODUCT_SERVICES = 5; + } + // Where this declaration was defined in source. Source source = 1; // Any comment associated with the declaration. string comment = 2; + + // The policy of the overlayable declaration + Policy policy = 3; } // An entry ID in the range [0x0000, 0xffff]. @@ -169,7 +181,7 @@ message Entry { AllowNew allow_new = 4; // Whether this resource can be overlaid by a runtime resource overlay (RRO). - Overlayable overlayable = 5; + repeated Overlayable overlayable = 5; // The set of values defined for this entry, each corresponding to a different // configuration/variant. diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp index 3a39a6baeeeb..ed70fb3c57d6 100644 --- a/tools/aapt2/format/binary/BinaryResourceParser.cpp +++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp @@ -398,7 +398,7 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package, if (type_spec_flags & ResTable_typeSpec::SPEC_OVERLAYABLE) { Overlayable overlayable; overlayable.source = source_.WithLine(0); - if (!table_->SetOverlayableMangled(name, overlayable, diag_)) { + if (!table_->AddOverlayableMangled(name, overlayable, diag_)) { return false; } } diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp index 8641a7c93d19..8a86f63a30c1 100644 --- a/tools/aapt2/format/binary/TableFlattener.cpp +++ b/tools/aapt2/format/binary/TableFlattener.cpp @@ -446,7 +446,7 @@ class PackageFlattener { config_masks[entry->id.value()] |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC); } - if (entry->overlayable) { + if (!entry->overlayable_declarations.empty()) { config_masks[entry->id.value()] |= util::HostToDevice32(ResTable_typeSpec::SPEC_OVERLAYABLE); } diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp index af19b98e528b..cd1414c7e628 100644 --- a/tools/aapt2/format/binary/TableFlattener_test.cpp +++ b/tools/aapt2/format/binary/TableFlattener_test.cpp @@ -634,7 +634,7 @@ TEST_F(TableFlattenerTest, FlattenOverlayable) { .AddSimple("com.app.test:integer/overlayable", ResourceId(0x7f020000)) .Build(); - ASSERT_TRUE(table->SetOverlayable(test::ParseNameOrDie("com.app.test:integer/overlayable"), + ASSERT_TRUE(table->AddOverlayable(test::ParseNameOrDie("com.app.test:integer/overlayable"), Overlayable{}, test::GetDiagnostics())); ResTable res_table; diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp index d1b2fdb84afc..f612914269de 100644 --- a/tools/aapt2/format/proto/ProtoDeserialize.cpp +++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp @@ -437,15 +437,37 @@ static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStr entry->allow_new = std::move(allow_new); } - if (pb_entry.has_overlayable()) { - const pb::Overlayable& pb_overlayable = pb_entry.overlayable(); - + for (const pb::Overlayable& pb_overlayable : pb_entry.overlayable()) { Overlayable overlayable; + switch (pb_overlayable.policy()) { + case pb::Overlayable::NONE: + overlayable.policy = {}; + break; + case pb::Overlayable::PUBLIC: + overlayable.policy = Overlayable::Policy::kPublic; + break; + case pb::Overlayable::PRODUCT: + overlayable.policy = Overlayable::Policy::kProduct; + break; + case pb::Overlayable::PRODUCT_SERVICES: + overlayable.policy = Overlayable::Policy::kProductServices; + break; + case pb::Overlayable::SYSTEM: + overlayable.policy = Overlayable::Policy::kSystem; + break; + case pb::Overlayable::VENDOR: + overlayable.policy = Overlayable::Policy::kVendor; + break; + default: + *out_error = "unknown overlayable policy"; + return false; + } + if (pb_overlayable.has_source()) { DeserializeSourceFromPb(pb_overlayable.source(), src_pool, &overlayable.source); } overlayable.comment = pb_overlayable.comment(); - entry->overlayable = std::move(overlayable); + entry->overlayable_declarations.push_back(overlayable); } ResourceId resid(pb_package.package_id().id(), pb_type.type_id().id(), diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp index 7e35ea7bb7a3..f1e96d61191b 100644 --- a/tools/aapt2/format/proto/ProtoSerialize.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize.cpp @@ -310,11 +310,31 @@ void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table pb_allow_new->set_comment(entry->allow_new.value().comment); } - if (entry->overlayable) { - pb::Overlayable* pb_overlayable = pb_entry->mutable_overlayable(); - SerializeSourceToPb(entry->overlayable.value().source, &source_pool, + for (const Overlayable& overlayable : entry->overlayable_declarations) { + pb::Overlayable* pb_overlayable = pb_entry->add_overlayable(); + if (overlayable.policy) { + switch (overlayable.policy.value()) { + case Overlayable::Policy::kPublic: + pb_overlayable->set_policy(pb::Overlayable::PUBLIC); + break; + case Overlayable::Policy::kProduct: + pb_overlayable->set_policy(pb::Overlayable::PRODUCT); + break; + case Overlayable::Policy::kProductServices: + pb_overlayable->set_policy(pb::Overlayable::PRODUCT_SERVICES); + break; + case Overlayable::Policy::kSystem: + pb_overlayable->set_policy(pb::Overlayable::SYSTEM); + break; + case Overlayable::Policy::kVendor: + pb_overlayable->set_policy(pb::Overlayable::VENDOR); + break; + } + } + + SerializeSourceToPb(overlayable.source, &source_pool, pb_overlayable->mutable_source()); - pb_overlayable->set_comment(entry->overlayable.value().comment); + pb_overlayable->set_comment(overlayable.comment); } for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) { diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp index 3c4d41ae5d1a..95dbbeb58a5d 100644 --- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp @@ -93,7 +93,7 @@ TEST(ProtoSerializeTest, SerializeSinglePackage) { util::make_unique<Reference>(expected_ref), context->GetDiagnostics())); // Make an overlayable resource. - ASSERT_TRUE(table->SetOverlayable(test::ParseNameOrDie("com.app.a:integer/overlayable"), + ASSERT_TRUE(table->AddOverlayable(test::ParseNameOrDie("com.app.a:integer/overlayable"), Overlayable{}, test::GetDiagnostics())); pb::ResourceTable pb_table; @@ -106,7 +106,7 @@ TEST(ProtoSerializeTest, SerializeSinglePackage) { ResourceTable new_table; std::string error; - ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error)); + ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error)) << error; EXPECT_THAT(error, IsEmpty()); Id* new_id = test::GetValue<Id>(&new_table, "com.app.a:id/foo"); @@ -160,7 +160,8 @@ TEST(ProtoSerializeTest, SerializeSinglePackage) { new_table.FindResource(test::ParseNameOrDie("com.app.a:integer/overlayable")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); - EXPECT_TRUE(search_result.value().entry->overlayable); + EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); + EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy); } TEST(ProtoSerializeTest, SerializeAndDeserializeXml) { @@ -464,4 +465,59 @@ TEST(ProtoSerializeTest, SerializeDeserializeConfiguration) { "night-xhdpi-stylus-keysexposed-qwerty-navhidden-dpad-300x200-v23"); } +TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); + std::unique_ptr<ResourceTable> table = + test::ResourceTableBuilder() + .AddOverlayable("com.app.a:bool/foo", Overlayable::Policy::kSystem) + .AddOverlayable("com.app.a:bool/foo", Overlayable::Policy::kProduct) + .AddOverlayable("com.app.a:bool/bar", Overlayable::Policy::kProductServices) + .AddOverlayable("com.app.a:bool/bar", Overlayable::Policy::kVendor) + .AddOverlayable("com.app.a:bool/baz", Overlayable::Policy::kPublic) + .AddOverlayable("com.app.a:bool/biz", {}) + .AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true")) + .Build(); + + pb::ResourceTable pb_table; + SerializeTableToPb(*table, &pb_table, context->GetDiagnostics()); + + MockFileCollection files; + ResourceTable new_table; + std::string error; + ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error)); + EXPECT_THAT(error, IsEmpty()); + + Maybe<ResourceTable::SearchResult> result = + new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo")); + ASSERT_TRUE(result); + ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2)); + EXPECT_THAT(result.value().entry->overlayable_declarations[0].policy, + Eq(Overlayable::Policy::kSystem)); + EXPECT_THAT(result.value().entry->overlayable_declarations[1].policy, + Eq(Overlayable::Policy::kProduct)); + + result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar")); + ASSERT_TRUE(result); + ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2)); + EXPECT_THAT(result.value().entry->overlayable_declarations[0].policy, + Eq(Overlayable::Policy::kProductServices)); + EXPECT_THAT(result.value().entry->overlayable_declarations[1].policy, + Eq(Overlayable::Policy::kVendor)); + + result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz")); + ASSERT_TRUE(result); + ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(1)); + EXPECT_THAT(result.value().entry->overlayable_declarations[0].policy, + Eq(Overlayable::Policy::kPublic)); + + result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz")); + ASSERT_TRUE(result); + ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(1)); + EXPECT_FALSE(result.value().entry->overlayable_declarations[0].policy); + + result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz")); + ASSERT_TRUE(result); + EXPECT_THAT(result.value().entry->overlayable_declarations.size(), Eq(0)); +} + } // namespace aapt diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp index afb8ae097449..d777e22fa4b7 100644 --- a/tools/aapt2/link/TableMerger.cpp +++ b/tools/aapt2/link/TableMerger.cpp @@ -101,7 +101,7 @@ static bool MergeType(IAaptContext* context, const Source& src, ResourceTableTyp return true; } -static bool MergeEntry(IAaptContext* context, const Source& src, bool overlay, +static bool MergeEntry(IAaptContext* context, const Source& src, ResourceEntry* dst_entry, ResourceEntry* src_entry, bool strict_visibility) { if (strict_visibility @@ -134,17 +134,35 @@ static bool MergeEntry(IAaptContext* context, const Source& src, bool overlay, dst_entry->allow_new = std::move(src_entry->allow_new); } - if (src_entry->overlayable) { - if (dst_entry->overlayable && !overlay) { - context->GetDiagnostics()->Error(DiagMessage(src_entry->overlayable.value().source) - << "duplicate overlayable declaration for resource '" - << src_entry->name << "'"); - context->GetDiagnostics()->Error(DiagMessage(dst_entry->overlayable.value().source) - << "previous declaration here"); - return false; + for (auto& src_overlayable : src_entry->overlayable_declarations) { + for (auto& dst_overlayable : dst_entry->overlayable_declarations) { + // An overlayable resource cannot be declared twice with the same policy + if (src_overlayable.policy == dst_overlayable.policy) { + context->GetDiagnostics()->Error(DiagMessage(src_overlayable.source) + << "duplicate overlayable declaration for resource '" + << src_entry->name << "'"); + context->GetDiagnostics()->Error(DiagMessage(dst_overlayable.source) + << "previous declaration here"); + return false; + } + + // An overlayable resource cannot be declared once with a policy and without a policy because + // the policy becomes unused + if (!src_overlayable.policy || !dst_overlayable.policy) { + context->GetDiagnostics()->Error(DiagMessage(src_overlayable.source) + << "overlayable resource '" << src_entry->name + << "' declared once with a policy and once with no " + << "policy"); + context->GetDiagnostics()->Error(DiagMessage(dst_overlayable.source) + << "previous declaration here"); + return false; + } } - dst_entry->overlayable = std::move(src_entry->overlayable); } + + dst_entry->overlayable_declarations.insert(dst_entry->overlayable_declarations.end(), + src_entry->overlayable_declarations.begin(), + src_entry->overlayable_declarations.end()); return true; } @@ -244,7 +262,7 @@ bool TableMerger::DoMerge(const Source& src, ResourceTable* src_table, continue; } - if (!MergeEntry(context_, src, overlay, dst_entry, src_entry.get(), options_.strict_visibility)) { + if (!MergeEntry(context_, src, dst_entry, src_entry.get(), options_.strict_visibility)) { error = true; continue; } diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp index 79a734bffabd..d6579d37b452 100644 --- a/tools/aapt2/link/TableMerger_test.cpp +++ b/tools/aapt2/link/TableMerger_test.cpp @@ -436,4 +436,97 @@ TEST_F(TableMergerTest, OverlaidStyleablesAndStylesShouldBeMerged) { Eq(make_value(Reference(test::ParseNameOrDie("com.app.a:style/OverlayParent"))))); } +TEST_F(TableMergerTest, AddOverlayable) { + std::unique_ptr<ResourceTable> table_a = + test::ResourceTableBuilder() + .SetPackageId("com.app.a", 0x7f) + .AddOverlayable("bool/foo", Overlayable::Policy::kProduct) + .Build(); + + std::unique_ptr<ResourceTable> table_b = + test::ResourceTableBuilder() + .SetPackageId("com.app.a", 0x7f) + .AddOverlayable("bool/foo", Overlayable::Policy::kProductServices) + .Build(); + + ResourceTable final_table; + TableMergerOptions options; + options.auto_add_overlay = true; + TableMerger merger(context_.get(), &final_table, options); + ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/)); + ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/)); + + const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo"); + Maybe<ResourceTable::SearchResult> result = final_table.FindResource(name); + ASSERT_TRUE(result); + ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2)); + ASSERT_THAT(result.value().entry->overlayable_declarations[0].policy, + Eq(Overlayable::Policy::kProduct)); + ASSERT_THAT(result.value().entry->overlayable_declarations[1].policy, + Eq(Overlayable::Policy::kProductServices)); +} + +TEST_F(TableMergerTest, AddDuplicateOverlayableFail) { + std::unique_ptr<ResourceTable> table_a = + test::ResourceTableBuilder() + .SetPackageId("com.app.a", 0x7f) + .AddOverlayable("bool/foo", Overlayable::Policy::kProduct) + .Build(); + + std::unique_ptr<ResourceTable> table_b = + test::ResourceTableBuilder() + .SetPackageId("com.app.a", 0x7f) + .AddOverlayable("bool/foo", Overlayable::Policy::kProduct) + .Build(); + + ResourceTable final_table; + TableMergerOptions options; + options.auto_add_overlay = true; + TableMerger merger(context_.get(), &final_table, options); + ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/)); + ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/)); +} + +TEST_F(TableMergerTest, AddOverlayablePolicyAndNoneFirstFail) { + std::unique_ptr<ResourceTable> table_a = + test::ResourceTableBuilder() + .SetPackageId("com.app.a", 0x7f) + .AddOverlayable("bool/foo", {}) + .Build(); + + std::unique_ptr<ResourceTable> table_b = + test::ResourceTableBuilder() + .SetPackageId("com.app.a", 0x7f) + .AddOverlayable("bool/foo", Overlayable::Policy::kProduct) + .Build(); + + ResourceTable final_table; + TableMergerOptions options; + options.auto_add_overlay = true; + TableMerger merger(context_.get(), &final_table, options); + ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/)); + ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/)); +} + +TEST_F(TableMergerTest, AddOverlayablePolicyAndNoneLastFail) { + std::unique_ptr<ResourceTable> table_a = + test::ResourceTableBuilder() + .SetPackageId("com.app.a", 0x7f) + .AddOverlayable("bool/foo", Overlayable::Policy::kProduct) + .Build(); + + std::unique_ptr<ResourceTable> table_b = + test::ResourceTableBuilder() + .SetPackageId("com.app.a", 0x7f) + .AddOverlayable("bool/foo", {}) + .Build(); + + ResourceTable final_table; + TableMergerOptions options; + options.auto_add_overlay = true; + TableMerger merger(context_.get(), &final_table, options); + ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/)); + ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/)); +} + } // namespace aapt diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp index f33ae3155192..03b59e033402 100644 --- a/tools/aapt2/test/Builders.cpp +++ b/tools/aapt2/test/Builders.cpp @@ -135,6 +135,15 @@ ResourceTableBuilder& ResourceTableBuilder::SetSymbolState(const StringPiece& na return *this; } +ResourceTableBuilder& ResourceTableBuilder::AddOverlayable(const StringPiece& name, + const Maybe<Overlayable::Policy> p) { + ResourceName res_name = ParseNameOrDie(name); + Overlayable overlayable; + overlayable.policy = p; + CHECK(table_->AddOverlayable(res_name, overlayable, GetDiagnostics())); + return *this; +} + StringPool* ResourceTableBuilder::string_pool() { return &table_->string_pool; } diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h index 915959972bed..d68c24ddc665 100644 --- a/tools/aapt2/test/Builders.h +++ b/tools/aapt2/test/Builders.h @@ -73,6 +73,8 @@ class ResourceTableBuilder { const ResourceId& id, std::unique_ptr<Value> value); ResourceTableBuilder& SetSymbolState(const android::StringPiece& name, const ResourceId& id, Visibility::Level level, bool allow_new = false); + ResourceTableBuilder& AddOverlayable(const android::StringPiece& name, + Maybe<Overlayable::Policy> policy); StringPool* string_pool(); std::unique_ptr<ResourceTable> Build(); |