diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/aapt/AaptAssets.h | 3 | ||||
| -rw-r--r-- | tools/aapt/Images.cpp | 5 | ||||
| -rw-r--r-- | tools/aapt/Images.h | 2 | ||||
| -rw-r--r-- | tools/aapt/Main.h | 3 | ||||
| -rw-r--r-- | tools/aapt/Resource.cpp | 74 | ||||
| -rw-r--r-- | tools/aapt/ResourceTable.cpp | 357 | ||||
| -rw-r--r-- | tools/aapt/ResourceTable.h | 51 | ||||
| -rw-r--r-- | tools/aapt/XMLNode.cpp | 56 | ||||
| -rw-r--r-- | tools/aapt/XMLNode.h | 7 |
9 files changed, 530 insertions, 28 deletions
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h index 3fc9f815c1a9..d809c5b3003c 100644 --- a/tools/aapt/AaptAssets.h +++ b/tools/aapt/AaptAssets.h @@ -104,6 +104,9 @@ private: struct AaptGroupEntry { public: + AaptGroupEntry() {} + AaptGroupEntry(const ConfigDescription& config) : mParams(config) {} + bool initFromDirName(const char* dir, String8* resType); inline const ConfigDescription& toParams() const { return mParams; } diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp index 137c85ce6ef2..56d1650c3652 100644 --- a/tools/aapt/Images.cpp +++ b/tools/aapt/Images.cpp @@ -1483,7 +1483,7 @@ status_t preProcessImageToCache(const Bundle* bundle, const String8& source, con return NO_ERROR; } -status_t postProcessImage(const sp<AaptAssets>& assets, +status_t postProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets, ResourceTable* table, const sp<AaptFile>& file) { String8 ext(file->getPath().getPathExtension()); @@ -1491,7 +1491,8 @@ status_t postProcessImage(const sp<AaptAssets>& assets, // At this point, now that we have all the resource data, all we need to // do is compile XML files. if (strcmp(ext.string(), ".xml") == 0) { - return compileXmlFile(assets, file, table); + String16 resourceName(parseResourceName(file->getPath().getPathLeaf())); + return compileXmlFile(bundle, assets, resourceName, file, table); } return NO_ERROR; diff --git a/tools/aapt/Images.h b/tools/aapt/Images.h index 91b6554c02c9..a0a94f8b9d16 100644 --- a/tools/aapt/Images.h +++ b/tools/aapt/Images.h @@ -20,7 +20,7 @@ status_t preProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets, status_t preProcessImageToCache(const Bundle* bundle, const String8& source, const String8& dest); -status_t postProcessImage(const sp<AaptAssets>& assets, +status_t postProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets, ResourceTable* table, const sp<AaptFile>& file); #endif diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h index f24a023b70a1..089dde5fcb8c 100644 --- a/tools/aapt/Main.h +++ b/tools/aapt/Main.h @@ -62,4 +62,7 @@ int dumpResources(Bundle* bundle); status_t writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw); + +android::String8 parseResourceName(const String8& pathLeaf); + #endif // __MAIN_H diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index afec5edd3f0f..a4c9dabb6be9 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -50,7 +50,7 @@ public: // ========================================================================== // ========================================================================== -static String8 parseResourceName(const String8& leaf) +String8 parseResourceName(const String8& leaf) { const char* firstDot = strchr(leaf.string(), '.'); const char* str = leaf.string(); @@ -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; @@ -1074,7 +1088,7 @@ status_t generateAndroidManifestForSplit(Bundle* bundle, const sp<AaptAssets>& a manifest->addChild(app); root->addChild(manifest); - int err = compileXmlFile(assets, root, outFile, table); + int err = compileXmlFile(bundle, assets, String16(), root, outFile, table); if (err < NO_ERROR) { return err; } @@ -1322,7 +1336,8 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil ResourceDirIterator it(layouts, String8("layout")); while ((err=it.next()) == NO_ERROR) { String8 src = it.getFile()->getPrintableSource(); - err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); + err = compileXmlFile(bundle, assets, String16(it.getBaseName()), + it.getFile(), &table, xmlFlags); if (err == NO_ERROR) { ResXMLTree block; block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); @@ -1341,7 +1356,8 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil if (anims != NULL) { ResourceDirIterator it(anims, String8("anim")); while ((err=it.next()) == NO_ERROR) { - err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); + err = compileXmlFile(bundle, assets, String16(it.getBaseName()), + it.getFile(), &table, xmlFlags); if (err != NO_ERROR) { hasErrors = true; } @@ -1356,7 +1372,8 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil if (animators != NULL) { ResourceDirIterator it(animators, String8("animator")); while ((err=it.next()) == NO_ERROR) { - err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); + err = compileXmlFile(bundle, assets, String16(it.getBaseName()), + it.getFile(), &table, xmlFlags); if (err != NO_ERROR) { hasErrors = true; } @@ -1371,7 +1388,8 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil if (interpolators != NULL) { ResourceDirIterator it(interpolators, String8("interpolator")); while ((err=it.next()) == NO_ERROR) { - err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); + err = compileXmlFile(bundle, assets, String16(it.getBaseName()), + it.getFile(), &table, xmlFlags); if (err != NO_ERROR) { hasErrors = true; } @@ -1386,7 +1404,8 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil if (transitions != NULL) { ResourceDirIterator it(transitions, String8("transition")); while ((err=it.next()) == NO_ERROR) { - err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); + err = compileXmlFile(bundle, assets, String16(it.getBaseName()), + it.getFile(), &table, xmlFlags); if (err != NO_ERROR) { hasErrors = true; } @@ -1401,7 +1420,8 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil if (xmls != NULL) { ResourceDirIterator it(xmls, String8("xml")); while ((err=it.next()) == NO_ERROR) { - err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); + err = compileXmlFile(bundle, assets, String16(it.getBaseName()), + it.getFile(), &table, xmlFlags); if (err != NO_ERROR) { hasErrors = true; } @@ -1416,7 +1436,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil if (drawables != NULL) { ResourceDirIterator it(drawables, String8("drawable")); while ((err=it.next()) == NO_ERROR) { - err = postProcessImage(assets, &table, it.getFile()); + err = postProcessImage(bundle, assets, &table, it.getFile()); if (err != NO_ERROR) { hasErrors = true; } @@ -1431,7 +1451,8 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil if (colors != NULL) { ResourceDirIterator it(colors, String8("color")); while ((err=it.next()) == NO_ERROR) { - err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); + err = compileXmlFile(bundle, assets, String16(it.getBaseName()), + it.getFile(), &table, xmlFlags); if (err != NO_ERROR) { hasErrors = true; } @@ -1447,7 +1468,8 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil ResourceDirIterator it(menus, String8("menu")); while ((err=it.next()) == NO_ERROR) { String8 src = it.getFile()->getPrintableSource(); - err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); + err = compileXmlFile(bundle, assets, String16(it.getBaseName()), + it.getFile(), &table, xmlFlags); if (err == NO_ERROR) { ResXMLTree block; block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); @@ -1463,6 +1485,22 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil err = NO_ERROR; } + // Now compile any generated resources. + std::queue<CompileResourceWorkItem>& workQueue = table.getWorkQueue(); + while (!workQueue.empty()) { + CompileResourceWorkItem& workItem = workQueue.front(); + err = compileXmlFile(bundle, assets, workItem.resourceName, workItem.file, &table, xmlFlags); + if (err == NO_ERROR) { + assets->addResource(workItem.resPath.getPathLeaf(), + workItem.resPath, + workItem.file, + workItem.file->getResourceType()); + } else { + hasErrors = true; + } + workQueue.pop(); + } + if (table.validateLocalizations()) { hasErrors = true; } @@ -1495,11 +1533,15 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil if (err < NO_ERROR) { return err; } - err = compileXmlFile(assets, manifestTree, manifestFile, &table); + err = compileXmlFile(bundle, assets, String16(), manifestTree, manifestFile, &table); if (err < NO_ERROR) { return err; } + if (table.modifyForCompat(bundle) != NO_ERROR) { + return UNKNOWN_ERROR; + } + //block.restart(); //printXMLBlock(&block); @@ -1581,7 +1623,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil sp<AaptFile> outManifestFile = new AaptFile(manifestFile->getSourceFile(), manifestFile->getGroupEntry(), manifestFile->getResourceType()); - err = compileXmlFile(assets, manifestFile, + err = compileXmlFile(bundle, assets, String16(), manifestFile, outManifestFile, &table, XML_COMPILE_ASSIGN_ATTRIBUTE_IDS | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES); diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 8341de6c4ba5..b8c34543c361 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -12,11 +12,14 @@ #include <androidfw/ResourceTypes.h> #include <utils/ByteOrder.h> +#include <utils/TypeHelpers.h> #include <stdarg.h> #define NOISY(x) //x -status_t compileXmlFile(const sp<AaptAssets>& assets, +status_t compileXmlFile(const Bundle* bundle, + const sp<AaptAssets>& assets, + const String16& resourceName, const sp<AaptFile>& target, ResourceTable* table, int options) @@ -26,10 +29,12 @@ status_t compileXmlFile(const sp<AaptAssets>& assets, return UNKNOWN_ERROR; } - return compileXmlFile(assets, root, target, table, options); + return compileXmlFile(bundle, assets, resourceName, root, target, table, options); } -status_t compileXmlFile(const sp<AaptAssets>& assets, +status_t compileXmlFile(const Bundle* bundle, + const sp<AaptAssets>& assets, + const String16& resourceName, const sp<AaptFile>& target, const sp<AaptFile>& outTarget, ResourceTable* table, @@ -40,10 +45,12 @@ status_t compileXmlFile(const sp<AaptAssets>& assets, return UNKNOWN_ERROR; } - return compileXmlFile(assets, root, outTarget, table, options); + return compileXmlFile(bundle, assets, resourceName, root, outTarget, table, options); } -status_t compileXmlFile(const sp<AaptAssets>& assets, +status_t compileXmlFile(const Bundle* bundle, + const sp<AaptAssets>& assets, + const String16& resourceName, const sp<XMLNode>& root, const sp<AaptFile>& target, ResourceTable* table, @@ -76,6 +83,10 @@ status_t compileXmlFile(const sp<AaptAssets>& assets, if (hasErrors) { return UNKNOWN_ERROR; } + + if (table->modifyForCompat(bundle, resourceName, target, root) != NO_ERROR) { + return UNKNOWN_ERROR; + } NOISY(printf("Input XML Resource:\n")); NOISY(root->print()); @@ -3287,6 +3298,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 +3395,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); @@ -4004,6 +4038,39 @@ sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package, return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay()); } +sp<ResourceTable::ConfigList> ResourceTable::getConfigList(const String16& package, + const String16& type, const String16& name) const +{ + const size_t packageCount = mOrderedPackages.size(); + for (size_t pi = 0; pi < packageCount; pi++) { + const sp<Package>& p = mOrderedPackages[pi]; + if (p == NULL || p->getName() != package) { + continue; + } + + const Vector<sp<Type> >& types = p->getOrderedTypes(); + const size_t typeCount = types.size(); + for (size_t ti = 0; ti < typeCount; ti++) { + const sp<Type>& t = types[ti]; + if (t == NULL || t->getName() != type) { + continue; + } + + const Vector<sp<ConfigList> >& configs = t->getOrderedConfigs(); + const size_t configCount = configs.size(); + for (size_t ci = 0; ci < configCount; ci++) { + const sp<ConfigList>& cl = configs[ci]; + if (cl == NULL || cl->getName() != name) { + continue; + } + + return cl; + } + } + } + return NULL; +} + sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID, const ResTable_config* config) const { @@ -4113,3 +4180,283 @@ 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); +} + +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_L) { + return true; + } + } + return false; +} + +/** + * 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 (isMinSdkVersionLOrAbove(bundle)) { + // If this app will only ever run on L+ devices, + // we don't need to do any compatibility work. + 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; +} + +status_t ResourceTable::modifyForCompat(const Bundle* bundle, + const String16& resourceName, + const sp<AaptFile>& target, + const sp<XMLNode>& root) { + if (isMinSdkVersionLOrAbove(bundle)) { + return NO_ERROR; + } + + if (target->getResourceType() == "" || target->getGroupEntry().toParams().sdkVersion >= SDK_L) { + // Skip resources that have no type (AndroidManifest.xml) or are already version qualified with v21 + // or higher. + return NO_ERROR; + } + + Vector<key_value_pair_t<sp<XMLNode>, size_t> > attrsToRemove; + + Vector<sp<XMLNode> > nodesToVisit; + nodesToVisit.push(root); + while (!nodesToVisit.isEmpty()) { + sp<XMLNode> node = nodesToVisit.top(); + nodesToVisit.pop(); + + const Vector<XMLNode::attribute_entry>& attrs = node->getAttributes(); + const size_t attrCount = attrs.size(); + for (size_t i = 0; i < attrCount; i++) { + const XMLNode::attribute_entry& attr = attrs[i]; + if (isAttributeFromL(attr.nameResId)) { + attrsToRemove.add(key_value_pair_t<sp<XMLNode>, size_t>(node, i)); + } + } + + // Schedule a visit to the children. + const Vector<sp<XMLNode> >& children = node->getChildren(); + const size_t childCount = children.size(); + for (size_t i = 0; i < childCount; i++) { + nodesToVisit.push(children[i]); + } + } + + if (attrsToRemove.isEmpty()) { + return NO_ERROR; + } + + ConfigDescription newConfig(target->getGroupEntry().toParams()); + newConfig.sdkVersion = SDK_L; + + // Look to see if we already have an overriding v21 configuration. + sp<ConfigList> cl = getConfigList(String16(mAssets->getPackage()), + String16(target->getResourceType()), resourceName); + if (cl->getEntries().indexOfKey(newConfig) < 0) { + // We don't have an overriding entry for v21, so we must duplicate this one. + sp<XMLNode> newRoot = root->clone(); + sp<AaptFile> newFile = new AaptFile(target->getSourceFile(), + AaptGroupEntry(newConfig), target->getResourceType()); + String8 resPath = String8::format("res/%s/%s", + newFile->getGroupEntry().toDirName(target->getResourceType()).string(), + target->getPath().getPathLeaf().string()); + resPath.convertToResPath(); + + // Add a resource table entry. + SourcePos(target->getSourceFile(), -1).printf( + "using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.", + SDK_L, + mAssets->getPackage().string(), + newFile->getResourceType().string(), + String8(resourceName).string(), + newConfig.toString().string()); + + addEntry(SourcePos(), + String16(mAssets->getPackage()), + String16(target->getResourceType()), + resourceName, + String16(resPath), + NULL, + &newConfig); + + // Schedule this to be compiled. + CompileResourceWorkItem item; + item.resourceName = resourceName; + item.resPath = resPath; + item.file = newFile; + mWorkQueue.push(item); + } + + const size_t removeCount = attrsToRemove.size(); + for (size_t i = 0; i < removeCount; i++) { + sp<XMLNode> node = attrsToRemove[i].key; + size_t attrIndex = attrsToRemove[i].value; + const XMLNode::attribute_entry& ae = node->getAttributes()[attrIndex]; + SourcePos(node->getFilename(), node->getStartLineNumber()).printf( + "removing attribute %s%s%s from <%s>", + String8(ae.ns).string(), + (ae.ns.size() == 0 ? "" : ":"), + String8(ae.name).string(), + String8(node->getElementName()).string()); + node->removeAttribute(attrIndex); + } + + return NO_ERROR; +} diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index 3721de4af015..c548a851fb2e 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -12,8 +12,9 @@ #include "SourcePos.h" #include "ResourceFilter.h" -#include <set> #include <map> +#include <queue> +#include <set> using namespace std; @@ -33,18 +34,24 @@ enum { | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES }; -status_t compileXmlFile(const sp<AaptAssets>& assets, +status_t compileXmlFile(const Bundle* bundle, + const sp<AaptAssets>& assets, + const String16& resourceName, const sp<AaptFile>& target, ResourceTable* table, int options = XML_COMPILE_STANDARD_RESOURCE); -status_t compileXmlFile(const sp<AaptAssets>& assets, +status_t compileXmlFile(const Bundle* bundle, + const sp<AaptAssets>& assets, + const String16& resourceName, const sp<AaptFile>& target, const sp<AaptFile>& outTarget, ResourceTable* table, int options = XML_COMPILE_STANDARD_RESOURCE); -status_t compileXmlFile(const sp<AaptAssets>& assets, +status_t compileXmlFile(const Bundle* bundle, + const sp<AaptAssets>& assets, + const String16& resourceName, const sp<XMLNode>& xmlTree, const sp<AaptFile>& target, ResourceTable* table, @@ -71,6 +78,14 @@ struct AccessorCookie } }; +// Holds the necessary information to compile the +// resource. +struct CompileResourceWorkItem { + String16 resourceName; + String8 resPath; + sp<AaptFile> file; +}; + class ResourceTable : public ResTable::Accessor { public: @@ -92,6 +107,18 @@ public: return mAssetsPackage; } + /** + * Returns the queue of resources that need to be compiled. + * This is only used for resources that have been generated + * during the compilation phase. If they were just added + * to the AaptAssets, then they may be skipped over + * and would mess up iteration order for the existing + * resources. + */ + queue<CompileResourceWorkItem>& getWorkQueue() { + return mWorkQueue; + } + status_t addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets); status_t addPublic(const SourcePos& pos, @@ -165,6 +192,12 @@ public: size_t numLocalResources() const; bool hasResources() const; + status_t modifyForCompat(const Bundle* bundle); + status_t modifyForCompat(const Bundle* bundle, + const String16& resourceName, + const sp<AaptFile>& file, + const sp<XMLNode>& root); + sp<AaptFile> flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, const bool isBase); @@ -281,6 +314,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 +347,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; } @@ -520,9 +558,13 @@ private: bool doSetIndex = false); sp<const Entry> getEntry(uint32_t resID, const ResTable_config* config = NULL) const; + sp<ConfigList> getConfigList(const String16& package, + const String16& type, + const String16& name) const; 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; @@ -537,6 +579,7 @@ private: // key = string resource name, value = set of locales in which that name is defined map<String16, map<String8, SourcePos> > mLocalizations; + queue<CompileResourceWorkItem> mWorkQueue; }; #endif diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp index 607d419b2971..51a4154d5335 100644 --- a/tools/aapt/XMLNode.cpp +++ b/tools/aapt/XMLNode.cpp @@ -621,6 +621,12 @@ sp<XMLNode> XMLNode::parse(const sp<AaptFile>& file) return state.root; } +XMLNode::XMLNode() + : mNextAttributeIndex(0x80000000) + , mStartLineNumber(0) + , mEndLineNumber(0) + , mUTF8(false) {} + XMLNode::XMLNode(const String8& filename, const String16& s1, const String16& s2, bool isNamespace) : mNextAttributeIndex(0x80000000) , mFilename(filename) @@ -810,6 +816,32 @@ status_t XMLNode::addAttribute(const String16& ns, const String16& name, return NO_ERROR; } +status_t XMLNode::removeAttribute(size_t index) +{ + if (getType() == TYPE_CDATA) { + return UNKNOWN_ERROR; + } + + if (index >= mAttributes.size()) { + return UNKNOWN_ERROR; + } + + const attribute_entry& e = mAttributes[index]; + const uint32_t key = e.nameResId ? e.nameResId : e.index; + mAttributeOrder.removeItem(key); + mAttributes.removeAt(index); + + // Shift all the indices. + const size_t attrCount = mAttributeOrder.size(); + for (size_t i = 0; i < attrCount; i++) { + size_t attrIdx = mAttributeOrder[i]; + if (attrIdx > index) { + mAttributeOrder.replaceValueAt(i, attrIdx - 1); + } + } + return NO_ERROR; +} + void XMLNode::setAttributeResID(size_t attrIdx, uint32_t resId) { attribute_entry& e = mAttributes.editItemAt(attrIdx); @@ -999,6 +1031,30 @@ status_t XMLNode::assignResourceIds(const sp<AaptAssets>& assets, return hasErrors ? UNKNOWN_ERROR : NO_ERROR; } +sp<XMLNode> XMLNode::clone() const { + sp<XMLNode> copy = new XMLNode(); + copy->mNamespacePrefix = mNamespacePrefix; + copy->mNamespaceUri = mNamespaceUri; + copy->mElementName = mElementName; + + const size_t childCount = mChildren.size(); + for (size_t i = 0; i < childCount; i++) { + copy->mChildren.add(mChildren[i]->clone()); + } + + copy->mAttributes = mAttributes; + copy->mAttributeOrder = mAttributeOrder; + copy->mNextAttributeIndex = mNextAttributeIndex; + copy->mChars = mChars; + memcpy(©->mCharsValue, &mCharsValue, sizeof(mCharsValue)); + copy->mComment = mComment; + copy->mFilename = mFilename; + copy->mStartLineNumber = mStartLineNumber; + copy->mEndLineNumber = mEndLineNumber; + copy->mUTF8 = mUTF8; + return copy; +} + status_t XMLNode::flatten(const sp<AaptFile>& dest, bool stripComments, bool stripRawValues) const { diff --git a/tools/aapt/XMLNode.h b/tools/aapt/XMLNode.h index ccbf9f403116..3161f6500291 100644 --- a/tools/aapt/XMLNode.h +++ b/tools/aapt/XMLNode.h @@ -116,6 +116,8 @@ public: status_t addAttribute(const String16& ns, const String16& name, const String16& value); + status_t removeAttribute(size_t index); + void setAttributeResID(size_t attrIdx, uint32_t resId); status_t appendChars(const String16& chars); @@ -137,6 +139,8 @@ public: status_t flatten(const sp<AaptFile>& dest, bool stripComments, bool stripRawValues) const; + sp<XMLNode> clone() const; + void print(int indent=0); private: @@ -163,6 +167,9 @@ private: static void XMLCALL commentData(void *userData, const char *comment); + // For cloning + XMLNode(); + // Creating an element node. XMLNode(const String8& filename, const String16& s1, const String16& s2, bool isNamespace); |