Merge "Fix backwards compat problem with AAPT public attrs" into lmp-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
b6812b64c4
@@ -1521,6 +1521,8 @@ public:
|
||||
|
||||
bool getResourceName(uint32_t resID, bool allowUtf8, resource_name* outName) const;
|
||||
|
||||
bool getResourceFlags(uint32_t resID, uint32_t* outFlags) const;
|
||||
|
||||
/**
|
||||
* Retrieve the value of a resource. If the resource is found, returns a
|
||||
* value >= 0 indicating the table it is in (for use with
|
||||
|
||||
@@ -5393,6 +5393,44 @@ const char16_t* StringPoolRef::string16(size_t* outLen) const {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool ResTable::getResourceFlags(uint32_t resID, uint32_t* outFlags) const {
|
||||
if (mError != NO_ERROR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ssize_t p = getResourcePackageIndex(resID);
|
||||
const int t = Res_GETTYPE(resID);
|
||||
const int e = Res_GETENTRY(resID);
|
||||
|
||||
if (p < 0) {
|
||||
if (Res_GETPACKAGE(resID)+1 == 0) {
|
||||
ALOGW("No package identifier when getting flags for resource number 0x%08x", resID);
|
||||
} else {
|
||||
ALOGW("No known package when getting flags for resource number 0x%08x", resID);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (t < 0) {
|
||||
ALOGW("No type identifier when getting flags for resource number 0x%08x", resID);
|
||||
return false;
|
||||
}
|
||||
|
||||
const PackageGroup* const grp = mPackageGroups[p];
|
||||
if (grp == NULL) {
|
||||
ALOGW("Bad identifier when getting flags for resource number 0x%08x", resID);
|
||||
return false;
|
||||
}
|
||||
|
||||
Entry entry;
|
||||
status_t err = getEntry(grp, t, e, NULL, &entry);
|
||||
if (err != NO_ERROR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*outFlags = entry.specFlags;
|
||||
return true;
|
||||
}
|
||||
|
||||
status_t ResTable::getEntry(
|
||||
const PackageGroup* packageGroup, int typeIndex, int entryIndex,
|
||||
const ResTable_config* config,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
aapt package -M AndroidManifest.xml -S res --split fr,de -F bundle.apk -f && \
|
||||
PATH_TO_FRAMEWORK_RES=$(gettop)/prebuilts/sdk/current/android.jar
|
||||
|
||||
aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES --split fr,de -F bundle.apk -f && \
|
||||
unzip bundle.apk resources.arsc && \
|
||||
mv resources.arsc basic.arsc && \
|
||||
xxd -i basic.arsc > basic_arsc.h && \
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user