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
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 15d7e2e..d44d79a 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -25,7 +25,7 @@
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 fdabce1..35971e7 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -39,6 +39,8 @@
return "bool";
case ResourceType::kColor:
return "color";
+ case ResourceType::kConfigVarying:
+ return "configVarying";
case ResourceType::kDimen:
return "dimen";
case ResourceType::kDrawable:
@@ -85,6 +87,7 @@
{"^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 1950ea3..4d915d9 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -44,6 +44,11 @@
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 79379fe..1c750c6 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -338,50 +338,52 @@
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 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)},
- {"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 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 @@
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 @@
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();
+
+ // 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;
+ }
+ return true;
}
-
- 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;
- }
-
- if (!ParseItem(parser, out_resource, resource_format)) {
- return false;
- }
- 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 @@
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 @@
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 c12dacf..cc0fa26 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -102,7 +102,8 @@
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 5762fb0..cf901da 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -719,4 +719,23 @@
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 6acb4d3..ad4e3ce 100644
--- a/tools/aapt2/Resource_test.cpp
+++ b/tools/aapt2/Resource_test.cpp
@@ -49,6 +49,10 @@
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 e5eaf2f..b4cf4f8 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -111,6 +111,36 @@
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 @@
}
}
- // 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 @@
});
// 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 @@
}
return true;
});
+ manifest_action["instrumentation"]["meta-data"] = meta_data_action;
manifest_action["original-package"];
manifest_action["protected-broadcast"];
@@ -208,51 +250,28 @@
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 12a304a..ce84993 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -90,7 +90,7 @@
}
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 @@
<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 @@
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 @@
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 e2a752e..44d22c4 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