diff options
author | 2017-01-31 13:47:27 -0800 | |
---|---|---|
committer | 2017-02-01 15:28:23 -0800 | |
commit | 86d67df8d57b9537666f9b54a9ca563779a2288b (patch) | |
tree | d7498429238104f724b28ff68a84e7deeebef8af | |
parent | a9285db08883dbbe7b5eb9276cb52b7e01b42aa3 (diff) |
AAPT2: Support CtsContentTestCases build
- Add <feature-group> to ManifestFixer.
- Support <meta-data> in <instrumentation>
- Add support for <bag> and type="configVarying". Some CTS tests use this
old notation, we need to support it (even though configVarying isn't
anything supported by the framework convention).
Change-Id: I6946fa633ce513ea8437c1496db883cf27dcf6de
Test: make aapt2_tests
-rw-r--r-- | tools/aapt2/Main.cpp | 2 | ||||
-rw-r--r-- | tools/aapt2/Resource.cpp | 3 | ||||
-rw-r--r-- | tools/aapt2/Resource.h | 5 | ||||
-rw-r--r-- | tools/aapt2/ResourceParser.cpp | 199 | ||||
-rw-r--r-- | tools/aapt2/ResourceParser.h | 3 | ||||
-rw-r--r-- | tools/aapt2/ResourceParser_test.cpp | 19 | ||||
-rw-r--r-- | tools/aapt2/Resource_test.cpp | 4 | ||||
-rw-r--r-- | tools/aapt2/link/ManifestFixer.cpp | 83 | ||||
-rw-r--r-- | tools/aapt2/link/ManifestFixer_test.cpp | 60 | ||||
-rw-r--r-- | tools/aapt2/readme.md | 7 |
10 files changed, 253 insertions, 132 deletions
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index 15d7e2e2241b..d44d79a80749 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -25,7 +25,7 @@ namespace aapt { static const char* sMajorVersion = "2"; // Update minor version whenever a feature or flag is added. -static const char* sMinorVersion = "5"; +static const char* sMinorVersion = "6"; int PrintVersion() { std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "." diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp index fdabce17433c..35971e7bd99b 100644 --- a/tools/aapt2/Resource.cpp +++ b/tools/aapt2/Resource.cpp @@ -39,6 +39,8 @@ StringPiece ToString(ResourceType type) { return "bool"; case ResourceType::kColor: return "color"; + case ResourceType::kConfigVarying: + return "configVarying"; case ResourceType::kDimen: return "dimen"; case ResourceType::kDrawable: @@ -85,6 +87,7 @@ static const std::map<StringPiece, ResourceType> sResourceTypeMap{ {"^attr-private", ResourceType::kAttrPrivate}, {"bool", ResourceType::kBool}, {"color", ResourceType::kColor}, + {"configVarying", ResourceType::kConfigVarying}, {"dimen", ResourceType::kDimen}, {"drawable", ResourceType::kDrawable}, {"font", ResourceType::kFont}, diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h index 1950ea35a652..4d915d9f23c0 100644 --- a/tools/aapt2/Resource.h +++ b/tools/aapt2/Resource.h @@ -44,6 +44,11 @@ enum class ResourceType { kAttrPrivate, kBool, kColor, + + // Not really a type, but it shows up in some CTS tests and + // we need to continue respecting it. + kConfigVarying, + kDimen, kDrawable, kFont, diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 79379fe4b5ee..1c750c6748b9 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -338,50 +338,52 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, using BagParseFunc = std::function<bool(ResourceParser*, xml::XmlPullParser*, ParsedResource*)>; - static const auto elToItemMap = - ImmutableMap<std::string, ItemTypeFormat>::CreatePreSorted({ - {"bool", {ResourceType::kBool, android::ResTable_map::TYPE_BOOLEAN}}, - {"color", {ResourceType::kColor, android::ResTable_map::TYPE_COLOR}}, - {"dimen", - {ResourceType::kDimen, android::ResTable_map::TYPE_FLOAT | - android::ResTable_map::TYPE_FRACTION | - android::ResTable_map::TYPE_DIMENSION}}, - {"drawable", - {ResourceType::kDrawable, android::ResTable_map::TYPE_COLOR}}, - {"fraction", - {ResourceType::kFraction, - android::ResTable_map::TYPE_FLOAT | - android::ResTable_map::TYPE_FRACTION | - android::ResTable_map::TYPE_DIMENSION}}, - {"integer", - {ResourceType::kInteger, android::ResTable_map::TYPE_INTEGER}}, - {"string", - {ResourceType::kString, android::ResTable_map::TYPE_STRING}}, - }); - - static const auto elToBagMap = - ImmutableMap<std::string, BagParseFunc>::CreatePreSorted({ - {"add-resource", std::mem_fn(&ResourceParser::ParseAddResource)}, - {"array", std::mem_fn(&ResourceParser::ParseArray)}, - {"attr", std::mem_fn(&ResourceParser::ParseAttr)}, - {"declare-styleable", - std::mem_fn(&ResourceParser::ParseDeclareStyleable)}, - {"integer-array", std::mem_fn(&ResourceParser::ParseIntegerArray)}, - {"java-symbol", std::mem_fn(&ResourceParser::ParseSymbol)}, - {"plurals", std::mem_fn(&ResourceParser::ParsePlural)}, - {"public", std::mem_fn(&ResourceParser::ParsePublic)}, - {"public-group", std::mem_fn(&ResourceParser::ParsePublicGroup)}, - {"string-array", std::mem_fn(&ResourceParser::ParseStringArray)}, - {"style", std::mem_fn(&ResourceParser::ParseStyle)}, - {"symbol", std::mem_fn(&ResourceParser::ParseSymbol)}, - }); + static const auto elToItemMap = ImmutableMap<std::string, ItemTypeFormat>::CreatePreSorted({ + {"bool", {ResourceType::kBool, android::ResTable_map::TYPE_BOOLEAN}}, + {"color", {ResourceType::kColor, android::ResTable_map::TYPE_COLOR}}, + {"configVarying", {ResourceType::kConfigVarying, android::ResTable_map::TYPE_ANY}}, + {"dimen", + {ResourceType::kDimen, + android::ResTable_map::TYPE_FLOAT | android::ResTable_map::TYPE_FRACTION | + android::ResTable_map::TYPE_DIMENSION}}, + {"drawable", {ResourceType::kDrawable, android::ResTable_map::TYPE_COLOR}}, + {"fraction", + {ResourceType::kFraction, + android::ResTable_map::TYPE_FLOAT | android::ResTable_map::TYPE_FRACTION | + android::ResTable_map::TYPE_DIMENSION}}, + {"integer", {ResourceType::kInteger, android::ResTable_map::TYPE_INTEGER}}, + {"string", {ResourceType::kString, android::ResTable_map::TYPE_STRING}}, + }); + + static const auto elToBagMap = ImmutableMap<std::string, BagParseFunc>::CreatePreSorted({ + {"add-resource", std::mem_fn(&ResourceParser::ParseAddResource)}, + {"array", std::mem_fn(&ResourceParser::ParseArray)}, + {"attr", std::mem_fn(&ResourceParser::ParseAttr)}, + {"configVarying", + std::bind(&ResourceParser::ParseStyle, std::placeholders::_1, ResourceType::kConfigVarying, + std::placeholders::_2, std::placeholders::_3)}, + {"declare-styleable", std::mem_fn(&ResourceParser::ParseDeclareStyleable)}, + {"integer-array", std::mem_fn(&ResourceParser::ParseIntegerArray)}, + {"java-symbol", std::mem_fn(&ResourceParser::ParseSymbol)}, + {"plurals", std::mem_fn(&ResourceParser::ParsePlural)}, + {"public", std::mem_fn(&ResourceParser::ParsePublic)}, + {"public-group", std::mem_fn(&ResourceParser::ParsePublicGroup)}, + {"string-array", std::mem_fn(&ResourceParser::ParseStringArray)}, + {"style", std::bind(&ResourceParser::ParseStyle, std::placeholders::_1, ResourceType::kStyle, + std::placeholders::_2, std::placeholders::_3)}, + {"symbol", std::mem_fn(&ResourceParser::ParseSymbol)}, + }); std::string resource_type = parser->element_name(); // The value format accepted for this resource. uint32_t resource_format = 0u; + bool can_be_item = true; + bool can_be_bag = true; if (resource_type == "item") { + can_be_bag = false; + // Items have their type encoded in the type attribute. if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) { @@ -406,6 +408,17 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, return false; } } + } else if (resource_type == "bag") { + can_be_item = false; + + // Bags have their type encoded in the type attribute. + if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) { + resource_type = maybe_type.value().to_string(); + } else { + diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) + << "<bag> must have a 'type' attribute"); + return false; + } } // Get the name of the resource. This will be checked later, because not all @@ -426,36 +439,61 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, return true; } - const auto item_iter = elToItemMap.find(resource_type); - if (item_iter != elToItemMap.end()) { - // This is an item, record its type and format and start parsing. + if (can_be_item) { + const auto item_iter = elToItemMap.find(resource_type); + if (item_iter != elToItemMap.end()) { + // This is an item, record its type and format and start parsing. - if (!maybe_name) { - diag_->Error(DiagMessage(out_resource->source) - << "<" << parser->element_name() - << "> missing 'name' attribute"); - return false; - } + if (!maybe_name) { + diag_->Error(DiagMessage(out_resource->source) + << "<" << parser->element_name() << "> missing 'name' attribute"); + return false; + } - out_resource->name.type = item_iter->second.type; - out_resource->name.entry = maybe_name.value().to_string(); + out_resource->name.type = item_iter->second.type; + out_resource->name.entry = maybe_name.value().to_string(); - // Only use the implicit format for this type if it wasn't overridden. - if (!resource_format) { - resource_format = item_iter->second.format; - } + // Only use the implicit format for this type if it wasn't overridden. + if (!resource_format) { + resource_format = item_iter->second.format; + } - if (!ParseItem(parser, out_resource, resource_format)) { - return false; + if (!ParseItem(parser, out_resource, resource_format)) { + return false; + } + return true; } - return true; } // This might be a bag or something. - const auto bag_iter = elToBagMap.find(resource_type); - if (bag_iter != elToBagMap.end()) { - // Ensure we have a name (unless this is a <public-group>). - if (resource_type != "public-group") { + 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>). + if (resource_type != "public-group") { + if (!maybe_name) { + diag_->Error(DiagMessage(out_resource->source) + << "<" << parser->element_name() << "> missing 'name' attribute"); + return false; + } + + out_resource->name.entry = maybe_name.value().to_string(); + } + + // Call the associated parse method. The type will be filled in by the + // parse func. + if (!bag_iter->second(this, parser, out_resource)) { + return false; + } + return true; + } + } + + if (can_be_item) { + // Try parsing the elementName (or type) as a resource. These shall only be + // resources like 'layout' or 'xml' and they can only be references. + const ResourceType* parsed_type = ParseResourceType(resource_type); + if (parsed_type) { if (!maybe_name) { diag_->Error(DiagMessage(out_resource->source) << "<" << parser->element_name() @@ -463,39 +501,16 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, return false; } + out_resource->name.type = *parsed_type; out_resource->name.entry = maybe_name.value().to_string(); + out_resource->value = ParseXml(parser, android::ResTable_map::TYPE_REFERENCE, kNoRawString); + if (!out_resource->value) { + diag_->Error(DiagMessage(out_resource->source) + << "invalid value for type '" << *parsed_type << "'. Expected a reference"); + return false; + } + return true; } - - // Call the associated parse method. The type will be filled in by the - // parse func. - if (!bag_iter->second(this, parser, out_resource)) { - return false; - } - return true; - } - - // Try parsing the elementName (or type) as a resource. These shall only be - // resources like 'layout' or 'xml' and they can only be references. - const ResourceType* parsed_type = ParseResourceType(resource_type); - if (parsed_type) { - if (!maybe_name) { - diag_->Error(DiagMessage(out_resource->source) - << "<" << parser->element_name() - << "> missing 'name' attribute"); - return false; - } - - out_resource->name.type = *parsed_type; - out_resource->name.entry = maybe_name.value().to_string(); - out_resource->value = - ParseXml(parser, android::ResTable_map::TYPE_REFERENCE, kNoRawString); - if (!out_resource->value) { - diag_->Error(DiagMessage(out_resource->source) - << "invalid value for type '" << *parsed_type - << "'. Expected a reference"); - return false; - } - return true; } diag_->Warn(DiagMessage(out_resource->source) @@ -1048,9 +1063,9 @@ bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) { return true; } -bool ResourceParser::ParseStyle(xml::XmlPullParser* parser, +bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* parser, ParsedResource* out_resource) { - out_resource->name.type = ResourceType::kStyle; + out_resource->name.type = type; std::unique_ptr<Style> style = util::make_unique<Style>(); diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h index c12dacf1c707..cc0fa26f44d5 100644 --- a/tools/aapt2/ResourceParser.h +++ b/tools/aapt2/ResourceParser.h @@ -102,7 +102,8 @@ class ResourceParser { bool weak); Maybe<Attribute::Symbol> ParseEnumOrFlagItem(xml::XmlPullParser* parser, const android::StringPiece& tag); - bool ParseStyle(xml::XmlPullParser* parser, ParsedResource* out_resource); + bool ParseStyle(const ResourceType type, xml::XmlPullParser* parser, + ParsedResource* out_resource); bool ParseStyleItem(xml::XmlPullParser* parser, Style* style); bool ParseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* out_resource); diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp index 5762fb035a81..cf901dae11bb 100644 --- a/tools/aapt2/ResourceParser_test.cpp +++ b/tools/aapt2/ResourceParser_test.cpp @@ -719,4 +719,23 @@ TEST_F(ResourceParserTest, ParseItemElementWithFormat) { EXPECT_EQ(uint32_t(android::Res_value::TYPE_FLOAT), val->value.dataType); } +TEST_F(ResourceParserTest, ParseConfigVaryingItem) { + std::string input = R"EOF(<item name="foo" type="configVarying">Hey</item>)EOF"; + ASSERT_TRUE(TestParse(input)); + ASSERT_NE(nullptr, test::GetValue<String>(&table_, "configVarying/foo")); +} + +TEST_F(ResourceParserTest, ParseBagElement) { + std::string input = + R"EOF(<bag name="bag" type="configVarying"><item name="test">Hello!</item></bag>)EOF"; + ASSERT_TRUE(TestParse(input)); + + Style* val = test::GetValue<Style>(&table_, "configVarying/bag"); + ASSERT_NE(nullptr, val); + + ASSERT_EQ(1u, val->entries.size()); + EXPECT_EQ(Reference(test::ParseNameOrDie("attr/test")), val->entries[0].key); + EXPECT_NE(nullptr, ValueCast<RawString>(val->entries[0].value.get())); +} + } // namespace aapt diff --git a/tools/aapt2/Resource_test.cpp b/tools/aapt2/Resource_test.cpp index 6acb4d3eb850..ad4e3ce02b32 100644 --- a/tools/aapt2/Resource_test.cpp +++ b/tools/aapt2/Resource_test.cpp @@ -49,6 +49,10 @@ TEST(ResourceTypeTest, ParseResourceTypes) { ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kColor); + type = ParseResourceType("configVarying"); + ASSERT_NE(type, nullptr); + EXPECT_EQ(*type, ResourceType::kConfigVarying); + type = ParseResourceType("dimen"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kDimen); diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index e5eaf2fe62a8..b4cf4f8d7f54 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -111,6 +111,36 @@ static bool FixCoreAppAttribute(xml::Element* el, SourcePathDiagnostics* diag) { return true; } +// Checks that <uses-feature> has android:glEsVersion or android:name, not both (or neither). +static bool VerifyUsesFeature(xml::Element* el, SourcePathDiagnostics* diag) { + bool has_name = false; + if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name")) { + if (attr->value.empty()) { + diag->Error(DiagMessage(el->line_number) + << "android:name in <uses-feature> must not be empty"); + return false; + } + has_name = true; + } + + bool has_gl_es_version = false; + if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "glEsVersion")) { + if (has_name) { + diag->Error(DiagMessage(el->line_number) + << "cannot define both android:name and android:glEsVersion in <uses-feature>"); + return false; + } + has_gl_es_version = true; + } + + if (!has_name && !has_gl_es_version) { + diag->Error(DiagMessage(el->line_number) + << "<uses-feature> must have either android:name or android:glEsVersion attribute"); + return false; + } + return true; +} + bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag) { // First verify some options. @@ -134,15 +164,25 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, } } - // Common intent-filter actions. + // Common <intent-filter> actions. xml::XmlNodeAction intent_filter_action; intent_filter_action["action"]; intent_filter_action["category"]; intent_filter_action["data"]; - // Common meta-data actions. + // Common <meta-data> actions. xml::XmlNodeAction meta_data_action; + // Common <uses-feature> actions. + xml::XmlNodeAction uses_feature_action; + uses_feature_action.Action(VerifyUsesFeature); + + // Common component actions. + xml::XmlNodeAction component_action; + component_action.Action(RequiredNameIsJavaClassName); + component_action["intent-filter"] = intent_filter_action; + component_action["meta-data"] = meta_data_action; + // Manifest actions. xml::XmlNodeAction& manifest_action = (*executor)["manifest"]; manifest_action.Action(VerifyManifest); @@ -190,6 +230,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, }); // Instrumentation actions. + manifest_action["instrumentation"].Action(RequiredNameIsJavaClassName); manifest_action["instrumentation"].Action([&](xml::Element* el) -> bool { if (!options_.rename_instrumentation_target_package) { return true; @@ -201,6 +242,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, } return true; }); + manifest_action["instrumentation"]["meta-data"] = meta_data_action; manifest_action["original-package"]; manifest_action["protected-broadcast"]; @@ -208,51 +250,28 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, manifest_action["permission"]; manifest_action["permission-tree"]; manifest_action["permission-group"]; - manifest_action["uses-configuration"]; - manifest_action["uses-feature"]; manifest_action["supports-screens"]; - + manifest_action["uses-feature"] = uses_feature_action; + manifest_action["feature-group"]["uses-feature"] = uses_feature_action; manifest_action["compatible-screens"]; manifest_action["compatible-screens"]["screen"]; - manifest_action["supports-gl-texture"]; - manifest_action["meta-data"] = meta_data_action; // Application actions. xml::XmlNodeAction& application_action = manifest_action["application"]; application_action.Action(OptionalNameIsJavaClassName); - // Uses library actions. application_action["uses-library"]; - - // Meta-data. application_action["meta-data"] = meta_data_action; - - // Activity actions. - application_action["activity"].Action(RequiredNameIsJavaClassName); - application_action["activity"]["intent-filter"] = intent_filter_action; - application_action["activity"]["meta-data"] = meta_data_action; - - // Activity alias actions. - application_action["activity-alias"]["intent-filter"] = intent_filter_action; - application_action["activity-alias"]["meta-data"] = meta_data_action; - - // Service actions. - application_action["service"].Action(RequiredNameIsJavaClassName); - application_action["service"]["intent-filter"] = intent_filter_action; - application_action["service"]["meta-data"] = meta_data_action; - - // Receiver actions. - application_action["receiver"].Action(RequiredNameIsJavaClassName); - application_action["receiver"]["intent-filter"] = intent_filter_action; - application_action["receiver"]["meta-data"] = meta_data_action; + application_action["activity"] = component_action; + application_action["activity-alias"] = component_action; + application_action["service"] = component_action; + application_action["receiver"] = component_action; // Provider actions. - application_action["provider"].Action(RequiredNameIsJavaClassName); - application_action["provider"]["intent-filter"] = intent_filter_action; - application_action["provider"]["meta-data"] = meta_data_action; + application_action["provider"] = component_action; application_action["provider"]["grant-uri-permissions"]; application_action["provider"]["path-permissions"]; diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp index 12a304a39f4b..ce84993feebe 100644 --- a/tools/aapt2/link/ManifestFixer_test.cpp +++ b/tools/aapt2/link/ManifestFixer_test.cpp @@ -90,7 +90,7 @@ TEST_F(ManifestFixerTest, EnsureManifestHasPackage) { } TEST_F(ManifestFixerTest, AllowMetaData) { - auto doc = Verify(R"EOF( + auto doc = Verify(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"> <meta-data /> @@ -98,12 +98,13 @@ TEST_F(ManifestFixerTest, AllowMetaData) { <meta-data /> <activity android:name=".Hi"><meta-data /></activity> <activity-alias android:name=".Ho"><meta-data /></activity-alias> - <receiver android:name=".OffToWork"><meta-data /></receiver> - <provider android:name=".We"><meta-data /></provider> - <service android:name=".Go"><meta-data /></service> + <receiver android:name=".OffTo"><meta-data /></receiver> + <provider android:name=".Work"><meta-data /></provider> + <service android:name=".We"><meta-data /></service> </application> + <instrumentation android:name=".Go"><meta-data /></instrumentation> </manifest>)EOF"); - ASSERT_NE(nullptr, doc); + ASSERT_NE(nullptr, doc); } TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) { @@ -290,7 +291,7 @@ TEST_F(ManifestFixerTest, std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"> - <instrumentation android:targetPackage="android" /> + <instrumentation android:name=".TestRunner" android:targetPackage="android" /> </manifest>)EOF", options); ASSERT_NE(nullptr, doc); @@ -354,4 +355,51 @@ TEST_F(ManifestFixerTest, EnsureManifestAttributesAreTyped) { EXPECT_NE(nullptr, ValueCast<BinaryPrimitive>(attr->compiled_value.get())); } +TEST_F(ManifestFixerTest, UsesFeatureMustHaveNameOrGlEsVersion) { + std::string input = R"EOF( + <manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android"> + <uses-feature android:name="feature" /> + <uses-feature android:glEsVersion="1" /> + <feature-group /> + <feature-group> + <uses-feature android:name="feature_in_group" /> + <uses-feature android:glEsVersion="2" /> + </feature-group> + </manifest>)EOF"; + EXPECT_NE(nullptr, Verify(input)); + + input = R"EOF( + <manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android"> + <uses-feature android:name="feature" android:glEsVersion="1" /> + </manifest>)EOF"; + EXPECT_EQ(nullptr, Verify(input)); + + input = R"EOF( + <manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android"> + <uses-feature /> + </manifest>)EOF"; + EXPECT_EQ(nullptr, Verify(input)); + + input = R"EOF( + <manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android"> + <feature-group> + <uses-feature android:name="feature" android:glEsVersion="1" /> + </feature-group> + </manifest>)EOF"; + EXPECT_EQ(nullptr, Verify(input)); + + input = R"EOF( + <manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android"> + <feature-group> + <uses-feature /> + </feature-group> + </manifest>)EOF"; + EXPECT_EQ(nullptr, Verify(input)); +} + } // namespace aapt diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md index e2a752e488ea..44d22c42468d 100644 --- a/tools/aapt2/readme.md +++ b/tools/aapt2/readme.md @@ -1,5 +1,12 @@ # Android Asset Packaging Tool 2.0 (AAPT2) release notes +## Version 2.6 +### `aapt2` +- Support legacy `configVarying` resource type. +- Support `<bag>` tag and treat as `<style>` regardless of type. +- Add `<feature-group>` manifest tag verification. +- Add `<meta-data>` tag support to `<instrumentation>`. + ## Version 2.5 ### `aapt2 link ...` - Transition XML versioning: Adds a new flag `--no-version-transitions` to disable automatic |