AAPT2: Insert <uses-sdk> element before <application>
PackageParser on the device uses the targetSdkVersion of the
app while it parses <application>. That means that if the
<uses-sdk> tag comes after <application>, the targetSdkVersion
is assumed to be 0.
Test: make libaapt2_tests
Change-Id: I60f2179a7ff44e7419217afb53f3d24f8c030f6e
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 717978ea..b525758 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -1356,8 +1356,8 @@
application_el->attributes.push_back(
xml::Attribute{xml::kSchemaAndroid, "hasCode", "false"});
- manifest_el->AddChild(std::move(application_el));
- namespace_android->AddChild(std::move(manifest_el));
+ manifest_el->AppendChild(std::move(application_el));
+ namespace_android->AppendChild(std::move(manifest_el));
doc->root = std::move(namespace_android);
return doc;
}
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index a418fc8..36a3494 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -309,10 +309,12 @@
if ((options_.min_sdk_version_default ||
options_.target_sdk_version_default) &&
root->FindChild({}, "uses-sdk") == nullptr) {
- // Auto insert a <uses-sdk> element.
+ // Auto insert a <uses-sdk> element. This must be inserted before the
+ // <application> tag. The device runtime PackageParser will make SDK version
+ // decisions while parsing <application>.
std::unique_ptr<xml::Element> uses_sdk = util::make_unique<xml::Element>();
uses_sdk->name = "uses-sdk";
- root->AddChild(std::move(uses_sdk));
+ root->InsertChild(0, std::move(uses_sdk));
}
xml::XmlActionExecutor executor;
@@ -327,7 +329,8 @@
if (options_.rename_manifest_package) {
// Rename manifest package outside of the XmlActionExecutor.
- // We need to extract the old package name and FullyQualify all class names.
+ // We need to extract the old package name and FullyQualify all class
+ // names.
if (!RenameManifestPackage(options_.rename_manifest_package.value(),
root)) {
return false;
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 0e29ba6..e9bc64a 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -168,6 +168,50 @@
EXPECT_EQ("22", attr->value);
}
+TEST_F(ManifestFixerTest, UsesSdkMustComeBeforeApplication) {
+ ManifestFixerOptions options = {std::string("8"), std::string("22")};
+ std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android">
+ <application android:name=".MainApplication" />
+ </manifest>)EOF",
+ options);
+ ASSERT_NE(nullptr, doc);
+
+ xml::Element* manifest_el = xml::FindRootElement(doc.get());
+ ASSERT_NE(nullptr, manifest_el);
+ ASSERT_EQ("manifest", manifest_el->name);
+
+ xml::Element* application_el = manifest_el->FindChild("", "application");
+ ASSERT_NE(nullptr, application_el);
+
+ xml::Element* uses_sdk_el = manifest_el->FindChild("", "uses-sdk");
+ ASSERT_NE(nullptr, uses_sdk_el);
+
+ // Check that the uses_sdk_el comes before application_el in the children
+ // vector.
+ // Since there are no namespaces here, these children are direct descendants
+ // of manifest.
+ auto uses_sdk_iter =
+ std::find_if(manifest_el->children.begin(), manifest_el->children.end(),
+ [&](const std::unique_ptr<xml::Node>& child) {
+ return child.get() == uses_sdk_el;
+ });
+
+ auto application_iter =
+ std::find_if(manifest_el->children.begin(), manifest_el->children.end(),
+ [&](const std::unique_ptr<xml::Node>& child) {
+ return child.get() == application_el;
+ });
+
+ ASSERT_NE(manifest_el->children.end(), uses_sdk_iter);
+ ASSERT_NE(manifest_el->children.end(), application_iter);
+
+ // The distance should be positive, meaning uses_sdk_iter comes before
+ // application_iter.
+ EXPECT_GT(std::distance(uses_sdk_iter, application_iter), 0);
+}
+
TEST_F(ManifestFixerTest, RenameManifestPackageAndFullyQualifyClasses) {
ManifestFixerOptions options;
options.rename_manifest_package = std::string("com.android");