diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp index 8862405189c09..cc1cf7f232462 100644 --- a/tools/aapt2/dump/DumpManifest.cpp +++ b/tools/aapt2/dump/DumpManifest.cpp @@ -1851,6 +1851,41 @@ class SupportsGlTexture : public ManifestExtractor::Element { } }; +/** Represents elements. **/ +class Property : public ManifestExtractor::Element { + public: + Property() = default; + std::string name; + std::string value; + const int* value_int; + std::string resource; + const int* resource_int; + + void Extract(xml::Element* element) override { + name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), ""); + value = GetAttributeStringDefault(FindAttribute(element, VALUE_ATTR), ""); + value_int = GetAttributeInteger(FindAttribute(element, VALUE_ATTR)); + resource = GetAttributeStringDefault(FindAttribute(element, RESOURCE_ATTR), ""); + resource_int = GetAttributeInteger(FindAttribute(element, RESOURCE_ATTR)); + } + + void Print(text::Printer* printer) override { + printer->Print(StringPrintf("property: name='%s' ", name.data())); + if (!value.empty()) { + printer->Print(StringPrintf("value='%s' ", value.data())); + } else if (value_int) { + printer->Print(StringPrintf("value='%d' ", *value_int)); + } else { + if (!resource.empty()) { + printer->Print(StringPrintf("resource='%s' ", resource.data())); + } else if (resource_int) { + printer->Print(StringPrintf("resource='%d' ", *resource_int)); + } + } + printer->Print("\n"); + } +}; + /** Recursively prints the extracted badging element. */ static void Print(ManifestExtractor::Element* el, text::Printer* printer) { el->Print(printer); @@ -2291,6 +2326,7 @@ T* ElementCast(ManifestExtractor::Element* element) { {"overlay", std::is_base_of::value}, {"package-verifier", std::is_base_of::value}, {"permission", std::is_base_of::value}, + {"property", std::is_base_of::value}, {"provider", std::is_base_of::value}, {"receiver", std::is_base_of::value}, {"required-feature", std::is_base_of::value}, @@ -2344,6 +2380,7 @@ std::unique_ptr ManifestExtractor::Element::Inflate( {"overlay", &CreateType}, {"package-verifier", &CreateType}, {"permission", &CreateType}, + {"property", &CreateType}, {"provider", &CreateType}, {"receiver", &CreateType}, {"required-feature", &CreateType}, diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index 3d8c25ebcbddc..fd3a4c035076e 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -112,6 +112,27 @@ static xml::XmlNodeAction::ActionFuncWithDiag RequiredAndroidAttribute(const std }; } +static xml::XmlNodeAction::ActionFuncWithDiag RequiredOneAndroidAttribute( + const std::string& attrName1, const std::string& attrName2) { + return [=](xml::Element* el, SourcePathDiagnostics* diag) -> bool { + xml::Attribute* attr1 = el->FindAttribute(xml::kSchemaAndroid, attrName1); + xml::Attribute* attr2 = el->FindAttribute(xml::kSchemaAndroid, attrName2); + if (attr1 == nullptr && attr2 == nullptr) { + diag->Error(DiagMessage(el->line_number) + << "<" << el->name << "> is missing required attribute 'android:" << attrName1 + << "' or 'android:" << attrName2 << "'"); + return false; + } + if (attr1 != nullptr && attr2 != nullptr) { + diag->Error(DiagMessage(el->line_number) + << "<" << el->name << "> can only specify one of attribute 'android:" << attrName1 + << "' or 'android:" << attrName2 << "'"); + return false; + } + return true; + }; +} + static bool AutoGenerateIsFeatureSplit(xml::Element* el, SourcePathDiagnostics* diag) { constexpr const char* kFeatureSplit = "featureSplit"; constexpr const char* kIsFeatureSplit = "isFeatureSplit"; @@ -282,6 +303,10 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, // Common actions. xml::XmlNodeAction meta_data_action; + // Common actions. + xml::XmlNodeAction property_action; + property_action.Action(RequiredOneAndroidAttribute("resource", "value")); + // Common actions. xml::XmlNodeAction uses_feature_action; uses_feature_action.Action(VerifyUsesFeature); @@ -292,6 +317,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, component_action["intent-filter"] = intent_filter_action; component_action["preferred"] = intent_filter_action; component_action["meta-data"] = meta_data_action; + component_action["property"] = property_action; // Manifest actions. xml::XmlNodeAction& manifest_action = (*executor)["manifest"]; @@ -427,6 +453,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, application_action["uses-native-library"].Action(RequiredNameIsNotEmpty); application_action["library"].Action(RequiredNameIsNotEmpty); application_action["profileable"]; + application_action["property"] = property_action; xml::XmlNodeAction& static_library_action = application_action["static-library"]; static_library_action.Action(RequiredNameIsJavaPackage); diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp index 0791805e25060..781ff7b51637c 100644 --- a/tools/aapt2/link/ManifestFixer_test.cpp +++ b/tools/aapt2/link/ManifestFixer_test.cpp @@ -886,4 +886,78 @@ TEST_F(ManifestFixerTest, UsesLibraryMustHaveNonEmptyName) { EXPECT_THAT(Verify(input), NotNull()); } +TEST_F(ManifestFixerTest, ApplicationPropertyAttributeRequired) { + std::string input = R"( + + + + + )"; + EXPECT_THAT(Verify(input), IsNull()); +} + +TEST_F(ManifestFixerTest, ApplicationPropertyOnlyOneAttributeDefined) { + std::string input = R"( + + + + + )"; + EXPECT_THAT(Verify(input), IsNull()); + + input = R"( + + + + + )"; + EXPECT_THAT(Verify(input), NotNull()); + + input = R"( + + + + + )"; + EXPECT_THAT(Verify(input), NotNull()); +} + +TEST_F(ManifestFixerTest, ComponentPropertyOnlyOneAttributeDefined) { + std::string input = R"( + + + + + + + )"; + EXPECT_THAT(Verify(input), IsNull()); + + input = R"( + + + + + + + )"; + EXPECT_THAT(Verify(input), NotNull()); + + input = R"( + + + + + + + )"; + EXPECT_THAT(Verify(input), NotNull()); +} } // namespace aapt