]> git.saurik.com Git - android/aapt.git/blobdiff - Resource.cpp
Implement issue #3116702: New manifest tags for supported screen sizes DO NOT MERGE
[android/aapt.git] / Resource.cpp
index 49ccf718519b6d9becb0c5129ecf4d9ebb39e9e5..c8ba9048f8670a0dcd723c14e7567d0341939883 100644 (file)
@@ -45,13 +45,6 @@ static String8 parseResourceName(const String8& leaf)
     }
 }
 
-class ResourceTypeSet : public RefBase,
-                        public KeyedVector<String8,sp<AaptGroup> >
-{
-public:
-    ResourceTypeSet();
-};
-
 ResourceTypeSet::ResourceTypeSet()
     :RefBase(),
      KeyedVector<String8,sp<AaptGroup> >()
@@ -108,13 +101,13 @@ public:
             String8 leaf(group->getLeaf());
             mLeafName = String8(leaf);
             mParams = file->getGroupEntry().toParams();
-            NOISY(printf("Dir %s: mcc=%d mnc=%d lang=%c%c cnt=%c%c orient=%d density=%d touch=%d key=%d inp=%d nav=%d\n",
+            NOISY(printf("Dir %s: mcc=%d mnc=%d lang=%c%c cnt=%c%c orient=%d ui=%d density=%d touch=%d key=%d inp=%d nav=%d\n",
                    group->getPath().string(), mParams.mcc, mParams.mnc,
                    mParams.language[0] ? mParams.language[0] : '-',
                    mParams.language[1] ? mParams.language[1] : '-',
                    mParams.country[0] ? mParams.country[0] : '-',
                    mParams.country[1] ? mParams.country[1] : '-',
-                   mParams.orientation,
+                   mParams.orientation, mParams.uiMode,
                    mParams.density, mParams.touchscreen, mParams.keyboard,
                    mParams.inputFlags, mParams.navigation));
             mPath = "res";
@@ -178,10 +171,11 @@ static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNec
                             NULL, String8());
 }
 
-static status_t parsePackage(const sp<AaptAssets>& assets, const sp<AaptGroup>& grp)
+static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets,
+    const sp<AaptGroup>& grp)
 {
     if (grp->getFiles().size() != 1) {
-        fprintf(stderr, "WARNING: Multiple AndroidManifest.xml files found, using %s\n",
+        fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
                 grp->getFiles().valueAt(0)->getPrintableSource().string());
     }
 
@@ -222,6 +216,22 @@ static status_t parsePackage(const sp<AaptAssets>& assets, const sp<AaptGroup>&
 
     assets->setPackage(String8(block.getAttributeStringValue(nameIndex, &len)));
 
+    String16 uses_sdk16("uses-sdk");
+    while ((code=block.next()) != ResXMLTree::END_DOCUMENT
+           && code != ResXMLTree::BAD_DOCUMENT) {
+        if (code == ResXMLTree::START_TAG) {
+            if (strcmp16(block.getElementName(&len), uses_sdk16.string()) == 0) {
+                ssize_t minSdkIndex = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
+                                                             "minSdkVersion");
+                if (minSdkIndex >= 0) {
+                    const uint16_t* minSdk16 = block.getAttributeStringValue(minSdkIndex, &len);
+                    const char* minSdk8 = strdup(String8(minSdk16).string());
+                    bundle->setManifestMinSdkVersion(minSdk8);
+                }
+            }
+        }
+    }
+
     return NO_ERROR;
 }
 
@@ -279,15 +289,16 @@ static status_t preProcessImages(Bundle* bundle, const sp<AaptAssets>& assets,
     ResourceDirIterator it(set, String8("drawable"));
     Vector<sp<AaptFile> > newNameFiles;
     Vector<String8> newNamePaths;
+    bool hasErrors = false;
     ssize_t res;
     while ((res=it.next()) == NO_ERROR) {
         res = preProcessImage(bundle, assets, it.getFile(), NULL);
-        if (res != NO_ERROR) {
-            return res;
+        if (res < NO_ERROR) {
+            hasErrors = true;
         }
     }
 
-    return NO_ERROR;
+    return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
 }
 
 status_t postProcessImages(const sp<AaptAssets>& assets,
@@ -295,15 +306,16 @@ status_t postProcessImages(const sp<AaptAssets>& assets,
                            const sp<ResourceTypeSet>& set)
 {
     ResourceDirIterator it(set, String8("drawable"));
+    bool hasErrors = false;
     ssize_t res;
     while ((res=it.next()) == NO_ERROR) {
         res = postProcessImage(assets, table, it.getFile());
-        if (res != NO_ERROR) {
-            return res;
+        if (res < NO_ERROR) {
+            hasErrors = true;
         }
     }
 
-    return res < NO_ERROR ? res : (status_t)NO_ERROR;
+    return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
 }
 
 static void collect_files(const sp<AaptDir>& dir,
@@ -367,14 +379,64 @@ enum {
     ATTR_LEADING_SPACES = -3,
     ATTR_TRAILING_SPACES = -4
 };
-static int validateAttr(const String8& path, const ResXMLParser& parser,
+static int validateAttr(const String8& path, const ResTable& table,
+        const ResXMLParser& parser,
         const char* ns, const char* attr, const char* validChars, bool required)
 {
     size_t len;
 
     ssize_t index = parser.indexOfAttribute(ns, attr);
     const uint16_t* str;
-    if (index >= 0 && (str=parser.getAttributeStringValue(index, &len)) != NULL) {
+    Res_value value;
+    if (index >= 0 && parser.getAttributeValue(index, &value) >= 0) {
+        const ResStringPool* pool = &parser.getStrings();
+        if (value.dataType == Res_value::TYPE_REFERENCE) {
+            uint32_t specFlags = 0;
+            int strIdx;
+            if ((strIdx=table.resolveReference(&value, 0x10000000, NULL, &specFlags)) < 0) {
+                fprintf(stderr, "%s:%d: Tag <%s> attribute %s references unknown resid 0x%08x.\n",
+                        path.string(), parser.getLineNumber(),
+                        String8(parser.getElementName(&len)).string(), attr,
+                        value.data);
+                return ATTR_NOT_FOUND;
+            }
+            
+            pool = table.getTableStringBlock(strIdx);
+            #if 0
+            if (pool != NULL) {
+                str = pool->stringAt(value.data, &len);
+            }
+            printf("***** RES ATTR: %s specFlags=0x%x strIdx=%d: %s\n", attr,
+                    specFlags, strIdx, str != NULL ? String8(str).string() : "???");
+            #endif
+            if ((specFlags&~ResTable_typeSpec::SPEC_PUBLIC) != 0 && false) {
+                fprintf(stderr, "%s:%d: Tag <%s> attribute %s varies by configurations 0x%x.\n",
+                        path.string(), parser.getLineNumber(),
+                        String8(parser.getElementName(&len)).string(), attr,
+                        specFlags);
+                return ATTR_NOT_FOUND;
+            }
+        }
+        if (value.dataType == Res_value::TYPE_STRING) {
+            if (pool == NULL) {
+                fprintf(stderr, "%s:%d: Tag <%s> attribute %s has no string block.\n",
+                        path.string(), parser.getLineNumber(),
+                        String8(parser.getElementName(&len)).string(), attr);
+                return ATTR_NOT_FOUND;
+            }
+            if ((str=pool->stringAt(value.data, &len)) == NULL) {
+                fprintf(stderr, "%s:%d: Tag <%s> attribute %s has corrupt string value.\n",
+                        path.string(), parser.getLineNumber(),
+                        String8(parser.getElementName(&len)).string(), attr);
+                return ATTR_NOT_FOUND;
+            }
+        } else {
+            fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid type %d.\n",
+                    path.string(), parser.getLineNumber(),
+                    String8(parser.getElementName(&len)).string(), attr,
+                    value.dataType);
+            return ATTR_NOT_FOUND;
+        }
         if (validChars) {
             for (size_t i=0; i<len; i++) {
                 uint16_t c = str[i];
@@ -426,22 +488,27 @@ static void checkForIds(const String8& path, ResXMLParser& parser)
         if (code == ResXMLTree::START_TAG) {
             ssize_t index = parser.indexOfAttribute(NULL, "id");
             if (index >= 0) {
-                fprintf(stderr, "%s:%d: WARNING: found plain 'id' attribute; did you mean the new 'android:id' name?\n",
+                fprintf(stderr, "%s:%d: warning: found plain 'id' attribute; did you mean the new 'android:id' name?\n",
                         path.string(), parser.getLineNumber());
             }
         }
     }
 }
 
-static void applyFileOverlay(const sp<AaptAssets>& assets, 
-                             const sp<ResourceTypeSet>& baseSet,
+static bool applyFileOverlay(Bundle *bundle,
+                             const sp<AaptAssets>& assets,
+                             sp<ResourceTypeSet> *baseSet,
                              const char *resType)
 {
+    if (bundle->getVerbose()) {
+        printf("applyFileOverlay for %s\n", resType);
+    }
+
     // Replace any base level files in this category with any found from the overlay
     // Also add any found only in the overlay.
     sp<AaptAssets> overlay = assets->getOverlay();
     String8 resTypeString(resType);
-    
+
     // work through the linked list of overlays
     while (overlay.get()) {
         KeyedVector<String8, sp<ResourceTypeSet> >* overlayRes = overlay->getResources();
@@ -455,36 +522,74 @@ static void applyFileOverlay(const sp<AaptAssets>& assets,
             // non-overlay "baseset".
             size_t overlayCount = overlaySet->size();
             for (size_t overlayIndex=0; overlayIndex<overlayCount; overlayIndex++) {
-                size_t baseIndex = baseSet->indexOfKey(overlaySet->keyAt(overlayIndex));
-                if (baseIndex != UNKNOWN_ERROR) {
+                if (bundle->getVerbose()) {
+                    printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string());
+                }
+                size_t baseIndex = UNKNOWN_ERROR;
+                if (baseSet->get() != NULL) {
+                    baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex));
+                }
+                if (baseIndex < UNKNOWN_ERROR) {
                     // look for same flavor.  For a given file (strings.xml, for example)
                     // there may be a locale specific or other flavors - we want to match
                     // the same flavor.
                     sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
-                    sp<AaptGroup> baseGroup = baseSet->valueAt(baseIndex);
-                   
-                    DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles = 
-                            baseGroup->getFiles();
-                    DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles = 
+                    sp<AaptGroup> baseGroup = (*baseSet)->valueAt(baseIndex);
+
+                    DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
                             overlayGroup->getFiles();
+                    if (bundle->getVerbose()) {
+                        DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles =
+                                baseGroup->getFiles();
+                        for (size_t i=0; i < baseFiles.size(); i++) {
+                            printf("baseFile %ld has flavor %s\n", i,
+                                    baseFiles.keyAt(i).toString().string());
+                        }
+                        for (size_t i=0; i < overlayFiles.size(); i++) {
+                            printf("overlayFile %ld has flavor %s\n", i,
+                                    overlayFiles.keyAt(i).toString().string());
+                        }
+                    }
+
                     size_t overlayGroupSize = overlayFiles.size();
-                    for (size_t overlayGroupIndex = 0; 
-                            overlayGroupIndex<overlayGroupSize; 
+                    for (size_t overlayGroupIndex = 0;
+                            overlayGroupIndex<overlayGroupSize;
                             overlayGroupIndex++) {
-                        size_t baseFileIndex = 
-                                baseFiles.indexOfKey(overlayFiles.keyAt(overlayGroupIndex));
+                        size_t baseFileIndex =
+                                baseGroup->getFiles().indexOfKey(overlayFiles.
+                                keyAt(overlayGroupIndex));
                         if(baseFileIndex < UNKNOWN_ERROR) {
+                            if (bundle->getVerbose()) {
+                                printf("found a match (%ld) for overlay file %s, for flavor %s\n",
+                                        baseFileIndex,
+                                        overlayGroup->getLeaf().string(),
+                                        overlayFiles.keyAt(overlayGroupIndex).toString().string());
+                            }
                             baseGroup->removeFile(baseFileIndex);
                         } else {
                             // didn't find a match fall through and add it..
                         }
                         baseGroup->addFile(overlayFiles.valueAt(overlayGroupIndex));
+                        assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
                     }
                 } else {
+                    if (baseSet->get() == NULL) {
+                        *baseSet = new ResourceTypeSet();
+                        assets->getResources()->add(String8(resType), *baseSet);
+                    }
                     // this group doesn't exist (a file that's only in the overlay)
-                    // add it
-                    baseSet->add(overlaySet->keyAt(overlayIndex),
-                                 overlaySet->valueAt(overlayIndex));
+                    (*baseSet)->add(overlaySet->keyAt(overlayIndex),
+                            overlaySet->valueAt(overlayIndex));
+                    // make sure all flavors are defined in the resources.
+                    sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
+                    DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
+                            overlayGroup->getFiles();
+                    size_t overlayGroupSize = overlayFiles.size();
+                    for (size_t overlayGroupIndex = 0;
+                            overlayGroupIndex<overlayGroupSize;
+                            overlayGroupIndex++) {
+                        assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
+                    }
                 }
             }
             // this overlay didn't have resources for this type
@@ -492,7 +597,7 @@ static void applyFileOverlay(const sp<AaptAssets>& assets,
         // try next overlay
         overlay = overlay->getOverlay();
     }
-    return;
+    return true;
 }
 
 void addTagAttribute(const sp<XMLNode>& node, const char* ns8,
@@ -506,7 +611,8 @@ void addTagAttribute(const sp<XMLNode>& node, const char* ns8,
     const String16 attr(attr8);
     
     if (node->getAttribute(ns, attr) != NULL) {
-        fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s)\n",
+        fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s);"
+                        " using existing value in manifest.\n",
                 String8(attr).string(), String8(ns).string());
         return;
     }
@@ -514,6 +620,34 @@ void addTagAttribute(const sp<XMLNode>& node, const char* ns8,
     node->addAttribute(ns, attr, String16(value));
 }
 
+static void fullyQualifyClassName(const String8& package, sp<XMLNode> node,
+        const String16& attrName) {
+    XMLNode::attribute_entry* attr = node->editAttribute(
+            String16("http://schemas.android.com/apk/res/android"), attrName);
+    if (attr != NULL) {
+        String8 name(attr->string);
+
+        // asdf     --> package.asdf
+        // .asdf  .a.b  --> package.asdf package.a.b
+        // asdf.adsf --> asdf.asdf
+        String8 className;
+        const char* p = name.string();
+        const char* q = strchr(p, '.');
+        if (p == q) {
+            className += package;
+            className += name;
+        } else if (q == NULL) {
+            className += package;
+            className += ".";
+            className += name;
+        } else {
+            className += name;
+        }
+        NOISY(printf("Qualifying class '%s' to '%s'", name.string(), className.string()));
+        attr->string.setTo(String16(className));
+    }
+}
+
 status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
 {
     root = root->searchElement(String16(), String16("manifest"));
@@ -543,6 +677,64 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
         addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion",
                 bundle->getMaxSdkVersion());
     }
+
+    if (bundle->getDebugMode()) {
+        sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
+        if (application != NULL) {
+            addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true");
+        }
+    }
+
+    // Deal with manifest package name overrides
+    const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride();
+    if (manifestPackageNameOverride != NULL) {
+        // Update the actual package name
+        XMLNode::attribute_entry* attr = root->editAttribute(String16(), String16("package"));
+        if (attr == NULL) {
+            fprintf(stderr, "package name is required with --rename-manifest-package.\n");
+            return UNKNOWN_ERROR;
+        }
+        String8 origPackage(attr->string);
+        attr->string.setTo(String16(manifestPackageNameOverride));
+        NOISY(printf("Overriding package '%s' to be '%s'\n", origPackage.string(), manifestPackageNameOverride));
+
+        // Make class names fully qualified
+        sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
+        if (application != NULL) {
+            fullyQualifyClassName(origPackage, application, String16("name"));
+            fullyQualifyClassName(origPackage, application, String16("backupAgent"));
+
+            Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(application->getChildren());
+            for (size_t i = 0; i < children.size(); i++) {
+                sp<XMLNode> child = children.editItemAt(i);
+                String8 tag(child->getElementName());
+                if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
+                    fullyQualifyClassName(origPackage, child, String16("name"));
+                } else if (tag == "activity-alias") {
+                    fullyQualifyClassName(origPackage, child, String16("name"));
+                    fullyQualifyClassName(origPackage, child, String16("targetActivity"));
+                }
+            }
+        }
+    }
+
+    // Deal with manifest package name overrides
+    const char* instrumentationPackageNameOverride = bundle->getInstrumentationPackageNameOverride();
+    if (instrumentationPackageNameOverride != NULL) {
+        // Fix up instrumentation targets.
+        Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(root->getChildren());
+        for (size_t i = 0; i < children.size(); i++) {
+            sp<XMLNode> child = children.editItemAt(i);
+            String8 tag(child->getElementName());
+            if (tag == "instrumentation") {
+                XMLNode::attribute_entry* attr = child->editAttribute(
+                        String16("http://schemas.android.com/apk/res/android"), String16("targetPackage"));
+                if (attr != NULL) {
+                    attr->string.setTo(String16(instrumentationPackageNameOverride));
+                }
+            }
+        }
+    }
     
     return NO_ERROR;
 }
@@ -566,7 +758,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
         return UNKNOWN_ERROR;
     }
 
-    status_t err = parsePackage(assets, androidManifestFile);
+    status_t err = parsePackage(bundle, assets, androidManifestFile);
     if (err != NO_ERROR) {
         return err;
     }
@@ -582,6 +774,18 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
 
     NOISY(printf("Found %d included resource packages\n", (int)table.size()));
 
+    // Standard flags for compiled XML and optional UTF-8 encoding
+    int xmlFlags = XML_COMPILE_STANDARD_RESOURCE;
+
+    /* Only enable UTF-8 if the caller of aapt didn't specifically
+     * request UTF-16 encoding and the parameters of this package
+     * allow UTF-8 to be used.
+     */
+    if (!bundle->getWantUTF16()
+            && bundle->isMinSdkAtLeast(SDK_FROYO)) {
+        xmlFlags |= XML_COMPILE_UTF8;
+    }
+
     // --------------------------------------------------------------
     // First, gather all resource information.
     // --------------------------------------------------------------
@@ -618,18 +822,22 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
         current = current->getOverlay();
     }
     // apply the overlay files to the base set
-    applyFileOverlay(assets, drawables, "drawable");
-    applyFileOverlay(assets, layouts, "layout");
-    applyFileOverlay(assets, anims, "anim");
-    applyFileOverlay(assets, xmls, "xml");
-    applyFileOverlay(assets, raws, "raw");
-    applyFileOverlay(assets, colors, "color");
-    applyFileOverlay(assets, menus, "menu");
+    if (!applyFileOverlay(bundle, assets, &drawables, "drawable") ||
+            !applyFileOverlay(bundle, assets, &layouts, "layout") ||
+            !applyFileOverlay(bundle, assets, &anims, "anim") ||
+            !applyFileOverlay(bundle, assets, &xmls, "xml") ||
+            !applyFileOverlay(bundle, assets, &raws, "raw") ||
+            !applyFileOverlay(bundle, assets, &colors, "color") ||
+            !applyFileOverlay(bundle, assets, &menus, "menu")) {
+        return UNKNOWN_ERROR;
+    }
 
     bool hasErrors = false;
 
     if (drawables != NULL) {
-        err = preProcessImages(bundle, assets, drawables);
+        if (bundle->getOutputAPKFile() != NULL) {
+            err = preProcessImages(bundle, assets, drawables);
+        }
         if (err == NO_ERROR) {
             err = makeFileResources(bundle, assets, &table, drawables, "drawable");
             if (err != NO_ERROR) {
@@ -730,7 +938,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
         ResourceDirIterator it(layouts, String8("layout"));
         while ((err=it.next()) == NO_ERROR) {
             String8 src = it.getFile()->getPrintableSource();
-            err = compileXmlFile(assets, it.getFile(), &table);
+            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
             if (err == NO_ERROR) {
                 ResXMLTree block;
                 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
@@ -749,7 +957,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
     if (anims != NULL) {
         ResourceDirIterator it(anims, String8("anim"));
         while ((err=it.next()) == NO_ERROR) {
-            err = compileXmlFile(assets, it.getFile(), &table);
+            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
             if (err != NO_ERROR) {
                 hasErrors = true;
             }
@@ -764,7 +972,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
     if (xmls != NULL) {
         ResourceDirIterator it(xmls, String8("xml"));
         while ((err=it.next()) == NO_ERROR) {
-            err = compileXmlFile(assets, it.getFile(), &table);
+            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
             if (err != NO_ERROR) {
                 hasErrors = true;
             }
@@ -786,7 +994,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
     if (colors != NULL) {
         ResourceDirIterator it(colors, String8("color"));
         while ((err=it.next()) == NO_ERROR) {
-          err = compileXmlFile(assets, it.getFile(), &table);
+          err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
             if (err != NO_ERROR) {
                 hasErrors = true;
             }
@@ -802,7 +1010,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
         ResourceDirIterator it(menus, String8("menu"));
         while ((err=it.next()) == NO_ERROR) {
             String8 src = it.getFile()->getPrintableSource();
-            err = compileXmlFile(assets, it.getFile(), &table);
+            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
             if (err != NO_ERROR) {
                 hasErrors = true;
             }
@@ -817,21 +1025,102 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
         err = NO_ERROR;
     }
 
+    if (table.validateLocalizations()) {
+        hasErrors = true;
+    }
+    
+    if (hasErrors) {
+        return UNKNOWN_ERROR;
+    }
+
     const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0));
     String8 manifestPath(manifestFile->getPrintableSource());
 
+    // Generate final compiled manifest file.
+    manifestFile->clearData();
+    sp<XMLNode> manifestTree = XMLNode::parse(manifestFile);
+    if (manifestTree == NULL) {
+        return UNKNOWN_ERROR;
+    }
+    err = massageManifest(bundle, manifestTree);
+    if (err < NO_ERROR) {
+        return err;
+    }
+    err = compileXmlFile(assets, manifestTree, manifestFile, &table);
+    if (err < NO_ERROR) {
+        return err;
+    }
+
+    //block.restart();
+    //printXMLBlock(&block);
+
+    // --------------------------------------------------------------
+    // Generate the final resource table.
+    // Re-flatten because we may have added new resource IDs
+    // --------------------------------------------------------------
+
+    ResTable finalResTable;
+    sp<AaptFile> resFile;
+    
+    if (table.hasResources()) {
+        sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
+        err = table.addSymbols(symbols);
+        if (err < NO_ERROR) {
+            return err;
+        }
+
+        resFile = getResourceFile(assets);
+        if (resFile == NULL) {
+            fprintf(stderr, "Error: unable to generate entry for resource data\n");
+            return UNKNOWN_ERROR;
+        }
+
+        err = table.flatten(bundle, resFile);
+        if (err < NO_ERROR) {
+            return err;
+        }
+
+        if (bundle->getPublicOutputFile()) {
+            FILE* fp = fopen(bundle->getPublicOutputFile(), "w+");
+            if (fp == NULL) {
+                fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n",
+                        (const char*)bundle->getPublicOutputFile(), strerror(errno));
+                return UNKNOWN_ERROR;
+            }
+            if (bundle->getVerbose()) {
+                printf("  Writing public definitions to %s.\n", bundle->getPublicOutputFile());
+            }
+            table.writePublicDefinitions(String16(assets->getPackage()), fp);
+            fclose(fp);
+        }
+        
+        // Read resources back in,
+        finalResTable.add(resFile->getData(), resFile->getSize(), NULL);
+        
+#if 0
+        NOISY(
+              printf("Generated resources:\n");
+              finalResTable.print();
+        )
+#endif
+    }
+    
     // Perform a basic validation of the manifest file.  This time we
     // parse it with the comments intact, so that we can use them to
     // generate java docs...  so we are not going to write this one
     // back out to the final manifest data.
-    err = compileXmlFile(assets, manifestFile, &table,
+    sp<AaptFile> outManifestFile = new AaptFile(manifestFile->getSourceFile(),
+            manifestFile->getGroupEntry(),
+            manifestFile->getResourceType());
+    err = compileXmlFile(assets, manifestFile,
+            outManifestFile, &table,
             XML_COMPILE_ASSIGN_ATTRIBUTE_IDS
             | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES);
     if (err < NO_ERROR) {
         return err;
     }
     ResXMLTree block;
-    block.setTo(manifestFile->getData(), manifestFile->getSize(), true);
+    block.setTo(outManifestFile->getData(), outManifestFile->getSize(), true);
     String16 manifest16("manifest");
     String16 permission16("permission");
     String16 permission_group16("permission-group");
@@ -870,16 +1159,20 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
                 continue;
             }
             if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) {
-                if (validateAttr(manifestPath, block, NULL, "package",
+                if (validateAttr(manifestPath, finalResTable, block, NULL, "package",
                                  packageIdentChars, true) != ATTR_OKAY) {
                     hasErrors = true;
                 }
+                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
+                                 "sharedUserId", packageIdentChars, false) != ATTR_OKAY) {
+                    hasErrors = true;
+                }
             } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0
                     || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) {
                 const bool isGroup = strcmp16(block.getElementName(&len),
                         permission_group16.string()) == 0;
-                if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name",
-                                 isGroup ? packageIdentCharsWithTheStupid
+                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
+                                 "name", isGroup ? packageIdentCharsWithTheStupid
                                  : packageIdentChars, true) != ATTR_OKAY) {
                     hasErrors = true;
                 }
@@ -957,56 +1250,56 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
                 }
                 syms->makeSymbolPublic(String8(e), srcPos);
             } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) {
-                if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name",
-                                 packageIdentChars, true) != ATTR_OKAY) {
+                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
+                                 "name", packageIdentChars, true) != ATTR_OKAY) {
                     hasErrors = true;
                 }
             } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) {
-                if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name",
-                                 classIdentChars, true) != ATTR_OKAY) {
+                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
+                                 "name", classIdentChars, true) != ATTR_OKAY) {
                     hasErrors = true;
                 }
-                if (validateAttr(manifestPath, block,
+                if (validateAttr(manifestPath, finalResTable, block,
                                  RESOURCES_ANDROID_NAMESPACE, "targetPackage",
                                  packageIdentChars, true) != ATTR_OKAY) {
                     hasErrors = true;
                 }
             } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) {
-                if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name",
-                                 classIdentChars, false) != ATTR_OKAY) {
+                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
+                                 "name", classIdentChars, false) != ATTR_OKAY) {
                     hasErrors = true;
                 }
-                if (validateAttr(manifestPath, block,
+                if (validateAttr(manifestPath, finalResTable, block,
                                  RESOURCES_ANDROID_NAMESPACE, "permission",
                                  packageIdentChars, false) != ATTR_OKAY) {
                     hasErrors = true;
                 }
-                if (validateAttr(manifestPath, block,
+                if (validateAttr(manifestPath, finalResTable, block,
                                  RESOURCES_ANDROID_NAMESPACE, "process",
                                  processIdentChars, false) != ATTR_OKAY) {
                     hasErrors = true;
                 }
-                if (validateAttr(manifestPath, block,
+                if (validateAttr(manifestPath, finalResTable, block,
                                  RESOURCES_ANDROID_NAMESPACE, "taskAffinity",
                                  processIdentChars, false) != ATTR_OKAY) {
                     hasErrors = true;
                 }
             } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) {
-                if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name",
-                                 classIdentChars, true) != ATTR_OKAY) {
+                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
+                                 "name", classIdentChars, true) != ATTR_OKAY) {
                     hasErrors = true;
                 }
-                if (validateAttr(manifestPath, block,
+                if (validateAttr(manifestPath, finalResTable, block,
                                  RESOURCES_ANDROID_NAMESPACE, "authorities",
                                  authoritiesIdentChars, true) != ATTR_OKAY) {
                     hasErrors = true;
                 }
-                if (validateAttr(manifestPath, block,
+                if (validateAttr(manifestPath, finalResTable, block,
                                  RESOURCES_ANDROID_NAMESPACE, "permission",
                                  packageIdentChars, false) != ATTR_OKAY) {
                     hasErrors = true;
                 }
-                if (validateAttr(manifestPath, block,
+                if (validateAttr(manifestPath, finalResTable, block,
                                  RESOURCES_ANDROID_NAMESPACE, "process",
                                  processIdentChars, false) != ATTR_OKAY) {
                     hasErrors = true;
@@ -1014,39 +1307,39 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
             } else if (strcmp16(block.getElementName(&len), service16.string()) == 0
                        || strcmp16(block.getElementName(&len), receiver16.string()) == 0
                        || strcmp16(block.getElementName(&len), activity16.string()) == 0) {
-                if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name",
-                                 classIdentChars, true) != ATTR_OKAY) {
+                if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
+                                 "name", classIdentChars, true) != ATTR_OKAY) {
                     hasErrors = true;
                 }
-                if (validateAttr(manifestPath, block,
+                if (validateAttr(manifestPath, finalResTable, block,
                                  RESOURCES_ANDROID_NAMESPACE, "permission",
                                  packageIdentChars, false) != ATTR_OKAY) {
                     hasErrors = true;
                 }
-                if (validateAttr(manifestPath, block,
+                if (validateAttr(manifestPath, finalResTable, block,
                                  RESOURCES_ANDROID_NAMESPACE, "process",
                                  processIdentChars, false) != ATTR_OKAY) {
                     hasErrors = true;
                 }
-                if (validateAttr(manifestPath, block,
+                if (validateAttr(manifestPath, finalResTable, block,
                                  RESOURCES_ANDROID_NAMESPACE, "taskAffinity",
                                  processIdentChars, false) != ATTR_OKAY) {
                     hasErrors = true;
                 }
             } else if (strcmp16(block.getElementName(&len), action16.string()) == 0
                        || strcmp16(block.getElementName(&len), category16.string()) == 0) {
-                if (validateAttr(manifestPath, block,
+                if (validateAttr(manifestPath, finalResTable, block,
                                  RESOURCES_ANDROID_NAMESPACE, "name",
                                  packageIdentChars, true) != ATTR_OKAY) {
                     hasErrors = true;
                 }
             } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) {
-                if (validateAttr(manifestPath, block,
+                if (validateAttr(manifestPath, finalResTable, block,
                                  RESOURCES_ANDROID_NAMESPACE, "mimeType",
                                  typeIdentChars, true) != ATTR_OKAY) {
                     hasErrors = true;
                 }
-                if (validateAttr(manifestPath, block,
+                if (validateAttr(manifestPath, finalResTable, block,
                                  RESOURCES_ANDROID_NAMESPACE, "scheme",
                                  schemeIdentChars, true) != ATTR_OKAY) {
                     hasErrors = true;
@@ -1055,75 +1348,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
         }
     }
 
-    if (table.validateLocalizations()) {
-        hasErrors = true;
-    }
-    
-    if (hasErrors) {
-        return UNKNOWN_ERROR;
-    }
-
-    // Generate final compiled manifest file.
-    manifestFile->clearData();
-    sp<XMLNode> manifestTree = XMLNode::parse(manifestFile);
-    if (manifestTree == NULL) {
-        return UNKNOWN_ERROR;
-    }
-    err = massageManifest(bundle, manifestTree);
-    if (err < NO_ERROR) {
-        return err;
-    }
-    err = compileXmlFile(assets, manifestTree, manifestFile, &table);
-    if (err < NO_ERROR) {
-        return err;
-    }
-
-    //block.restart();
-    //printXMLBlock(&block);
-
-    // --------------------------------------------------------------
-    // Generate the final resource table.
-    // Re-flatten because we may have added new resource IDs
-    // --------------------------------------------------------------
-
-    if (table.hasResources()) {
-        sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
-        err = table.addSymbols(symbols);
-        if (err < NO_ERROR) {
-            return err;
-        }
-
-        sp<AaptFile> resFile(getResourceFile(assets));
-        if (resFile == NULL) {
-            fprintf(stderr, "Error: unable to generate entry for resource data\n");
-            return UNKNOWN_ERROR;
-        }
-
-        err = table.flatten(bundle, resFile);
-        if (err < NO_ERROR) {
-            return err;
-        }
-
-        if (bundle->getPublicOutputFile()) {
-            FILE* fp = fopen(bundle->getPublicOutputFile(), "w+");
-            if (fp == NULL) {
-                fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n",
-                        (const char*)bundle->getPublicOutputFile(), strerror(errno));
-                return UNKNOWN_ERROR;
-            }
-            if (bundle->getVerbose()) {
-                printf("  Writing public definitions to %s.\n", bundle->getPublicOutputFile());
-            }
-            table.writePublicDefinitions(String16(assets->getPackage()), fp);
-        }
-
-        NOISY(
-              ResTable rt;
-              rt.add(resFile->getData(), resFile->getSize(), NULL);
-              printf("Generated resources:\n");
-              rt.print();
-        )
-
+    if (resFile != NULL) {
         // These resources are now considered to be a part of the included
         // resources, for others to reference.
         err = assets->addIncludedResources(resFile);
@@ -1132,7 +1357,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
             return err;
         }
     }
-
+    
     return err;
 }
 
@@ -1231,10 +1456,16 @@ static status_t writeLayoutClasses(
 
         NA = idents.size();
 
+        bool deprecated = false;
+        
         String16 comment = symbols->getComment(realClassName);
         fprintf(fp, "%s/** ", indentStr);
         if (comment.size() > 0) {
-            fprintf(fp, "%s\n", String8(comment).string());
+            String8 cmt(comment);
+            fprintf(fp, "%s\n", cmt.string());
+            if (strstr(cmt.string(), "@deprecated") != NULL) {
+                deprecated = true;
+            }
         } else {
             fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string());
         }
@@ -1246,10 +1477,10 @@ static status_t writeLayoutClasses(
                     hasTable = true;
                     fprintf(fp,
                             "%s   <p>Includes the following attributes:</p>\n"
-                            "%s   <table border=\"2\" width=\"85%%\" align=\"center\" frame=\"hsides\" rules=\"all\" cellpadding=\"5\">\n"
+                            "%s   <table>\n"
                             "%s   <colgroup align=\"left\" />\n"
                             "%s   <colgroup align=\"left\" />\n"
-                            "%s   <tr><th>Attribute<th>Summary</tr>\n",
+                            "%s   <tr><th>Attribute</th><th>Description</th></tr>\n",
                             indentStr,
                             indentStr,
                             indentStr,
@@ -1283,7 +1514,7 @@ static status_t writeLayoutClasses(
                 }
                 String16 name(name8);
                 fixupSymbol(&name);
-                fprintf(fp, "%s   <tr><th><code>{@link #%s_%s %s:%s}</code><td>%s</tr>\n",
+                fprintf(fp, "%s   <tr><td><code>{@link #%s_%s %s:%s}</code></td><td>%s</td></tr>\n",
                         indentStr, nclassName.string(),
                         String8(name).string(),
                         assets->getPackage().string(),
@@ -1310,6 +1541,10 @@ static status_t writeLayoutClasses(
         }
         fprintf(fp, "%s */\n", getIndentSpace(indent));
 
+        if (deprecated) {
+            fprintf(fp, "%s@Deprecated\n", indentStr);
+        }
+        
         fprintf(fp,
                 "%spublic static final int[] %s = {\n"
                 "%s",
@@ -1358,11 +1593,17 @@ static status_t writeLayoutClasses(
                 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
                 //    String8(attr16).string(), String8(name16).string(), typeSpecFlags);
                 const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
-                    
+                
+                bool deprecated = false;
+                
                 fprintf(fp, "%s/**\n", indentStr);
                 if (comment.size() > 0) {
+                    String8 cmt(comment);
                     fprintf(fp, "%s  <p>\n%s  @attr description\n", indentStr, indentStr);
-                    fprintf(fp, "%s  %s\n", indentStr, String8(comment).string());
+                    fprintf(fp, "%s  %s\n", indentStr, cmt.string());
+                    if (strstr(cmt.string(), "@deprecated") != NULL) {
+                        deprecated = true;
+                    }
                 } else {
                     fprintf(fp,
                             "%s  <p>This symbol is the offset where the {@link %s.R.attr#%s}\n"
@@ -1374,7 +1615,11 @@ static status_t writeLayoutClasses(
                             indentStr, nclassName.string());
                 }
                 if (typeComment.size() > 0) {
-                    fprintf(fp, "\n\n%s  %s\n", indentStr, String8(typeComment).string());
+                    String8 cmt(typeComment);
+                    fprintf(fp, "\n\n%s  %s\n", indentStr, cmt.string());
+                    if (strstr(cmt.string(), "@deprecated") != NULL) {
+                        deprecated = true;
+                    }
                 }
                 if (comment.size() > 0) {
                     if (pub) {
@@ -1392,6 +1637,9 @@ static status_t writeLayoutClasses(
                 fprintf(fp, "%s  @attr name %s:%s\n", indentStr,
                         "android", String8(name).string());
                 fprintf(fp, "%s*/\n", indentStr);
+                if (deprecated) {
+                    fprintf(fp, "%s@Deprecated\n", indentStr);
+                }
                 fprintf(fp,
                         "%spublic static final int %s_%s = %d;\n",
                         indentStr, nclassName.string(),
@@ -1433,11 +1681,16 @@ static status_t writeSymbolClass(
         }
         String16 comment(sym.comment);
         bool haveComment = false;
+        bool deprecated = false;
         if (comment.size() > 0) {
             haveComment = true;
+            String8 cmt(comment);
             fprintf(fp,
                     "%s/** %s\n",
-                    getIndentSpace(indent), String8(comment).string());
+                    getIndentSpace(indent), cmt.string());
+            if (strstr(cmt.string(), "@deprecated") != NULL) {
+                deprecated = true;
+            }
         } else if (sym.isPublic && !includePrivate) {
             sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
                 assets->getPackage().string(), className.string(),
@@ -1445,20 +1698,25 @@ static status_t writeSymbolClass(
         }
         String16 typeComment(sym.typeComment);
         if (typeComment.size() > 0) {
+            String8 cmt(typeComment);
             if (!haveComment) {
                 haveComment = true;
                 fprintf(fp,
-                        "%s/** %s\n",
-                        getIndentSpace(indent), String8(typeComment).string());
+                        "%s/** %s\n", getIndentSpace(indent), cmt.string());
             } else {
                 fprintf(fp,
-                        "%s %s\n",
-                        getIndentSpace(indent), String8(typeComment).string());
+                        "%s %s\n", getIndentSpace(indent), cmt.string());
+            }
+            if (strstr(cmt.string(), "@deprecated") != NULL) {
+                deprecated = true;
             }
         }
         if (haveComment) {
             fprintf(fp,"%s */\n", getIndentSpace(indent));
         }
+        if (deprecated) {
+            fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
+        }
         fprintf(fp, "%spublic static final int %s=0x%08x;\n",
                 getIndentSpace(indent),
                 String8(name).string(), (int)sym.int32Val);
@@ -1477,17 +1735,25 @@ static status_t writeSymbolClass(
             return UNKNOWN_ERROR;
         }
         String16 comment(sym.comment);
+        bool deprecated = false;
         if (comment.size() > 0) {
+            String8 cmt(comment);
             fprintf(fp,
                     "%s/** %s\n"
                      "%s */\n",
-                    getIndentSpace(indent), String8(comment).string(),
+                    getIndentSpace(indent), cmt.string(),
                     getIndentSpace(indent));
+            if (strstr(cmt.string(), "@deprecated") != NULL) {
+                deprecated = true;
+            }
         } else if (sym.isPublic && !includePrivate) {
             sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
                 assets->getPackage().string(), className.string(),
                 String8(sym.name).string());
         }
+        if (deprecated) {
+            fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
+        }
         fprintf(fp, "%spublic static final String %s=\"%s\";\n",
                 getIndentSpace(indent),
                 String8(name).string(), sym.stringVal.string());
@@ -1582,3 +1848,320 @@ status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
 
     return NO_ERROR;
 }
+
+
+
+class ProguardKeepSet
+{
+public:
+    // { rule --> { file locations } }
+    KeyedVector<String8, SortedVector<String8> > rules;
+
+    void add(const String8& rule, const String8& where);
+};
+
+void ProguardKeepSet::add(const String8& rule, const String8& where)
+{
+    ssize_t index = rules.indexOfKey(rule);
+    if (index < 0) {
+        index = rules.add(rule, SortedVector<String8>());
+    }
+    rules.editValueAt(index).add(where);
+}
+
+void
+addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName,
+        const char* pkg, const String8& srcName, int line)
+{
+    String8 className(inClassName);
+    if (pkg != NULL) {
+        // asdf     --> package.asdf
+        // .asdf  .a.b  --> package.asdf package.a.b
+        // asdf.adsf --> asdf.asdf
+        const char* p = className.string();
+        const char* q = strchr(p, '.');
+        if (p == q) {
+            className = pkg;
+            className.append(inClassName);
+        } else if (q == NULL) {
+            className = pkg;
+            className.append(".");
+            className.append(inClassName);
+        }
+    }
+
+    String8 rule("-keep class ");
+    rule += className;
+    rule += " { <init>(...); }";
+
+    String8 location("view ");
+    location += srcName;
+    char lineno[20];
+    sprintf(lineno, ":%d", line);
+    location += lineno;
+
+    keep->add(rule, location);
+}
+
+status_t
+writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
+{
+    status_t err;
+    ResXMLTree tree;
+    size_t len;
+    ResXMLTree::event_code_t code;
+    int depth = 0;
+    bool inApplication = false;
+    String8 error;
+    sp<AaptGroup> assGroup;
+    sp<AaptFile> assFile;
+    String8 pkg;
+
+    // First, look for a package file to parse.  This is required to
+    // be able to generate the resource information.
+    assGroup = assets->getFiles().valueFor(String8("AndroidManifest.xml"));
+    if (assGroup == NULL) {
+        fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n");
+        return -1;
+    }
+
+    if (assGroup->getFiles().size() != 1) {
+        fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
+                assGroup->getFiles().valueAt(0)->getPrintableSource().string());
+    }
+
+    assFile = assGroup->getFiles().valueAt(0);
+
+    err = parseXMLResource(assFile, &tree);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    tree.restart();
+
+    while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+        if (code == ResXMLTree::END_TAG) {
+            if (/* name == "Application" && */ depth == 2) {
+                inApplication = false;
+            }
+            depth--;
+            continue;
+        }
+        if (code != ResXMLTree::START_TAG) {
+            continue;
+        }
+        depth++;
+        String8 tag(tree.getElementName(&len));
+        // printf("Depth %d tag %s\n", depth, tag.string());
+        bool keepTag = false;
+        if (depth == 1) {
+            if (tag != "manifest") {
+                fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
+                return -1;
+            }
+            pkg = getAttribute(tree, NULL, "package", NULL);
+        } else if (depth == 2) {
+            if (tag == "application") {
+                inApplication = true;
+                keepTag = true;
+
+                String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android",
+                        "backupAgent", &error);
+                if (agent.length() > 0) {
+                    addProguardKeepRule(keep, agent, pkg.string(),
+                            assFile->getPrintableSource(), tree.getLineNumber());
+                }
+            } else if (tag == "instrumentation") {
+                keepTag = true;
+            }
+        }
+        if (!keepTag && inApplication && depth == 3) {
+            if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
+                keepTag = true;
+            }
+        }
+        if (keepTag) {
+            String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android",
+                    "name", &error);
+            if (error != "") {
+                fprintf(stderr, "ERROR: %s\n", error.string());
+                return -1;
+            }
+            if (name.length() > 0) {
+                addProguardKeepRule(keep, name, pkg.string(),
+                        assFile->getPrintableSource(), tree.getLineNumber());
+            }
+        }
+    }
+
+    return NO_ERROR;
+}
+
+struct NamespaceAttributePair {
+    const char* ns;
+    const char* attr;
+
+    NamespaceAttributePair(const char* n, const char* a) : ns(n), attr(a) {}
+    NamespaceAttributePair() : ns(NULL), attr(NULL) {}
+};
+
+status_t
+writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
+        const char* startTag, const KeyedVector<String8, NamespaceAttributePair>* tagAttrPairs)
+{
+    status_t err;
+    ResXMLTree tree;
+    size_t len;
+    ResXMLTree::event_code_t code;
+
+    err = parseXMLResource(layoutFile, &tree);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    tree.restart();
+
+    if (startTag != NULL) {
+        bool haveStart = false;
+        while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+            if (code != ResXMLTree::START_TAG) {
+                continue;
+            }
+            String8 tag(tree.getElementName(&len));
+            if (tag == startTag) {
+                haveStart = true;
+            }
+            break;
+        }
+        if (!haveStart) {
+            return NO_ERROR;
+        }
+    }
+
+    while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+        if (code != ResXMLTree::START_TAG) {
+            continue;
+        }
+        String8 tag(tree.getElementName(&len));
+
+        // If there is no '.', we'll assume that it's one of the built in names.
+        if (strchr(tag.string(), '.')) {
+            addProguardKeepRule(keep, tag, NULL,
+                    layoutFile->getPrintableSource(), tree.getLineNumber());
+        } else if (tagAttrPairs != NULL) {
+            ssize_t tagIndex = tagAttrPairs->indexOfKey(tag);
+            if (tagIndex >= 0) {
+                const NamespaceAttributePair& nsAttr = tagAttrPairs->valueAt(tagIndex);
+                ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr);
+                if (attrIndex < 0) {
+                    // fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n",
+                    //        layoutFile->getPrintableSource().string(), tree.getLineNumber(),
+                    //        tag.string(), nsAttr.ns, nsAttr.attr);
+                } else {
+                    size_t len;
+                    addProguardKeepRule(keep,
+                                        String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
+                                        layoutFile->getPrintableSource(), tree.getLineNumber());
+                }
+            }
+        }
+    }
+
+    return NO_ERROR;
+}
+
+static void addTagAttrPair(KeyedVector<String8, NamespaceAttributePair>* dest,
+        const char* tag, const char* ns, const char* attr) {
+    dest->add(String8(tag), NamespaceAttributePair(ns, attr));
+}
+
+status_t
+writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
+{
+    status_t err;
+
+    // tag:attribute pairs that should be checked in layout files.
+    KeyedVector<String8, NamespaceAttributePair> kLayoutTagAttrPairs;
+    addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class");
+    addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name");
+
+    // tag:attribute pairs that should be checked in xml files.
+    KeyedVector<String8, NamespaceAttributePair> kXmlTagAttrPairs;
+    addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment");
+    addTagAttrPair(&kXmlTagAttrPairs, "Header", RESOURCES_ANDROID_NAMESPACE, "fragment");
+
+    const Vector<sp<AaptDir> >& dirs = assets->resDirs();
+    const size_t K = dirs.size();
+    for (size_t k=0; k<K; k++) {
+        const sp<AaptDir>& d = dirs.itemAt(k);
+        const String8& dirName = d->getLeaf();
+        const char* startTag = NULL;
+        const KeyedVector<String8, NamespaceAttributePair>* tagAttrPairs = NULL;
+        if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) {
+            tagAttrPairs = &kLayoutTagAttrPairs;
+        } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
+            startTag = "PreferenceScreen";
+            tagAttrPairs = &kXmlTagAttrPairs;
+        } else {
+            continue;
+        }
+
+        const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles();
+        const size_t N = groups.size();
+        for (size_t i=0; i<N; i++) {
+            const sp<AaptGroup>& group = groups.valueAt(i);
+            const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles();
+            const size_t M = files.size();
+            for (size_t j=0; j<M; j++) {
+                err = writeProguardForXml(keep, files.valueAt(j), startTag, tagAttrPairs);
+                if (err < 0) {
+                    return err;
+                }
+            }
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t
+writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets)
+{
+    status_t err = -1;
+
+    if (!bundle->getProguardFile()) {
+        return NO_ERROR;
+    }
+
+    ProguardKeepSet keep;
+
+    err = writeProguardForAndroidManifest(&keep, assets);
+    if (err < 0) {
+        return err;
+    }
+
+    err = writeProguardForLayouts(&keep, assets);
+    if (err < 0) {
+        return err;
+    }
+
+    FILE* fp = fopen(bundle->getProguardFile(), "w+");
+    if (fp == NULL) {
+        fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
+                bundle->getProguardFile(), strerror(errno));
+        return UNKNOWN_ERROR;
+    }
+
+    const KeyedVector<String8, SortedVector<String8> >& rules = keep.rules;
+    const size_t N = rules.size();
+    for (size_t i=0; i<N; i++) {
+        const SortedVector<String8>& locations = rules.valueAt(i);
+        const size_t M = locations.size();
+        for (size_t j=0; j<M; j++) {
+            fprintf(fp, "# %s\n", locations.itemAt(j).string());
+        }
+        fprintf(fp, "%s\n\n", rules.keyAt(i).string());
+    }
+    fclose(fp);
+
+    return err;
+}