Loading libs/androidfw/LoadedArsc.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -623,7 +623,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, } // Add the pairing of overlayable properties to resource ids to the package OverlayableInfo overlayable_info; OverlayableInfo overlayable_info{}; overlayable_info.policy_flags = policy_header->policy_flags; loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids)); break; Loading tools/aapt2/ResourceParser.cpp +65 −36 Original line number Diff line number Diff line Loading @@ -99,7 +99,7 @@ struct ParsedResource { ResourceId id; Visibility::Level visibility_level = Visibility::Level::kUndefined; bool allow_new = false; Maybe<Overlayable> overlayable; Maybe<OverlayableItem> overlayable_item; std::string comment; std::unique_ptr<Value> value; Loading Loading @@ -133,8 +133,8 @@ static bool AddResourcesToTable(ResourceTable* table, IDiagnostics* diag, Parsed } } if (res->overlayable) { if (!table->SetOverlayable(res->name, res->overlayable.value(), diag)) { if (res->overlayable_item) { if (!table->SetOverlayable(res->name, res->overlayable_item.value(), diag)) { return false; } } Loading Loading @@ -1063,88 +1063,115 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource << "' for <overlayable> tag"); } Maybe<StringPiece> overlayable_name = xml::FindNonEmptyAttribute(parser, "name"); if (!overlayable_name) { diag_->Error(DiagMessage(out_resource->source) << "<overlayable> tag must have a 'name' attribute"); return false; } const std::string kActorUriScheme = android::base::StringPrintf("%s://", Overlayable::kActorScheme); Maybe<StringPiece> overlayable_actor = xml::FindNonEmptyAttribute(parser, "actor"); if (overlayable_actor && !util::StartsWith(overlayable_actor.value(), kActorUriScheme)) { diag_->Error(DiagMessage(out_resource->source) << "specified <overlayable> tag 'actor' attribute must use the scheme '" << Overlayable::kActorScheme << "'"); return false; } // Create a overlayable entry grouping that represents this <overlayable> auto overlayable = std::make_shared<Overlayable>( overlayable_name.value(), (overlayable_actor) ? overlayable_actor.value() : "", out_resource->source); bool error = false; std::string comment; Overlayable::PolicyFlags current_policies = Overlayable::Policy::kNone; OverlayableItem::PolicyFlags current_policies = OverlayableItem::Policy::kNone; 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 overlayable element // Break the loop when exiting the <overlayable> break; } else if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth + 1) { // Clear the current policies when exiting the policy element current_policies = Overlayable::Policy::kNone; // Clear the current policies when exiting the <policy> tags current_policies = OverlayableItem::Policy::kNone; continue; } else if (event == xml::XmlPullParser::Event::kComment) { // Get the comment of individual item elements // Retrieve the comment of individual <item> tags comment = parser->comment(); continue; } else if (event != xml::XmlPullParser::Event::kStartElement) { // Skip to the next element // Skip to the start of the next element continue; } const Source item_source = source_.WithLine(parser->line_number()); const Source element_source = source_.WithLine(parser->line_number()); const std::string& element_name = parser->element_name(); const std::string& element_namespace = parser->element_namespace(); if (element_namespace.empty() && element_name == "item") { // Items specify the name and type of resource that should be overlayable 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"); Maybe<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name"); if (!item_name) { diag_->Error(DiagMessage(element_source) << "<item> within an <overlayable> must have a 'name' attribute"); 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"); Maybe<StringPiece> item_type = xml::FindNonEmptyAttribute(parser, "type"); if (!item_type) { diag_->Error(DiagMessage(element_source) << "<item> within an <overlayable> must have a 'type' attribute"); error = true; continue; } const ResourceType* type = ParseResourceType(maybe_type.value()); const ResourceType* type = ParseResourceType(item_type.value()); if (type == nullptr) { diag_->Error(DiagMessage(item_source) << "invalid resource type '" << maybe_type.value() diag_->Error(DiagMessage(element_source) << "invalid resource type '" << item_type.value() << "' in <item> within an <overlayable>"); error = true; continue; } ParsedResource child_resource; OverlayableItem overlayable_item(overlayable); overlayable_item.policies = current_policies; overlayable_item.comment = comment; overlayable_item.source = element_source; ParsedResource child_resource{}; child_resource.name.type = *type; child_resource.name.entry = maybe_name.value().to_string(); child_resource.overlayable = Overlayable{current_policies, item_source, comment}; child_resource.name.entry = item_name.value().to_string(); child_resource.overlayable_item = overlayable_item; out_resource->child_resources.push_back(std::move(child_resource)); } else if (element_namespace.empty() && element_name == "policy") { if (current_policies != Overlayable::Policy::kNone) { if (current_policies != OverlayableItem::Policy::kNone) { // 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"); diag_->Error(DiagMessage(element_source) << "<policy> blocks cannot be recursively nested"); error = true; 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 // policies. Items within the policy tag will have the specified policy. for (StringPiece part : util::Tokenize(maybe_type.value(), '|')) { StringPiece trimmed_part = util::TrimWhitespace(part); if (trimmed_part == "public") { current_policies |= Overlayable::Policy::kPublic; current_policies |= OverlayableItem::Policy::kPublic; } else if (trimmed_part == "product") { current_policies |= Overlayable::Policy::kProduct; current_policies |= OverlayableItem::Policy::kProduct; } else if (trimmed_part == "product_services") { current_policies |= Overlayable::Policy::kProductServices; current_policies |= OverlayableItem::Policy::kProductServices; } else if (trimmed_part == "system") { current_policies |= Overlayable::Policy::kSystem; current_policies |= OverlayableItem::Policy::kSystem; } else if (trimmed_part == "vendor") { current_policies |= Overlayable::Policy::kVendor; current_policies |= OverlayableItem::Policy::kVendor; } else { diag_->Error(DiagMessage(item_source) diag_->Error(DiagMessage(element_source) << "<policy> has unsupported type '" << trimmed_part << "'"); error = true; continue; Loading @@ -1152,11 +1179,13 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource } } } else if (!ShouldIgnoreElement(element_namespace, element_name)) { diag_->Error(DiagMessage(item_source) << "invalid element <" << element_name << "> " diag_->Error(DiagMessage(element_source) << "invalid element <" << element_name << "> " << " in <overlayable>"); error = true; break; } comment.clear(); } return !error; Loading tools/aapt2/ResourceParser_test.cpp +89 −55 Original line number Diff line number Diff line Loading @@ -892,11 +892,8 @@ TEST_F(ResourceParserTest, ParsePlatformIndependentNewline) { } TEST_F(ResourceParserTest, ParseOverlayable) { std::string input = R"(<overlayable />)"; EXPECT_TRUE(TestParse(input)); input = R"( <overlayable> std::string input = R"( <overlayable name="Name" actor="overlay://theme"> <item type="string" name="foo" /> <item type="drawable" name="bar" /> </overlayable>)"; Loading @@ -905,24 +902,35 @@ TEST_F(ResourceParserTest, ParseOverlayable) { auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); EXPECT_THAT(search_result.value().entry->overlayable.value().policies, Eq(Overlayable::Policy::kNone)); ASSERT_TRUE(search_result.value().entry->overlayable_item); OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone)); search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); EXPECT_THAT(search_result.value().entry->overlayable.value().policies, Eq(Overlayable::Policy::kNone)); ASSERT_TRUE(search_result.value().entry->overlayable_item); result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone)); } TEST_F(ResourceParserTest, ParseOverlayablePolicy) { std::string input = R"(<overlayable />)"; EXPECT_TRUE(TestParse(input)); TEST_F(ResourceParserTest, ParseOverlayableRequiresName) { EXPECT_FALSE(TestParse(R"(<overlayable actor="overlay://theme" />)")); EXPECT_TRUE(TestParse(R"(<overlayable name="Name" />)")); EXPECT_TRUE(TestParse(R"(<overlayable name="Name" actor="overlay://theme" />)")); } input = R"( <overlayable> TEST_F(ResourceParserTest, ParseOverlayableBadActorFail) { EXPECT_FALSE(TestParse(R"(<overlayable name="Name" actor="overley://theme" />)")); } TEST_F(ResourceParserTest, ParseOverlayablePolicy) { std::string input = R"( <overlayable name="Name"> <item type="string" name="foo" /> <policy type="product"> <item type="string" name="bar" /> Loading @@ -945,49 +953,55 @@ TEST_F(ResourceParserTest, ParseOverlayablePolicy) { auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); Overlayable& overlayable = search_result.value().entry->overlayable.value(); EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kNone)); ASSERT_TRUE(search_result.value().entry->overlayable_item); OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone)); search_result = table_.FindResource(test::ParseNameOrDie("string/bar")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); overlayable = search_result.value().entry->overlayable.value(); EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct)); ASSERT_TRUE(search_result.value().entry->overlayable_item); result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct)); search_result = table_.FindResource(test::ParseNameOrDie("string/baz")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); overlayable = search_result.value().entry->overlayable.value(); EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProductServices)); ASSERT_TRUE(search_result.value().entry->overlayable_item); result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProductServices)); search_result = table_.FindResource(test::ParseNameOrDie("string/fiz")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); overlayable = search_result.value().entry->overlayable.value(); EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kSystem)); ASSERT_TRUE(search_result.value().entry->overlayable_item); result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem)); search_result = table_.FindResource(test::ParseNameOrDie("string/fuz")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); overlayable = search_result.value().entry->overlayable.value(); EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor)); ASSERT_TRUE(search_result.value().entry->overlayable_item); result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor)); search_result = table_.FindResource(test::ParseNameOrDie("string/faz")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); overlayable = search_result.value().entry->overlayable.value(); EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kPublic)); ASSERT_TRUE(search_result.value().entry->overlayable_item); result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic)); } TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) { std::string input = R"( <overlayable> <overlayable name="Name"> <policy type="illegal_policy"> <item type="string" name="foo" /> </policy> Loading @@ -995,7 +1009,7 @@ TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) { EXPECT_FALSE(TestParse(input)); input = R"( <overlayable> <overlayable name="Name"> <policy type="product"> <item name="foo" /> </policy> Loading @@ -1003,7 +1017,7 @@ TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) { EXPECT_FALSE(TestParse(input)); input = R"( <overlayable> <overlayable name="Name"> <policy type="vendor"> <item type="string" /> </policy> Loading @@ -1013,7 +1027,7 @@ TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) { TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) { std::string input = R"( <overlayable> <overlayable name="Name"> <policy type="vendor|product_services"> <item type="string" name="foo" /> </policy> Loading @@ -1026,39 +1040,59 @@ TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) { auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); Overlayable& overlayable = search_result.value().entry->overlayable.value(); EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor | Overlayable::Policy::kProductServices)); ASSERT_TRUE(search_result.value().entry->overlayable_item); OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor | OverlayableItem::Policy::kProductServices)); search_result = table_.FindResource(test::ParseNameOrDie("string/bar")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); overlayable = search_result.value().entry->overlayable.value(); EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct | Overlayable::Policy::kSystem)); ASSERT_TRUE(search_result.value().entry->overlayable_item); result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct | OverlayableItem::Policy::kSystem)); } TEST_F(ResourceParserTest, DuplicateOverlayableIsError) { std::string input = R"( <overlayable> <overlayable name="Name"> <item type="string" name="foo" /> <item type="string" name="foo" /> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( <overlayable name="Name"> <item type="string" name="foo" /> </overlayable> <overlayable name="Name"> <item type="string" name="foo" /> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( <overlayable name="Name"> <item type="string" name="foo" /> </overlayable> <overlayable name="Other"> <item type="string" name="foo" /> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( <overlayable> <overlayable name="Name" actor="overlay://my.actor.one"> <item type="string" name="foo" /> </overlayable> <overlayable> <overlayable name="Other" actor="overlay://my.actor.two"> <item type="string" name="foo" /> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( <overlayable> <overlayable name="Name"> <policy type="product"> <item type="string" name="foo" /> <item type="string" name="foo" /> Loading @@ -1067,7 +1101,7 @@ TEST_F(ResourceParserTest, DuplicateOverlayableIsError) { EXPECT_FALSE(TestParse(input)); input = R"( <overlayable> <overlayable name="Name"> <policy type="product"> <item type="string" name="foo" /> </policy> Loading @@ -1076,7 +1110,7 @@ TEST_F(ResourceParserTest, DuplicateOverlayableIsError) { EXPECT_FALSE(TestParse(input)); input = R"( <overlayable> <overlayable name="Name"> <policy type="product"> <item type="string" name="foo" /> </policy> Loading @@ -1087,13 +1121,13 @@ TEST_F(ResourceParserTest, DuplicateOverlayableIsError) { EXPECT_FALSE(TestParse(input)); input = R"( <overlayable> <overlayable name="Name"> <policy type="product"> <item type="string" name="foo" /> </policy> </overlayable> <overlayable> <overlayable name="Name"> <policy type="product"> <item type="string" name="foo" /> </policy> Loading @@ -1103,7 +1137,7 @@ TEST_F(ResourceParserTest, DuplicateOverlayableIsError) { TEST_F(ResourceParserTest, NestPolicyInOverlayableError) { std::string input = R"( <overlayable> <overlayable name="Name"> <policy type="vendor|product"> <policy type="product_services"> <item type="string" name="foo" /> Loading tools/aapt2/ResourceTable.cpp +12 −8 Original line number Diff line number Diff line Loading @@ -40,6 +40,8 @@ using ::android::base::StringPrintf; namespace aapt { const char* Overlayable::kActorScheme = "overlay"; static bool less_than_type_and_id(const std::unique_ptr<ResourceTableType>& lhs, const std::pair<ResourceType, Maybe<uint8_t>>& rhs) { return lhs->type < rhs.first || (lhs->type == rhs.first && rhs.second && lhs->id < rhs.second); Loading Loading @@ -625,17 +627,18 @@ bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& return true; } bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable, bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable, IDiagnostics* diag) { return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag); } bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable, IDiagnostics* diag) { const OverlayableItem& overlayable, IDiagnostics* diag) { return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag); } bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable, bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const OverlayableItem& overlayable, NameValidator name_validator, IDiagnostics *diag) { CHECK(diag != nullptr); Loading @@ -647,14 +650,15 @@ bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overla ResourceTableType* type = package->FindOrCreateType(name.type); ResourceEntry* entry = type->FindOrCreateEntry(name.entry); if (entry->overlayable) { if (entry->overlayable_item) { diag->Error(DiagMessage(overlayable.source) << "duplicate overlayable declaration for resource '" << name << "'"); diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here"); diag->Error(DiagMessage(entry->overlayable_item.value().source) << "previous declaration here"); return false; } entry->overlayable = overlayable; entry->overlayable_item = overlayable; return true; } Loading Loading @@ -690,7 +694,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_item = entry->overlayable_item; for (const auto& config_value : entry->values) { ResourceConfigValue* new_value = Loading tools/aapt2/ResourceTable.h +24 −8 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
libs/androidfw/LoadedArsc.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -623,7 +623,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, } // Add the pairing of overlayable properties to resource ids to the package OverlayableInfo overlayable_info; OverlayableInfo overlayable_info{}; overlayable_info.policy_flags = policy_header->policy_flags; loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids)); break; Loading
tools/aapt2/ResourceParser.cpp +65 −36 Original line number Diff line number Diff line Loading @@ -99,7 +99,7 @@ struct ParsedResource { ResourceId id; Visibility::Level visibility_level = Visibility::Level::kUndefined; bool allow_new = false; Maybe<Overlayable> overlayable; Maybe<OverlayableItem> overlayable_item; std::string comment; std::unique_ptr<Value> value; Loading Loading @@ -133,8 +133,8 @@ static bool AddResourcesToTable(ResourceTable* table, IDiagnostics* diag, Parsed } } if (res->overlayable) { if (!table->SetOverlayable(res->name, res->overlayable.value(), diag)) { if (res->overlayable_item) { if (!table->SetOverlayable(res->name, res->overlayable_item.value(), diag)) { return false; } } Loading Loading @@ -1063,88 +1063,115 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource << "' for <overlayable> tag"); } Maybe<StringPiece> overlayable_name = xml::FindNonEmptyAttribute(parser, "name"); if (!overlayable_name) { diag_->Error(DiagMessage(out_resource->source) << "<overlayable> tag must have a 'name' attribute"); return false; } const std::string kActorUriScheme = android::base::StringPrintf("%s://", Overlayable::kActorScheme); Maybe<StringPiece> overlayable_actor = xml::FindNonEmptyAttribute(parser, "actor"); if (overlayable_actor && !util::StartsWith(overlayable_actor.value(), kActorUriScheme)) { diag_->Error(DiagMessage(out_resource->source) << "specified <overlayable> tag 'actor' attribute must use the scheme '" << Overlayable::kActorScheme << "'"); return false; } // Create a overlayable entry grouping that represents this <overlayable> auto overlayable = std::make_shared<Overlayable>( overlayable_name.value(), (overlayable_actor) ? overlayable_actor.value() : "", out_resource->source); bool error = false; std::string comment; Overlayable::PolicyFlags current_policies = Overlayable::Policy::kNone; OverlayableItem::PolicyFlags current_policies = OverlayableItem::Policy::kNone; 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 overlayable element // Break the loop when exiting the <overlayable> break; } else if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth + 1) { // Clear the current policies when exiting the policy element current_policies = Overlayable::Policy::kNone; // Clear the current policies when exiting the <policy> tags current_policies = OverlayableItem::Policy::kNone; continue; } else if (event == xml::XmlPullParser::Event::kComment) { // Get the comment of individual item elements // Retrieve the comment of individual <item> tags comment = parser->comment(); continue; } else if (event != xml::XmlPullParser::Event::kStartElement) { // Skip to the next element // Skip to the start of the next element continue; } const Source item_source = source_.WithLine(parser->line_number()); const Source element_source = source_.WithLine(parser->line_number()); const std::string& element_name = parser->element_name(); const std::string& element_namespace = parser->element_namespace(); if (element_namespace.empty() && element_name == "item") { // Items specify the name and type of resource that should be overlayable 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"); Maybe<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name"); if (!item_name) { diag_->Error(DiagMessage(element_source) << "<item> within an <overlayable> must have a 'name' attribute"); 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"); Maybe<StringPiece> item_type = xml::FindNonEmptyAttribute(parser, "type"); if (!item_type) { diag_->Error(DiagMessage(element_source) << "<item> within an <overlayable> must have a 'type' attribute"); error = true; continue; } const ResourceType* type = ParseResourceType(maybe_type.value()); const ResourceType* type = ParseResourceType(item_type.value()); if (type == nullptr) { diag_->Error(DiagMessage(item_source) << "invalid resource type '" << maybe_type.value() diag_->Error(DiagMessage(element_source) << "invalid resource type '" << item_type.value() << "' in <item> within an <overlayable>"); error = true; continue; } ParsedResource child_resource; OverlayableItem overlayable_item(overlayable); overlayable_item.policies = current_policies; overlayable_item.comment = comment; overlayable_item.source = element_source; ParsedResource child_resource{}; child_resource.name.type = *type; child_resource.name.entry = maybe_name.value().to_string(); child_resource.overlayable = Overlayable{current_policies, item_source, comment}; child_resource.name.entry = item_name.value().to_string(); child_resource.overlayable_item = overlayable_item; out_resource->child_resources.push_back(std::move(child_resource)); } else if (element_namespace.empty() && element_name == "policy") { if (current_policies != Overlayable::Policy::kNone) { if (current_policies != OverlayableItem::Policy::kNone) { // 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"); diag_->Error(DiagMessage(element_source) << "<policy> blocks cannot be recursively nested"); error = true; 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 // policies. Items within the policy tag will have the specified policy. for (StringPiece part : util::Tokenize(maybe_type.value(), '|')) { StringPiece trimmed_part = util::TrimWhitespace(part); if (trimmed_part == "public") { current_policies |= Overlayable::Policy::kPublic; current_policies |= OverlayableItem::Policy::kPublic; } else if (trimmed_part == "product") { current_policies |= Overlayable::Policy::kProduct; current_policies |= OverlayableItem::Policy::kProduct; } else if (trimmed_part == "product_services") { current_policies |= Overlayable::Policy::kProductServices; current_policies |= OverlayableItem::Policy::kProductServices; } else if (trimmed_part == "system") { current_policies |= Overlayable::Policy::kSystem; current_policies |= OverlayableItem::Policy::kSystem; } else if (trimmed_part == "vendor") { current_policies |= Overlayable::Policy::kVendor; current_policies |= OverlayableItem::Policy::kVendor; } else { diag_->Error(DiagMessage(item_source) diag_->Error(DiagMessage(element_source) << "<policy> has unsupported type '" << trimmed_part << "'"); error = true; continue; Loading @@ -1152,11 +1179,13 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource } } } else if (!ShouldIgnoreElement(element_namespace, element_name)) { diag_->Error(DiagMessage(item_source) << "invalid element <" << element_name << "> " diag_->Error(DiagMessage(element_source) << "invalid element <" << element_name << "> " << " in <overlayable>"); error = true; break; } comment.clear(); } return !error; Loading
tools/aapt2/ResourceParser_test.cpp +89 −55 Original line number Diff line number Diff line Loading @@ -892,11 +892,8 @@ TEST_F(ResourceParserTest, ParsePlatformIndependentNewline) { } TEST_F(ResourceParserTest, ParseOverlayable) { std::string input = R"(<overlayable />)"; EXPECT_TRUE(TestParse(input)); input = R"( <overlayable> std::string input = R"( <overlayable name="Name" actor="overlay://theme"> <item type="string" name="foo" /> <item type="drawable" name="bar" /> </overlayable>)"; Loading @@ -905,24 +902,35 @@ TEST_F(ResourceParserTest, ParseOverlayable) { auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); EXPECT_THAT(search_result.value().entry->overlayable.value().policies, Eq(Overlayable::Policy::kNone)); ASSERT_TRUE(search_result.value().entry->overlayable_item); OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone)); search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); EXPECT_THAT(search_result.value().entry->overlayable.value().policies, Eq(Overlayable::Policy::kNone)); ASSERT_TRUE(search_result.value().entry->overlayable_item); result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone)); } TEST_F(ResourceParserTest, ParseOverlayablePolicy) { std::string input = R"(<overlayable />)"; EXPECT_TRUE(TestParse(input)); TEST_F(ResourceParserTest, ParseOverlayableRequiresName) { EXPECT_FALSE(TestParse(R"(<overlayable actor="overlay://theme" />)")); EXPECT_TRUE(TestParse(R"(<overlayable name="Name" />)")); EXPECT_TRUE(TestParse(R"(<overlayable name="Name" actor="overlay://theme" />)")); } input = R"( <overlayable> TEST_F(ResourceParserTest, ParseOverlayableBadActorFail) { EXPECT_FALSE(TestParse(R"(<overlayable name="Name" actor="overley://theme" />)")); } TEST_F(ResourceParserTest, ParseOverlayablePolicy) { std::string input = R"( <overlayable name="Name"> <item type="string" name="foo" /> <policy type="product"> <item type="string" name="bar" /> Loading @@ -945,49 +953,55 @@ TEST_F(ResourceParserTest, ParseOverlayablePolicy) { auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); Overlayable& overlayable = search_result.value().entry->overlayable.value(); EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kNone)); ASSERT_TRUE(search_result.value().entry->overlayable_item); OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone)); search_result = table_.FindResource(test::ParseNameOrDie("string/bar")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); overlayable = search_result.value().entry->overlayable.value(); EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct)); ASSERT_TRUE(search_result.value().entry->overlayable_item); result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct)); search_result = table_.FindResource(test::ParseNameOrDie("string/baz")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); overlayable = search_result.value().entry->overlayable.value(); EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProductServices)); ASSERT_TRUE(search_result.value().entry->overlayable_item); result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProductServices)); search_result = table_.FindResource(test::ParseNameOrDie("string/fiz")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); overlayable = search_result.value().entry->overlayable.value(); EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kSystem)); ASSERT_TRUE(search_result.value().entry->overlayable_item); result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem)); search_result = table_.FindResource(test::ParseNameOrDie("string/fuz")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); overlayable = search_result.value().entry->overlayable.value(); EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor)); ASSERT_TRUE(search_result.value().entry->overlayable_item); result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor)); search_result = table_.FindResource(test::ParseNameOrDie("string/faz")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); overlayable = search_result.value().entry->overlayable.value(); EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kPublic)); ASSERT_TRUE(search_result.value().entry->overlayable_item); result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic)); } TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) { std::string input = R"( <overlayable> <overlayable name="Name"> <policy type="illegal_policy"> <item type="string" name="foo" /> </policy> Loading @@ -995,7 +1009,7 @@ TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) { EXPECT_FALSE(TestParse(input)); input = R"( <overlayable> <overlayable name="Name"> <policy type="product"> <item name="foo" /> </policy> Loading @@ -1003,7 +1017,7 @@ TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) { EXPECT_FALSE(TestParse(input)); input = R"( <overlayable> <overlayable name="Name"> <policy type="vendor"> <item type="string" /> </policy> Loading @@ -1013,7 +1027,7 @@ TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) { TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) { std::string input = R"( <overlayable> <overlayable name="Name"> <policy type="vendor|product_services"> <item type="string" name="foo" /> </policy> Loading @@ -1026,39 +1040,59 @@ TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) { auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); Overlayable& overlayable = search_result.value().entry->overlayable.value(); EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kVendor | Overlayable::Policy::kProductServices)); ASSERT_TRUE(search_result.value().entry->overlayable_item); OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor | OverlayableItem::Policy::kProductServices)); search_result = table_.FindResource(test::ParseNameOrDie("string/bar")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); ASSERT_TRUE(search_result.value().entry->overlayable); overlayable = search_result.value().entry->overlayable.value(); EXPECT_THAT(overlayable.policies, Eq(Overlayable::Policy::kProduct | Overlayable::Policy::kSystem)); ASSERT_TRUE(search_result.value().entry->overlayable_item); result_overlayable_item = search_result.value().entry->overlayable_item.value(); EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name")); EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct | OverlayableItem::Policy::kSystem)); } TEST_F(ResourceParserTest, DuplicateOverlayableIsError) { std::string input = R"( <overlayable> <overlayable name="Name"> <item type="string" name="foo" /> <item type="string" name="foo" /> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( <overlayable name="Name"> <item type="string" name="foo" /> </overlayable> <overlayable name="Name"> <item type="string" name="foo" /> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( <overlayable name="Name"> <item type="string" name="foo" /> </overlayable> <overlayable name="Other"> <item type="string" name="foo" /> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( <overlayable> <overlayable name="Name" actor="overlay://my.actor.one"> <item type="string" name="foo" /> </overlayable> <overlayable> <overlayable name="Other" actor="overlay://my.actor.two"> <item type="string" name="foo" /> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( <overlayable> <overlayable name="Name"> <policy type="product"> <item type="string" name="foo" /> <item type="string" name="foo" /> Loading @@ -1067,7 +1101,7 @@ TEST_F(ResourceParserTest, DuplicateOverlayableIsError) { EXPECT_FALSE(TestParse(input)); input = R"( <overlayable> <overlayable name="Name"> <policy type="product"> <item type="string" name="foo" /> </policy> Loading @@ -1076,7 +1110,7 @@ TEST_F(ResourceParserTest, DuplicateOverlayableIsError) { EXPECT_FALSE(TestParse(input)); input = R"( <overlayable> <overlayable name="Name"> <policy type="product"> <item type="string" name="foo" /> </policy> Loading @@ -1087,13 +1121,13 @@ TEST_F(ResourceParserTest, DuplicateOverlayableIsError) { EXPECT_FALSE(TestParse(input)); input = R"( <overlayable> <overlayable name="Name"> <policy type="product"> <item type="string" name="foo" /> </policy> </overlayable> <overlayable> <overlayable name="Name"> <policy type="product"> <item type="string" name="foo" /> </policy> Loading @@ -1103,7 +1137,7 @@ TEST_F(ResourceParserTest, DuplicateOverlayableIsError) { TEST_F(ResourceParserTest, NestPolicyInOverlayableError) { std::string input = R"( <overlayable> <overlayable name="Name"> <policy type="vendor|product"> <policy type="product_services"> <item type="string" name="foo" /> Loading
tools/aapt2/ResourceTable.cpp +12 −8 Original line number Diff line number Diff line Loading @@ -40,6 +40,8 @@ using ::android::base::StringPrintf; namespace aapt { const char* Overlayable::kActorScheme = "overlay"; static bool less_than_type_and_id(const std::unique_ptr<ResourceTableType>& lhs, const std::pair<ResourceType, Maybe<uint8_t>>& rhs) { return lhs->type < rhs.first || (lhs->type == rhs.first && rhs.second && lhs->id < rhs.second); Loading Loading @@ -625,17 +627,18 @@ bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& return true; } bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable, bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable, IDiagnostics* diag) { return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag); } bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable, IDiagnostics* diag) { const OverlayableItem& overlayable, IDiagnostics* diag) { return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag); } bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable, bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const OverlayableItem& overlayable, NameValidator name_validator, IDiagnostics *diag) { CHECK(diag != nullptr); Loading @@ -647,14 +650,15 @@ bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overla ResourceTableType* type = package->FindOrCreateType(name.type); ResourceEntry* entry = type->FindOrCreateEntry(name.entry); if (entry->overlayable) { if (entry->overlayable_item) { diag->Error(DiagMessage(overlayable.source) << "duplicate overlayable declaration for resource '" << name << "'"); diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here"); diag->Error(DiagMessage(entry->overlayable_item.value().source) << "previous declaration here"); return false; } entry->overlayable = overlayable; entry->overlayable_item = overlayable; return true; } Loading Loading @@ -690,7 +694,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_item = entry->overlayable_item; for (const auto& config_value : entry->values) { ResourceConfigValue* new_value = Loading
tools/aapt2/ResourceTable.h +24 −8 File changed.Preview size limit exceeded, changes collapsed. Show changes