AAPT: Only print last uses-sdk tag
When an APK defines multiple "uses-sdk" tags, the Android runtime only
uses the minSdkVersion and targetSdkVersion values from the last
occurrence of the "uses-sdk" tag.
For example an application with the following tags:
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>
<uses-sdk android:maxSdkVersion="28"/>
Will have the following version codes at runtime:
minSdk=1 targetSdk=1
Another example:
<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="28"/>
<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="19"/>
Will have the following version codes at runtime:
minSdk=5 targetSdk=19
AAPT must print the version data from only the last tag, skipping other
occurrences of the tag.
Bug: 175789289
Test: manual
Change-Id: Ic855ff920d0b7abedd250d977bfa55189f4c8946
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 21386b8..f2c3b86 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -969,6 +969,8 @@
densities.add(dens);
}
+ std::vector<ResXMLParser::ResXMLPosition> tagsToSkip;
+
size_t len;
ResXMLTree::event_code_t code;
int depth = 0;
@@ -1091,6 +1093,42 @@
Vector<FeatureGroup> featureGroups;
KeyedVector<String8, ImpliedFeature> impliedFeatures;
+ {
+ int curDepth = 0;
+ ResXMLParser::ResXMLPosition initialPos;
+ tree.getPosition(&initialPos);
+
+ // Find all of the "uses-sdk" tags within the "manifest" tag.
+ std::vector<ResXMLParser::ResXMLPosition> usesSdkTagPositions;
+ ResXMLParser::ResXMLPosition curPos;
+ while ((code = tree.next()) != ResXMLTree::END_DOCUMENT &&
+ code != ResXMLTree::BAD_DOCUMENT) {
+ if (code == ResXMLTree::END_TAG) {
+ curDepth--;
+ continue;
+ }
+ if (code == ResXMLTree::START_TAG) {
+ curDepth++;
+ }
+ const char16_t* ctag16 = tree.getElementName(&len);
+ if (ctag16 == NULL || String8(ctag16) != "uses-sdk" || curDepth != 2) {
+ continue;
+ }
+
+ tree.getPosition(&curPos);
+ usesSdkTagPositions.emplace_back(curPos);
+ }
+
+ // Skip all "uses-sdk" tags besides the very last tag. The android runtime only uses
+ // the attribute values from the last defined tag.
+ for (size_t i = 0; i < usesSdkTagPositions.size() - 1; i++) {
+ tagsToSkip.emplace_back(usesSdkTagPositions[i]);
+ }
+
+ // Reset the position before parsing.
+ tree.setPosition(initialPos);
+ }
+
while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::END_TAG) {
@@ -1202,8 +1240,25 @@
if (code != ResXMLTree::START_TAG) {
continue;
}
+
depth++;
+ // If this tag should be skipped, skip to the end of this tag.
+ ResXMLParser::ResXMLPosition curPos;
+ tree.getPosition(&curPos);
+ if (std::find(tagsToSkip.begin(), tagsToSkip.end(), curPos) != tagsToSkip.end()) {
+ const int breakDepth = depth - 1;
+ while ((code = tree.next()) != ResXMLTree::END_DOCUMENT &&
+ code != ResXMLTree::BAD_DOCUMENT) {
+ if (code == ResXMLTree::END_TAG && --depth == breakDepth) {
+ break;
+ } else if (code == ResXMLTree::START_TAG) {
+ depth++;
+ }
+ }
+ continue;
+ }
+
const char16_t* ctag16 = tree.getElementName(&len);
if (ctag16 == NULL) {
SourcePos(manifestFile, tree.getLineNumber()).error(