diff --git a/tests/Split/res/layout/main.xml b/tests/Split/res/layout/main.xml index 607cdb0b130b4..5a33c86355d29 100644 --- a/tests/Split/res/layout/main.xml +++ b/tests/Split/res/layout/main.xml @@ -21,5 +21,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" + android:colorAccent="#ffffffff" + android:paddingStart="13dp" android:src="@drawable/image"/> diff --git a/tests/Split/res/values-v10/values.xml b/tests/Split/res/values-v10/values.xml new file mode 100644 index 0000000000000..3d41a84bdbb82 --- /dev/null +++ b/tests/Split/res/values-v10/values.xml @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/tests/Split/res/values-v17/values.xml b/tests/Split/res/values-v17/values.xml new file mode 100644 index 0000000000000..c24eeaeced371 --- /dev/null +++ b/tests/Split/res/values-v17/values.xml @@ -0,0 +1,24 @@ + + + + + + + diff --git a/tests/Split/res/values/values.xml b/tests/Split/res/values/values.xml index 68edc77d47f8d..aabb2325ba450 100644 --- a/tests/Split/res/values/values.xml +++ b/tests/Split/res/values/values.xml @@ -44,4 +44,10 @@ @string/boom 25dp + + diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index e87138c898182..e000a1dc1feb9 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -4329,38 +4329,80 @@ bool ResourceTable::getItemValue( } /** - * Returns true if the given attribute ID comes from - * a platform version from or after L. + * Returns the SDK version at which the attribute was + * made public, or -1 if the resource ID is not an attribute + * or is not public. */ -bool ResourceTable::isAttributeFromL(uint32_t attrId) { - const uint32_t baseAttrId = 0x010103f7; - if ((attrId & 0xffff0000) != (baseAttrId & 0xffff0000)) { - return false; +int ResourceTable::getPublicAttributeSdkLevel(uint32_t attrId) const { + if (Res_GETPACKAGE(attrId) + 1 != 0x01 || Res_GETTYPE(attrId) + 1 != 0x01) { + return -1; } uint32_t specFlags; if (!mAssets->getIncludedResources().getResourceFlags(attrId, &specFlags)) { - return false; + return -1; + } + + if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) { + return -1; + } + + const size_t entryId = Res_GETENTRY(attrId); + if (entryId <= 0x021c) { + return 1; + } else if (entryId <= 0x021d) { + return 2; + } else if (entryId <= 0x0269) { + return SDK_CUPCAKE; + } else if (entryId <= 0x028d) { + return SDK_DONUT; + } else if (entryId <= 0x02ad) { + return SDK_ECLAIR; + } else if (entryId <= 0x02b3) { + return SDK_ECLAIR_0_1; + } else if (entryId <= 0x02b5) { + return SDK_ECLAIR_MR1; + } else if (entryId <= 0x02bd) { + return SDK_FROYO; + } else if (entryId <= 0x02cb) { + return SDK_GINGERBREAD; + } else if (entryId <= 0x0361) { + return SDK_HONEYCOMB; + } else if (entryId <= 0x0366) { + return SDK_HONEYCOMB_MR1; + } else if (entryId <= 0x03a6) { + return SDK_HONEYCOMB_MR2; + } else if (entryId <= 0x03ae) { + return SDK_JELLY_BEAN; + } else if (entryId <= 0x03cc) { + return SDK_JELLY_BEAN_MR1; + } else if (entryId <= 0x03da) { + return SDK_JELLY_BEAN_MR2; + } else if (entryId <= 0x03f1) { + return SDK_KITKAT; + } else if (entryId <= 0x03f6) { + return SDK_KITKAT_WATCH; + } else if (entryId <= 0x04ce) { + return SDK_LOLLIPOP; + } else { + // Anything else is marked as defined in + // SDK_LOLLIPOP_MR1 since after this + // version no attribute compat work + // needs to be done. + return SDK_LOLLIPOP_MR1; } - - return (specFlags & ResTable_typeSpec::SPEC_PUBLIC) != 0 && - (attrId & 0x0000ffff) >= (baseAttrId & 0x0000ffff); } -static bool isMinSdkVersionLOrAbove(const Bundle* bundle) { - if (bundle->getMinSdkVersion() != NULL && strlen(bundle->getMinSdkVersion()) > 0) { - const char firstChar = bundle->getMinSdkVersion()[0]; - if (firstChar >= 'L' && firstChar <= 'Z') { - // L is the code-name for the v21 release. - return true; - } - - const int minSdk = atoi(bundle->getMinSdkVersion()); - if (minSdk >= SDK_LOLLIPOP) { - return true; - } +/** + * First check the Manifest, then check the command line flag. + */ +static int getMinSdkVersion(const Bundle* bundle) { + if (bundle->getManifestMinSdkVersion() != NULL && strlen(bundle->getManifestMinSdkVersion()) > 0) { + return atoi(bundle->getManifestMinSdkVersion()); + } else if (bundle->getMinSdkVersion() != NULL && strlen(bundle->getMinSdkVersion()) > 0) { + return atoi(bundle->getMinSdkVersion()); } - return false; + return 0; } /** @@ -4406,9 +4448,10 @@ static bool isMinSdkVersionLOrAbove(const Bundle* bundle) { * attribute will be respected. */ status_t ResourceTable::modifyForCompat(const Bundle* bundle) { - if (isMinSdkVersionLOrAbove(bundle)) { - // If this app will only ever run on L+ devices, - // we don't need to do any compatibility work. + const int minSdk = getMinSdkVersion(bundle); + if (minSdk >= SDK_LOLLIPOP_MR1) { + // Lollipop MR1 and up handles public attributes differently, no + // need to do any compat modifications. return NO_ERROR; } @@ -4447,20 +4490,19 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle) { } const ConfigDescription& config = entries.keyAt(ei); - if (config.sdkVersion >= SDK_LOLLIPOP) { - // We don't need to do anything if the resource is - // already qualified for version 21 or higher. + if (config.sdkVersion >= SDK_LOLLIPOP_MR1) { continue; } - Vector attributesToRemove; + KeyedVector > attributesToRemove; const KeyedVector& bag = e->getBag(); const size_t bagCount = bag.size(); for (size_t bi = 0; bi < bagCount; bi++) { const Item& item = bag.valueAt(bi); const uint32_t attrId = getResId(bag.keyAt(bi), &attr16); - if (isAttributeFromL(attrId)) { - attributesToRemove.add(bag.keyAt(bi)); + const int sdkLevel = getPublicAttributeSdkLevel(attrId); + if (sdkLevel > 1 && sdkLevel > config.sdkVersion && sdkLevel > minSdk) { + AaptUtil::appendValue(attributesToRemove, sdkLevel, bag.keyAt(bi)); } } @@ -4468,16 +4510,41 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle) { continue; } - // Duplicate the entry under the same configuration - // but with sdkVersion == SDK_LOLLIPOP. - ConfigDescription newConfig(config); - newConfig.sdkVersion = SDK_LOLLIPOP; - entriesToAdd.add(key_value_pair_t >( - newConfig, new Entry(*e))); + const size_t sdkCount = attributesToRemove.size(); + for (size_t i = 0; i < sdkCount; i++) { + const int sdkLevel = attributesToRemove.keyAt(i); + + // Duplicate the entry under the same configuration + // but with sdkVersion == sdkLevel. + ConfigDescription newConfig(config); + newConfig.sdkVersion = sdkLevel; + + sp newEntry = new Entry(*e); + + // Remove all items that have a higher SDK level than + // the one we are synthesizing. + for (size_t j = 0; j < sdkCount; j++) { + if (j == i) { + continue; + } + + if (attributesToRemove.keyAt(j) > sdkLevel) { + const size_t attrCount = attributesToRemove[j].size(); + for (size_t k = 0; k < attrCount; k++) { + newEntry->removeFromBag(attributesToRemove[j][k]); + } + } + } + + entriesToAdd.add(key_value_pair_t >( + newConfig, newEntry)); + } // Remove the attribute from the original. for (size_t i = 0; i < attributesToRemove.size(); i++) { - e->removeFromBag(attributesToRemove[i]); + for (size_t j = 0; j < attributesToRemove[i].size(); j++) { + e->removeFromBag(attributesToRemove[i][j]); + } } } @@ -4494,7 +4561,7 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle) { if (bundle->getVerbose()) { entriesToAdd[i].value->getPos() .printf("using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.", - SDK_LOLLIPOP, + entriesToAdd[i].key.sdkVersion, String8(p->getName()).string(), String8(t->getName()).string(), String8(entriesToAdd[i].value->getName()).string(), @@ -4517,17 +4584,23 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle, const String16& resourceName, const sp& target, const sp& root) { - if (isMinSdkVersionLOrAbove(bundle)) { + const int minSdk = getMinSdkVersion(bundle); + if (minSdk >= SDK_LOLLIPOP_MR1) { + // Lollipop MR1 and up handles public attributes differently, no + // need to do any compat modifications. return NO_ERROR; } - if (target->getResourceType() == "" || target->getGroupEntry().toParams().sdkVersion >= SDK_LOLLIPOP) { + const ConfigDescription config(target->getGroupEntry().toParams()); + if (target->getResourceType() == "" || config.sdkVersion >= SDK_LOLLIPOP_MR1) { // Skip resources that have no type (AndroidManifest.xml) or are already version qualified with v21 // or higher. return NO_ERROR; } sp newRoot = NULL; + ConfigDescription newConfig(target->getGroupEntry().toParams()); + newConfig.sdkVersion = SDK_LOLLIPOP_MR1; Vector > nodesToVisit; nodesToVisit.push(root); @@ -4538,11 +4611,19 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle, const Vector& attrs = node->getAttributes(); for (size_t i = 0; i < attrs.size(); i++) { const XMLNode::attribute_entry& attr = attrs[i]; - if (isAttributeFromL(attr.nameResId)) { + const int sdkLevel = getPublicAttributeSdkLevel(attr.nameResId); + if (sdkLevel > 1 && sdkLevel > config.sdkVersion && sdkLevel > minSdk) { if (newRoot == NULL) { newRoot = root->clone(); } + // Find the smallest sdk version that we need to synthesize for + // and do that one. Subsequent versions will be processed on + // the next pass. + if (sdkLevel < newConfig.sdkVersion) { + newConfig.sdkVersion = sdkLevel; + } + if (bundle->getVerbose()) { SourcePos(node->getFilename(), node->getStartLineNumber()).printf( "removing attribute %s%s%s from <%s>", @@ -4568,9 +4649,6 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle, return NO_ERROR; } - ConfigDescription newConfig(target->getGroupEntry().toParams()); - newConfig.sdkVersion = SDK_LOLLIPOP; - // Look to see if we already have an overriding v21 configuration. sp cl = getConfigList(String16(mAssets->getPackage()), String16(target->getResourceType()), resourceName); @@ -4587,7 +4665,7 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle, if (bundle->getVerbose()) { SourcePos(target->getSourceFile(), -1).printf( "using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.", - SDK_LOLLIPOP, + newConfig.sdkVersion, mAssets->getPackage().string(), newFile->getResourceType().string(), String8(resourceName).string(), diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index 81590bc961db4..eef0ae10e60d3 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -575,7 +575,7 @@ class ResourceTable : public ResTable::Accessor const Item* getItem(uint32_t resID, uint32_t attrID) const; bool getItemValue(uint32_t resID, uint32_t attrID, Res_value* outValue); - bool isAttributeFromL(uint32_t attrId); + int getPublicAttributeSdkLevel(uint32_t attrId) const; String16 mAssetsPackage; diff --git a/tools/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h index 7fd10301b15c0..4e0fe10022a8f 100644 --- a/tools/aapt/SdkConstants.h +++ b/tools/aapt/SdkConstants.h @@ -37,6 +37,7 @@ enum { SDK_KITKAT = 19, SDK_KITKAT_WATCH = 20, SDK_LOLLIPOP = 21, + SDK_LOLLIPOP_MR1 = 22, }; #endif // H_AAPT_SDK_CONSTANTS