summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Shane Farmer <safarmer@google.com> 2018-01-31 16:43:24 -0800
committer Shane Farmer <safarmer@google.com> 2018-02-02 12:11:18 -0800
commit11cdc1cf7ab2e915611e2bb7264bd312012a9b64 (patch)
tree80eafc89fd3c394718d6eb1e5d2023c4586fdb7a
parentd82eeb321b2f6ef504f7d5679ae2069997c70024 (diff)
AAPT2: Add order attribute to groups
Require explicit ordering of groups in the configuration file to ensure that the correct version code is set. Ordering based on a single ABI is straight forward to ensure Play Store delivers the correct APK, but when an APK needs more than one ABI things get messy quickly. This also goes for screen density etc. The only thing that is easily sorted without this attribute is android-sdk since an artifact can only reference a single SDK. Test: unit tests Test: manually split an APK with update config.xml Change-Id: I37a2b8b8a8409d6d6ff27c7142d4c8c8065a7a51
-rw-r--r--tools/aapt2/configuration/ConfigurationParser.cpp80
-rw-r--r--tools/aapt2/configuration/ConfigurationParser.internal.h34
-rw-r--r--tools/aapt2/configuration/ConfigurationParser_test.cpp129
-rw-r--r--tools/aapt2/configuration/aapt2.xsd5
-rw-r--r--tools/aapt2/configuration/example/config.xml24
5 files changed, 212 insertions, 60 deletions
diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp
index eabeb47fccec..902334b98d00 100644
--- a/tools/aapt2/configuration/ConfigurationParser.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser.cpp
@@ -20,6 +20,7 @@
#include <functional>
#include <map>
#include <memory>
+#include <string>
#include <utility>
#include "android-base/file.h"
@@ -93,6 +94,7 @@ class NoopDiagnostics : public IDiagnostics {
};
NoopDiagnostics noop_;
+/** Returns the value of the label attribute for a given element. */
std::string GetLabel(const Element* element, IDiagnostics* diag) {
std::string label;
for (const auto& attr : element->attributes) {
@@ -108,6 +110,18 @@ std::string GetLabel(const Element* element, IDiagnostics* diag) {
return label;
}
+/** Returns the value of the version-code-order attribute for a given element. */
+Maybe<int32_t> GetVersionCodeOrder(const Element* element, IDiagnostics* diag) {
+ const xml::Attribute* version = element->FindAttribute("", "version-code-order");
+ if (version == nullptr) {
+ std::string label = GetLabel(element, diag);
+ diag->Error(DiagMessage() << "No version-code-order found for element '" << element->name
+ << "' with label '" << label << "'");
+ return {};
+ }
+ return std::stoi(version->value);
+}
+
/** XML node visitor that removes all of the namespace URIs from the node and all children. */
class NamespaceVisitor : public xml::Visitor {
public:
@@ -437,26 +451,37 @@ Maybe<std::vector<OutputArtifact>> ConfigurationParser::Parse(
// Convert from a parsed configuration to a list of artifacts for processing.
const std::string& apk_name = file::GetFilename(apk_path).to_string();
std::vector<OutputArtifact> output_artifacts;
- bool has_errors = false;
PostProcessingConfiguration& config = maybe_config.value();
- config.SortArtifacts();
+ bool valid = true;
int version = 1;
+
for (const ConfiguredArtifact& artifact : config.artifacts) {
Maybe<OutputArtifact> output_artifact = ToOutputArtifact(artifact, apk_name, config, diag_);
if (!output_artifact) {
// Defer return an error condition so that all errors are reported.
- has_errors = true;
+ valid = false;
} else {
output_artifact.value().version = version++;
output_artifacts.push_back(std::move(output_artifact.value()));
}
}
- if (has_errors) {
+ if (!config.ValidateVersionCodeOrdering(diag_)) {
+ diag_->Error(DiagMessage() << "could not validate post processing configuration");
+ valid = false;
+ }
+
+ if (valid) {
+ // Sorting artifacts requires that all references are valid as it uses them to determine order.
+ config.SortArtifacts();
+ }
+
+ if (!valid) {
return {};
}
+
return {output_artifacts};
}
@@ -509,8 +534,15 @@ bool AbiGroupTagHandler(PostProcessingConfiguration* config, Element* root_eleme
return false;
}
- auto& group = GetOrCreateGroup(label, &config->abi_groups);
bool valid = true;
+ OrderedEntry<Abi>& entry = config->abi_groups[label];
+ Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ if (!order) {
+ valid = false;
+ } else {
+ entry.order = order.value();
+ }
+ auto& group = entry.entry;
// Special case for empty abi-group tag. Label will be used as the ABI.
if (root_element->GetChildElements().empty()) {
@@ -519,7 +551,7 @@ bool AbiGroupTagHandler(PostProcessingConfiguration* config, Element* root_eleme
return false;
}
group.push_back(abi->second);
- return true;
+ return valid;
}
for (auto* child : root_element->GetChildElements()) {
@@ -553,8 +585,15 @@ bool ScreenDensityGroupTagHandler(PostProcessingConfiguration* config, Element*
return false;
}
- auto& group = GetOrCreateGroup(label, &config->screen_density_groups);
bool valid = true;
+ OrderedEntry<ConfigDescription>& entry = config->screen_density_groups[label];
+ Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ if (!order) {
+ valid = false;
+ } else {
+ entry.order = order.value();
+ }
+ auto& group = entry.entry;
// Special case for empty screen-density-group tag. Label will be used as the screen density.
if (root_element->GetChildElements().empty()) {
@@ -613,8 +652,15 @@ bool LocaleGroupTagHandler(PostProcessingConfiguration* config, Element* root_el
return false;
}
- auto& group = GetOrCreateGroup(label, &config->locale_groups);
bool valid = true;
+ OrderedEntry<ConfigDescription>& entry = config->locale_groups[label];
+ Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ if (!order) {
+ valid = false;
+ } else {
+ entry.order = order.value();
+ }
+ auto& group = entry.entry;
// Special case to auto insert a locale for an empty group. Label will be used for locale.
if (root_element->GetChildElements().empty()) {
@@ -728,8 +774,15 @@ bool GlTextureGroupTagHandler(PostProcessingConfiguration* config, Element* root
return false;
}
- auto& group = GetOrCreateGroup(label, &config->gl_texture_groups);
bool valid = true;
+ OrderedEntry<GlTexture>& entry = config->gl_texture_groups[label];
+ Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ if (!order) {
+ valid = false;
+ } else {
+ entry.order = order.value();
+ }
+ auto& group = entry.entry;
GlTexture result;
for (auto* child : root_element->GetChildElements()) {
@@ -771,8 +824,15 @@ bool DeviceFeatureGroupTagHandler(PostProcessingConfiguration* config, Element*
return false;
}
- auto& group = GetOrCreateGroup(label, &config->device_feature_groups);
bool valid = true;
+ OrderedEntry<DeviceFeature>& entry = config->device_feature_groups[label];
+ Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ if (!order) {
+ valid = false;
+ } else {
+ entry.order = order.value();
+ }
+ auto& group = entry.entry;
for (auto* child : root_element->GetChildElements()) {
if (child->name != "supports-feature") {
diff --git a/tools/aapt2/configuration/ConfigurationParser.internal.h b/tools/aapt2/configuration/ConfigurationParser.internal.h
index a583057427e6..f071a69fc9e3 100644
--- a/tools/aapt2/configuration/ConfigurationParser.internal.h
+++ b/tools/aapt2/configuration/ConfigurationParser.internal.h
@@ -33,18 +33,31 @@ namespace configuration {
template <typename T>
struct OrderedEntry {
- size_t order;
+ int32_t order;
std::vector<T> entry;
};
-/** A mapping of group labels to group of configuration items. */
-template <class T>
-using Group = std::unordered_map<std::string, OrderedEntry<T>>;
-
/** A mapping of group label to a single configuration item. */
template <class T>
using Entry = std::unordered_map<std::string, T>;
+/** A mapping of group labels to group of configuration items. */
+template <class T>
+using Group = Entry<OrderedEntry<T>>;
+
+template<typename T>
+bool IsGroupValid(const Group<T>& group, const std::string& name, IDiagnostics* diag) {
+ std::set<int32_t> orders;
+ for (const auto& p : group) {
+ orders.insert(p.second.order);
+ }
+ bool valid = orders.size() == group.size();
+ if (!valid) {
+ diag->Error(DiagMessage() << name << " have overlapping version-code-order attributes");
+ }
+ return valid;
+}
+
/** Retrieves an entry from the provided Group, creating a new instance if one does not exist. */
template <typename T>
std::vector<T>& GetOrCreateGroup(std::string label, Group<T>* group) {
@@ -93,7 +106,7 @@ class ComparisonChain {
private:
template <typename T>
- inline size_t GetGroupOrder(const Group<T>& groups, const Maybe<std::string>& label) {
+ inline size_t GetGroupOrder(const Entry<T>& groups, const Maybe<std::string>& label) {
if (!label) {
return std::numeric_limits<size_t>::max();
}
@@ -141,6 +154,15 @@ struct PostProcessingConfiguration {
Group<GlTexture> gl_texture_groups;
Entry<AndroidSdk> android_sdks;
+ bool ValidateVersionCodeOrdering(IDiagnostics* diag) {
+ bool valid = IsGroupValid(abi_groups, "abi-groups", diag);
+ valid &= IsGroupValid(screen_density_groups, "screen-density-groups", diag);
+ valid &= IsGroupValid(locale_groups, "locale-groups", diag);
+ valid &= IsGroupValid(device_feature_groups, "device-feature-groups", diag);
+ valid &= IsGroupValid(gl_texture_groups, "gl-texture-groups", diag);
+ return valid;
+ }
+
/**
* Sorts the configured artifacts based on the ordering of the groups in the configuration file.
* The only exception to this rule is Android SDK versions. Larger SDK versions will have a larger
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index 0329846a5bb5..febbb2ed11a0 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -82,22 +82,22 @@ using ::testing::StrEq;
constexpr const char* kValidConfig = R"(<?xml version="1.0" encoding="utf-8" ?>
<post-process xmlns="http://schemas.android.com/tools/aapt">
<abi-groups>
- <abi-group label="arm">
- <abi>armeabi-v7a</abi>
- <abi>arm64-v8a</abi>
- </abi-group>
- <abi-group label="other">
+ <abi-group label="other" version-code-order="2">
<abi>x86</abi>
<abi>mips</abi>
</abi-group>
+ <abi-group label="arm" version-code-order="1">
+ <abi>armeabi-v7a</abi>
+ <abi>arm64-v8a</abi>
+ </abi-group>
</abi-groups>
<screen-density-groups>
- <screen-density-group label="large">
+ <screen-density-group label="large" version-code-order="2">
<screen-density>xhdpi</screen-density>
<screen-density>xxhdpi</screen-density>
<screen-density>xxxhdpi</screen-density>
</screen-density-group>
- <screen-density-group label="alldpi">
+ <screen-density-group label="alldpi" version-code-order="1">
<screen-density>ldpi</screen-density>
<screen-density>mdpi</screen-density>
<screen-density>hdpi</screen-density>
@@ -107,17 +107,20 @@ constexpr const char* kValidConfig = R"(<?xml version="1.0" encoding="utf-8" ?>
</screen-density-group>
</screen-density-groups>
<locale-groups>
- <locale-group label="europe">
+ <locale-group label="europe" version-code-order="1">
<locale>en</locale>
<locale>es</locale>
<locale>fr</locale>
<locale>de</locale>
</locale-group>
- <locale-group label="north-america">
+ <locale-group label="north-america" version-code-order="2">
<locale>en</locale>
<locale>es-rMX</locale>
<locale>fr-rCA</locale>
</locale-group>
+ <locale-group label="all" version-code-order="-1">
+ <locale />
+ </locale-group>
</locale-groups>
<android-sdks>
<android-sdk
@@ -131,14 +134,14 @@ constexpr const char* kValidConfig = R"(<?xml version="1.0" encoding="utf-8" ?>
</android-sdk>
</android-sdks>
<gl-texture-groups>
- <gl-texture-group label="dxt1">
+ <gl-texture-group label="dxt1" version-code-order="2">
<gl-texture name="GL_EXT_texture_compression_dxt1">
<texture-path>assets/dxt1/*</texture-path>
</gl-texture>
</gl-texture-group>
</gl-texture-groups>
<device-feature-groups>
- <device-feature-group label="low-latency">
+ <device-feature-group label="low-latency" version-code-order="2">
<supports-feature>android.hardware.audio.low_latency</supports-feature>
</device-feature-group>
</device-feature-groups>
@@ -188,19 +191,22 @@ TEST_F(ConfigurationParserTest, ExtractConfiguration) {
auto& arm = config.abi_groups["arm"];
auto& other = config.abi_groups["other"];
- EXPECT_EQ(arm.order, 1ul);
- EXPECT_EQ(other.order, 2ul);
+ EXPECT_EQ(arm.order, 1);
+ EXPECT_EQ(other.order, 2);
auto& large = config.screen_density_groups["large"];
auto& alldpi = config.screen_density_groups["alldpi"];
- EXPECT_EQ(large.order, 1ul);
- EXPECT_EQ(alldpi.order, 2ul);
+ EXPECT_EQ(large.order, 2);
+ EXPECT_EQ(alldpi.order, 1);
auto& north_america = config.locale_groups["north-america"];
auto& europe = config.locale_groups["europe"];
+ auto& all = config.locale_groups["all"];
// Checked in reverse to make sure access order does not matter.
- EXPECT_EQ(north_america.order, 2ul);
- EXPECT_EQ(europe.order, 1ul);
+ EXPECT_EQ(north_america.order, 2);
+ EXPECT_EQ(europe.order, 1);
+ EXPECT_EQ(all.order, -1);
+ EXPECT_EQ(3ul, config.locale_groups.size());
}
TEST_F(ConfigurationParserTest, ValidateFile) {
@@ -392,7 +398,7 @@ TEST_F(ConfigurationParserTest, ArtifactFormatAction) {
TEST_F(ConfigurationParserTest, AbiGroupAction) {
static constexpr const char* xml = R"xml(
- <abi-group label="arm">
+ <abi-group label="arm" version-code-order="2">
<!-- First comment. -->
<abi>
armeabi-v7a
@@ -415,7 +421,8 @@ TEST_F(ConfigurationParserTest, AbiGroupAction) {
}
TEST_F(ConfigurationParserTest, AbiGroupAction_EmptyGroup) {
- static constexpr const char* xml = R"xml(<abi-group label="arm64-v8a"/>)xml";
+ static constexpr const char* xml =
+ R"xml(<abi-group label="arm64-v8a" version-code-order="3"/>)xml";
auto doc = test::BuildXmlDom(xml);
@@ -426,12 +433,23 @@ TEST_F(ConfigurationParserTest, AbiGroupAction_EmptyGroup) {
EXPECT_THAT(config.abi_groups, SizeIs(1ul));
ASSERT_EQ(1u, config.abi_groups.count("arm64-v8a"));
- auto& out = config.abi_groups["arm64-v8a"].entry;
- ASSERT_THAT(out, ElementsAre(Abi::kArm64V8a));
+ auto& out = config.abi_groups["arm64-v8a"];
+ ASSERT_THAT(out.entry, ElementsAre(Abi::kArm64V8a));
+ EXPECT_EQ(3, out.order);
+}
+
+TEST_F(ConfigurationParserTest, AbiGroupAction_EmptyGroup_NoOrder) {
+ static constexpr const char* xml = R"xml(<abi-group label="arm64-v8a"/>)xml";
+
+ auto doc = test::BuildXmlDom(xml);
+
+ PostProcessingConfiguration config;
+ bool ok = AbiGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_FALSE(ok);
}
TEST_F(ConfigurationParserTest, AbiGroupAction_InvalidEmptyGroup) {
- static constexpr const char* xml = R"xml(<abi-group label="arm"/>)xml";
+ static constexpr const char* xml = R"xml(<abi-group label="arm" order="2"/>)xml";
auto doc = test::BuildXmlDom(xml);
@@ -442,7 +460,7 @@ TEST_F(ConfigurationParserTest, AbiGroupAction_InvalidEmptyGroup) {
TEST_F(ConfigurationParserTest, ScreenDensityGroupAction) {
static constexpr const char* xml = R"xml(
- <screen-density-group label="large">
+ <screen-density-group label="large" version-code-order="2">
<screen-density>xhdpi</screen-density>
<screen-density>
xxhdpi
@@ -471,7 +489,8 @@ TEST_F(ConfigurationParserTest, ScreenDensityGroupAction) {
}
TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_EmtpyGroup) {
- static constexpr const char* xml = R"xml(<screen-density-group label="xhdpi"/>)xml";
+ static constexpr const char* xml =
+ R"xml(<screen-density-group label="xhdpi" version-code-order="4"/>)xml";
auto doc = test::BuildXmlDom(xml);
@@ -485,8 +504,19 @@ TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_EmtpyGroup) {
ConfigDescription xhdpi;
xhdpi.density = ResTable_config::DENSITY_XHIGH;
- auto& out = config.screen_density_groups["xhdpi"].entry;
- ASSERT_THAT(out, ElementsAre(xhdpi));
+ auto& out = config.screen_density_groups["xhdpi"];
+ EXPECT_THAT(out.entry, ElementsAre(xhdpi));
+ EXPECT_EQ(4, out.order);
+}
+
+TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_EmtpyGroup_NoVersion) {
+ static constexpr const char* xml = R"xml(<screen-density-group label="xhdpi"/>)xml";
+
+ auto doc = test::BuildXmlDom(xml);
+
+ PostProcessingConfiguration config;
+ bool ok = ScreenDensityGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_FALSE(ok);
}
TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_InvalidEmtpyGroup) {
@@ -501,7 +531,7 @@ TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_InvalidEmtpyGroup) {
TEST_F(ConfigurationParserTest, LocaleGroupAction) {
static constexpr const char* xml = R"xml(
- <locale-group label="europe">
+ <locale-group label="europe" version-code-order="2">
<locale>en</locale>
<locale>es</locale>
<locale>fr</locale>
@@ -528,7 +558,7 @@ TEST_F(ConfigurationParserTest, LocaleGroupAction) {
}
TEST_F(ConfigurationParserTest, LocaleGroupAction_EmtpyGroup) {
- static constexpr const char* xml = R"xml(<locale-group label="en"/>)xml";
+ static constexpr const char* xml = R"xml(<locale-group label="en" version-code-order="6"/>)xml";
auto doc = test::BuildXmlDom(xml);
@@ -539,11 +569,22 @@ TEST_F(ConfigurationParserTest, LocaleGroupAction_EmtpyGroup) {
ASSERT_EQ(1ul, config.locale_groups.size());
ASSERT_EQ(1u, config.locale_groups.count("en"));
- const auto& out = config.locale_groups["en"].entry;
+ const auto& out = config.locale_groups["en"];
ConfigDescription en = test::ParseConfigOrDie("en");
- ASSERT_THAT(out, ElementsAre(en));
+ EXPECT_THAT(out.entry, ElementsAre(en));
+ EXPECT_EQ(6, out.order);
+}
+
+TEST_F(ConfigurationParserTest, LocaleGroupAction_EmtpyGroup_NoOrder) {
+ static constexpr const char* xml = R"xml(<locale-group label="en"/>)xml";
+
+ auto doc = test::BuildXmlDom(xml);
+
+ PostProcessingConfiguration config;
+ bool ok = LocaleGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_FALSE(ok);
}
TEST_F(ConfigurationParserTest, LocaleGroupAction_InvalidEmtpyGroup) {
@@ -695,7 +736,7 @@ TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_NonNumeric) {
TEST_F(ConfigurationParserTest, GlTextureGroupAction) {
static constexpr const char* xml = R"xml(
- <gl-texture-group label="dxt1">
+ <gl-texture-group label="dxt1" version-code-order="2">
<gl-texture name="GL_EXT_texture_compression_dxt1">
<texture-path>assets/dxt1/main/*</texture-path>
<texture-path>
@@ -726,7 +767,7 @@ TEST_F(ConfigurationParserTest, GlTextureGroupAction) {
TEST_F(ConfigurationParserTest, DeviceFeatureGroupAction) {
static constexpr const char* xml = R"xml(
- <device-feature-group label="low-latency">
+ <device-feature-group label="low-latency" version-code-order="2">
<supports-feature>android.hardware.audio.low_latency</supports-feature>
<supports-feature>
android.hardware.audio.pro
@@ -749,6 +790,30 @@ TEST_F(ConfigurationParserTest, DeviceFeatureGroupAction) {
ASSERT_THAT(out, ElementsAre(low_latency, pro));
}
+TEST_F(ConfigurationParserTest, Group_Valid) {
+ Group<int32_t> group;
+ group["item1"].order = 1;
+ group["item2"].order = 2;
+ group["item3"].order = 3;
+ group["item4"].order = 4;
+ group["item5"].order = 5;
+ group["item6"].order = 6;
+
+ EXPECT_TRUE(IsGroupValid(group, "test", &diag_));
+}
+
+TEST_F(ConfigurationParserTest, Group_OverlappingOrder) {
+ Group<int32_t> group;
+ group["item1"].order = 1;
+ group["item2"].order = 2;
+ group["item3"].order = 3;
+ group["item4"].order = 2;
+ group["item5"].order = 5;
+ group["item6"].order = 1;
+
+ EXPECT_FALSE(IsGroupValid(group, "test", &diag_));
+}
+
// Artifact name parser test cases.
TEST(ArtifactTest, Simple) {
diff --git a/tools/aapt2/configuration/aapt2.xsd b/tools/aapt2/configuration/aapt2.xsd
index fb2f49bae7b7..a28e28be48f4 100644
--- a/tools/aapt2/configuration/aapt2.xsd
+++ b/tools/aapt2/configuration/aapt2.xsd
@@ -81,6 +81,7 @@
<xsd:element name="gl-texture" type="gl-texture" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="label" type="xsd:string"/>
+ <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/>
</xsd:complexType>
<xsd:complexType name="gl-texture">
@@ -95,6 +96,7 @@
<xsd:element name="supports-feature" type="xsd:string" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="label" type="xsd:string"/>
+ <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/>
</xsd:complexType>
<xsd:complexType name="abi-group">
@@ -102,6 +104,7 @@
<xsd:element name="abi" type="abi-name" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="label" type="xsd:string"/>
+ <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/>
</xsd:complexType>
<xsd:simpleType name="abi-name">
@@ -122,6 +125,7 @@
<xsd:element name="screen-density" type="screen-density" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="label" type="xsd:string"/>
+ <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/>
</xsd:complexType>
<xsd:simpleType name="screen-density">
@@ -158,6 +162,7 @@
<xsd:element name="locale" type="locale" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="label" type="xsd:string"/>
+ <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/>
</xsd:complexType>
<xsd:complexType name="locale">
diff --git a/tools/aapt2/configuration/example/config.xml b/tools/aapt2/configuration/example/config.xml
index d8aba09e836d..e6db2a0f461b 100644
--- a/tools/aapt2/configuration/example/config.xml
+++ b/tools/aapt2/configuration/example/config.xml
@@ -36,28 +36,28 @@
</android-sdks>
<abi-groups>
- <abi-group label="arm">
+ <abi-group label="arm" version-code-order="1">
<abi>armeabi-v7a</abi>
<abi>arm64-v8a</abi>
</abi-group>
- <abi-group label="other">
+ <abi-group label="other" version-code-order="2">
<abi>x86</abi>
<abi>mips</abi>
</abi-group>
</abi-groups>
<screen-density-groups>
- <screen-density-group label="large">
+ <screen-density-group label="alldpi" version-code-order="1">
+ <screen-density>ldpi</screen-density>
+ <screen-density>mdpi</screen-density>
+ <screen-density>hdpi</screen-density>
<screen-density>xhdpi</screen-density>
<screen-density>xxhdpi</screen-density>
<screen-density>xxxhdpi</screen-density>
</screen-density-group>
- <screen-density-group label="alldpi">
- <screen-density>ldpi</screen-density>
- <screen-density>mdpi</screen-density>
- <screen-density>hdpi</screen-density>
+ <screen-density-group label="large" version-code-order="2">
<screen-density>xhdpi</screen-density>
<screen-density>xxhdpi</screen-density>
<screen-density>xxxhdpi</screen-density>
@@ -65,26 +65,26 @@
</screen-density-groups>
<locale-groups>
- <locale-group label="europe">
+ <locale-group label="europe" version-code-order="1">
<locale lang="en"/>
<locale lang="es"/>
<locale lang="fr"/>
<locale lang="de" compressed="true"/>
</locale-group>
- <locale-group label="north-america">
+ <locale-group label="north-america" version-code-order="2">
<locale lang="en"/>
<locale lang="es" region="MX"/>
<locale lang="fr" region="CA" compressed="true"/>
</locale-group>
- <locale-group label="all">
+ <locale-group label="all" version-code-order="0">
<locale compressed="true"/>
</locale-group>
</locale-groups>
<gl-texture-groups>
- <gl-texture-group label="dxt1">
+ <gl-texture-group label="dxt1" version-code-order="1">
<gl-texture name="GL_EXT_texture_compression_dxt1">
<texture-path>assets/dxt1/*</texture-path>
</gl-texture>
@@ -92,7 +92,7 @@
</gl-texture-groups>
<device-feature-groups>
- <device-feature-group label="low-latency">
+ <device-feature-group label="low-latency" version-code-order="1">
<supports-feature>android.hardware.audio.low_latency</supports-feature>
</device-feature-group>
</device-feature-groups>