diff options
author | Adam Lesinski <adamlesinski@google.com> | 2014-09-19 19:08:50 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2014-09-19 19:08:50 +0000 |
commit | 0fa7511db2ec1e2326938f92262d00f23876307e (patch) | |
tree | 79fe6be8c92ccf3acef647bbaa3ee85e6f890890 /tools/aapt | |
parent | e83bc5932eba4af67e2cd7b76648ae26740b9b01 (diff) | |
parent | a7d1a111c7176fdf853e53e74f6c7c7843c3c81c (diff) | |
download | frameworks_base-0fa7511db2ec1e2326938f92262d00f23876307e.zip frameworks_base-0fa7511db2ec1e2326938f92262d00f23876307e.tar.gz frameworks_base-0fa7511db2ec1e2326938f92262d00f23876307e.tar.bz2 |
am 9ee3ba23: am e7e9ad6d: am 863b1a90: am 19f9d54f: Merge "Fix backwards compat problem with AAPT public attrs" into lmp-dev
* commit '9ee3ba23395bd1a13bbfd3fe523ee611a5ca001b':
Fix backwards compat problem with AAPT public attrs
Diffstat (limited to 'tools/aapt')
-rw-r--r-- | tools/aapt/Resource.cpp | 24 | ||||
-rw-r--r-- | tools/aapt/ResourceTable.cpp | 196 | ||||
-rw-r--r-- | tools/aapt/ResourceTable.h | 8 |
3 files changed, 225 insertions, 3 deletions
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index 3ab3d59..1e6ba74 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -781,12 +781,18 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root) if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName", bundle->getVersionName(), errorOnFailedInsert, replaceVersion)) { return UNKNOWN_ERROR; + } else { + const XMLNode::attribute_entry* attr = root->getAttribute( + String16(RESOURCES_ANDROID_NAMESPACE), String16("versionName")); + if (attr != NULL) { + bundle->setVersionName(strdup(String8(attr->string).string())); + } } + sp<XMLNode> vers = root->getChildElement(String16(), String16("uses-sdk")); if (bundle->getMinSdkVersion() != NULL || bundle->getTargetSdkVersion() != NULL || bundle->getMaxSdkVersion() != NULL) { - sp<XMLNode> vers = root->getChildElement(String16(), String16("uses-sdk")); if (vers == NULL) { vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk")); root->insertChildAt(vers, 0); @@ -806,6 +812,14 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root) } } + if (vers != NULL) { + const XMLNode::attribute_entry* attr = vers->getAttribute( + String16(RESOURCES_ANDROID_NAMESPACE), String16("minSdkVersion")); + if (attr != NULL) { + bundle->setMinSdkVersion(strdup(String8(attr->string).string())); + } + } + if (bundle->getPlatformBuildVersionCode() != "") { if (!addTagAttribute(root, "", "platformBuildVersionCode", bundle->getPlatformBuildVersionCode(), errorOnFailedInsert, true)) { @@ -973,8 +987,8 @@ static ssize_t extractPlatformBuildVersion(ResXMLTree& tree, Bundle* bundle) { static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) { int32_t cookie = getPlatformAssetCookie(assets); if (cookie == 0) { - fprintf(stderr, "ERROR: Platform package not found\n"); - return UNKNOWN_ERROR; + // No platform was loaded. + return NO_ERROR; } ResXMLTree tree; @@ -1500,6 +1514,10 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil return err; } + if (table.modifyForCompat(bundle) != NO_ERROR) { + return UNKNOWN_ERROR; + } + //block.restart(); //printXMLBlock(&block); diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index dccc363..f66ed08 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -12,6 +12,7 @@ #include <androidfw/ResourceTypes.h> #include <utils/ByteOrder.h> +#include <utils/TypeHelpers.h> #include <stdarg.h> #define NOISY(x) //x @@ -3287,6 +3288,18 @@ ResourceTable::Item::Item(const SourcePos& _sourcePos, } } +ResourceTable::Entry::Entry(const Entry& entry) + : RefBase() + , mName(entry.mName) + , mParent(entry.mParent) + , mType(entry.mType) + , mItem(entry.mItem) + , mItemFormat(entry.mItemFormat) + , mBag(entry.mBag) + , mNameIndex(entry.mNameIndex) + , mParentId(entry.mParentId) + , mPos(entry.mPos) {} + status_t ResourceTable::Entry::makeItABag(const SourcePos& sourcePos) { if (mType == TYPE_BAG) { @@ -3372,6 +3385,17 @@ status_t ResourceTable::Entry::addToBag(const SourcePos& sourcePos, return NO_ERROR; } +status_t ResourceTable::Entry::removeFromBag(const String16& key) { + if (mType != Entry::TYPE_BAG) { + return NO_ERROR; + } + + if (mBag.removeItem(key) >= 0) { + return NO_ERROR; + } + return UNKNOWN_ERROR; +} + status_t ResourceTable::Entry::emptyBag(const SourcePos& sourcePos) { status_t err = makeItABag(sourcePos); @@ -4113,3 +4137,175 @@ bool ResourceTable::getItemValue( } return res; } + +/** + * Returns true if the given attribute ID comes from + * a platform version from or after L. + */ +bool ResourceTable::isAttributeFromL(uint32_t attrId) { + const uint32_t baseAttrId = 0x010103f7; + if ((attrId & 0xffff0000) != (baseAttrId & 0xffff0000)) { + return false; + } + + uint32_t specFlags; + if (!mAssets->getIncludedResources().getResourceFlags(attrId, &specFlags)) { + return false; + } + + return (specFlags & ResTable_typeSpec::SPEC_PUBLIC) != 0 && + (attrId & 0x0000ffff) >= (baseAttrId & 0x0000ffff); +} + +/** + * Modifies the entries in the resource table to account for compatibility + * issues with older versions of Android. + * + * This primarily handles the issue of private/public attribute clashes + * in framework resources. + * + * AAPT has traditionally assigned resource IDs to public attributes, + * and then followed those public definitions with private attributes. + * + * --- PUBLIC --- + * | 0x01010234 | attr/color + * | 0x01010235 | attr/background + * + * --- PRIVATE --- + * | 0x01010236 | attr/secret + * | 0x01010237 | attr/shhh + * + * Each release, when attributes are added, they take the place of the private + * attributes and the private attributes are shifted down again. + * + * --- PUBLIC --- + * | 0x01010234 | attr/color + * | 0x01010235 | attr/background + * | 0x01010236 | attr/shinyNewAttr + * | 0x01010237 | attr/highlyValuedFeature + * + * --- PRIVATE --- + * | 0x01010238 | attr/secret + * | 0x01010239 | attr/shhh + * + * Platform code may look for private attributes set in a theme. If an app + * compiled against a newer version of the platform uses a new public + * attribute that happens to have the same ID as the private attribute + * the older platform is expecting, then the behavior is undefined. + * + * We get around this by detecting any newly defined attributes (in L), + * copy the resource into a -v21 qualified resource, and delete the + * attribute from the original resource. This ensures that older platforms + * don't see the new attribute, but when running on L+ platforms, the + * attribute will be respected. + */ +status_t ResourceTable::modifyForCompat(const Bundle* bundle) { + if (bundle->getMinSdkVersion() != NULL) { + // If this app will only ever run on L+ devices, + // we don't need to do any compatibility work. + + if (String8("L") == bundle->getMinSdkVersion()) { + // Code-name for the v21 release. + return NO_ERROR; + } + + const int minSdk = atoi(bundle->getMinSdkVersion()); + if (minSdk >= SDK_L) { + return NO_ERROR; + } + } + + const String16 attr16("attr"); + + const size_t packageCount = mOrderedPackages.size(); + for (size_t pi = 0; pi < packageCount; pi++) { + sp<Package> p = mOrderedPackages.itemAt(pi); + if (p == NULL || p->getTypes().size() == 0) { + // Empty, skip! + continue; + } + + const size_t typeCount = p->getOrderedTypes().size(); + for (size_t ti = 0; ti < typeCount; ti++) { + sp<Type> t = p->getOrderedTypes().itemAt(ti); + if (t == NULL) { + continue; + } + + const size_t configCount = t->getOrderedConfigs().size(); + for (size_t ci = 0; ci < configCount; ci++) { + sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci); + if (c == NULL) { + continue; + } + + Vector<key_value_pair_t<ConfigDescription, sp<Entry> > > entriesToAdd; + const DefaultKeyedVector<ConfigDescription, sp<Entry> >& entries = + c->getEntries(); + const size_t entryCount = entries.size(); + for (size_t ei = 0; ei < entryCount; ei++) { + sp<Entry> e = entries.valueAt(ei); + if (e == NULL || e->getType() != Entry::TYPE_BAG) { + continue; + } + + const ConfigDescription& config = entries.keyAt(ei); + if (config.sdkVersion >= SDK_L) { + // We don't need to do anything if the resource is + // already qualified for version 21 or higher. + continue; + } + + Vector<String16> attributesToRemove; + const KeyedVector<String16, Item>& 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)); + } + } + + if (attributesToRemove.isEmpty()) { + continue; + } + + // Duplicate the entry under the same configuration + // but with sdkVersion == SDK_L. + ConfigDescription newConfig(config); + newConfig.sdkVersion = SDK_L; + entriesToAdd.add(key_value_pair_t<ConfigDescription, sp<Entry> >( + newConfig, new Entry(*e))); + + // Remove the attribute from the original. + for (size_t i = 0; i < attributesToRemove.size(); i++) { + e->removeFromBag(attributesToRemove[i]); + } + } + + const size_t entriesToAddCount = entriesToAdd.size(); + for (size_t i = 0; i < entriesToAddCount; i++) { + if (entries.indexOfKey(entriesToAdd[i].key) >= 0) { + // An entry already exists for this config. + // That means that any attributes that were + // defined in L in the original bag will be overriden + // anyways on L devices, so we do nothing. + continue; + } + + entriesToAdd[i].value->getPos() + .printf("using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.", + SDK_L, + String8(p->getName()).string(), + String8(t->getName()).string(), + String8(entriesToAdd[i].value->getName()).string(), + entriesToAdd[i].key.toString().string()); + + c->addEntry(entriesToAdd[i].key, entriesToAdd[i].value); + } + } + } + } + return NO_ERROR; +} diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index 3721de4..025a868 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -165,6 +165,8 @@ public: size_t numLocalResources() const; bool hasResources() const; + status_t modifyForCompat(const Bundle* bundle); + sp<AaptFile> flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, const bool isBase); @@ -281,6 +283,9 @@ public: : mName(name), mType(TYPE_UNKNOWN), mItemFormat(ResTable_map::TYPE_ANY), mNameIndex(-1), mPos(pos) { } + + Entry(const Entry& entry); + virtual ~Entry() { } enum type { @@ -311,6 +316,8 @@ public: bool replace=false, bool isId = false, int32_t format = ResTable_map::TYPE_ANY); + status_t removeFromBag(const String16& key); + // Index of the entry's name string in the key pool. int32_t getNameIndex() const { return mNameIndex; } void setNameIndex(int32_t index) { mNameIndex = index; } @@ -523,6 +530,7 @@ private: 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); String16 mAssetsPackage; |