am 36b5795f: am 81745c51: Merge "AAPT: Continuation of public/private attribute fix" into lmp-dev

* commit '36b5795fc9e9a2eed320a0d626bce44a8c231f6b':
  AAPT: Continuation of public/private attribute fix
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 3fc9f81..d809c5b 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -104,6 +104,9 @@
 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 137c85c..56d1650 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -1483,7 +1483,7 @@
     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 @@
     // 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 91b6554..a0a94f8 100644
--- a/tools/aapt/Images.h
+++ b/tools/aapt/Images.h
@@ -20,7 +20,7 @@
 
 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 f24a023b..089dde5 100644
--- a/tools/aapt/Main.h
+++ b/tools/aapt/Main.h
@@ -62,4 +62,7 @@
 
 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 1e6ba74..8c02e6f 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -50,7 +50,7 @@
 // ==========================================================================
 // ==========================================================================
 
-static String8 parseResourceName(const String8& leaf)
+String8 parseResourceName(const String8& leaf)
 {
     const char* firstDot = strchr(leaf.string(), '.');
     const char* str = leaf.string();
@@ -1088,7 +1088,7 @@
     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;
     }
@@ -1336,7 +1336,8 @@
         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);
@@ -1355,7 +1356,8 @@
     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;
             }
@@ -1370,7 +1372,8 @@
     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;
             }
@@ -1385,7 +1388,8 @@
     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;
             }
@@ -1400,7 +1404,8 @@
     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;
             }
@@ -1415,7 +1420,8 @@
     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;
             }
@@ -1430,7 +1436,7 @@
     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;
             }
@@ -1445,7 +1451,8 @@
     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;
             }
@@ -1461,7 +1468,8 @@
         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);
@@ -1477,6 +1485,22 @@
         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;
     }
@@ -1509,7 +1533,7 @@
     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;
     }
@@ -1599,7 +1623,7 @@
     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 f66ed08..95d9697 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -17,7 +17,9 @@
 
 #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)
@@ -27,10 +29,12 @@
         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,
@@ -41,10 +45,12 @@
         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,
@@ -77,6 +83,10 @@
     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());
@@ -4028,6 +4038,39 @@
     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
 {
@@ -4157,6 +4200,22 @@
         (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.
@@ -4200,19 +4259,10 @@
  * attribute will be respected.
  */
 status_t ResourceTable::modifyForCompat(const Bundle* bundle) {
-    if (bundle->getMinSdkVersion() != NULL) {
+    if (isMinSdkVersionLOrAbove(bundle)) {
         // 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;
-        }
+        return NO_ERROR;
     }
 
     const String16 attr16("attr");
@@ -4309,3 +4359,104 @@
     }
     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 025a868..c548a85 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 @@
             | 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 @@
     }
 };
 
+// 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 @@
         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,
@@ -166,6 +193,10 @@
     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);
@@ -527,6 +558,9 @@
                        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);
@@ -545,6 +579,7 @@
     
     // 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 03c66c1..899fb63 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -621,6 +621,12 @@
     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 @@
     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 @@
     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(&copy->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 ccbf9f4..3161f65 100644
--- a/tools/aapt/XMLNode.h
+++ b/tools/aapt/XMLNode.h
@@ -116,6 +116,8 @@
     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 @@
     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 @@
     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);