]> git.saurik.com Git - android/aapt.git/blobdiff - Resource.cpp
Use UTF-8 strings to avoid duplicate caching, part 1
[android/aapt.git] / Resource.cpp
index e8410cdf0ca4746ca9bb8eb6d1ccc02bdc96317c..88c5441c26d3442425eaaf931a06c4bd1e7b5596 100644 (file)
@@ -101,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";
@@ -171,7 +171,8 @@ 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",
@@ -215,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("android",
+                                                             "minSdkVersion");
+                if (minSdkIndex >= 0) {
+                    String8 minSdkString = String8(
+                        block.getAttributeStringValue(minSdkIndex, &len));
+                   bundle->setMinSdkVersion(minSdkString.string());
+                }
+            }
+        }
+    }
+
     return NO_ERROR;
 }
 
@@ -428,10 +445,15 @@ static void checkForIds(const String8& path, ResXMLParser& parser)
     }
 }
 
-static bool applyFileOverlay(const sp<AaptAssets>& assets,
+static bool applyFileOverlay(Bundle *bundle,
+                             const sp<AaptAssets>& assets,
                              const 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();
@@ -450,6 +472,9 @@ static bool applyFileOverlay(const sp<AaptAssets>& assets,
             // non-overlay "baseset".
             size_t overlayCount = overlaySet->size();
             for (size_t overlayIndex=0; overlayIndex<overlayCount; overlayIndex++) {
+                if (bundle->getVerbose()) {
+                    printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string());
+                }
                 size_t baseIndex = baseSet->indexOfKey(overlaySet->keyAt(overlayIndex));
                 if (baseIndex < UNKNOWN_ERROR) {
                     // look for same flavor.  For a given file (strings.xml, for example)
@@ -457,18 +482,36 @@ static bool applyFileOverlay(const sp<AaptAssets>& assets,
                     // 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 = 
+
+                    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..
@@ -482,11 +525,11 @@ static bool applyFileOverlay(const sp<AaptAssets>& assets,
                             overlaySet->valueAt(overlayIndex));
                     // make sure all flavors are defined in the resources.
                     sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
-                    DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles = 
+                    DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
                             overlayGroup->getFiles();
                     size_t overlayGroupSize = overlayFiles.size();
-                    for (size_t overlayGroupIndex = 0; 
-                            overlayGroupIndex<overlayGroupSize; 
+                    for (size_t overlayGroupIndex = 0;
+                            overlayGroupIndex<overlayGroupSize;
                             overlayGroupIndex++) {
                         assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
                     }
@@ -519,6 +562,33 @@ void addTagAttribute(const sp<XMLNode>& node, const char* ns8,
     node->addAttribute(ns, attr, String16(value));
 }
 
+static void fullyQualifyClassName(String8& package, sp<XMLNode> node) {
+    XMLNode::attribute_entry* attr = node->editAttribute(
+            String16("http://schemas.android.com/apk/res/android"), String16("name"));
+    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"));
@@ -548,7 +618,36 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
         addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion",
                 bundle->getMaxSdkVersion());
     }
-    
+
+    // 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);
+
+            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);
+                }
+            }
+        }
+    }
+
     return NO_ERROR;
 }
 
@@ -571,7 +670,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;
     }
@@ -587,6 +686,12 @@ 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;
+    if (bundle->getUTF8()) {
+        xmlFlags |= XML_COMPILE_UTF8;
+    }
+
     // --------------------------------------------------------------
     // First, gather all resource information.
     // --------------------------------------------------------------
@@ -623,13 +728,13 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
         current = current->getOverlay();
     }
     // apply the overlay files to the base set
-    if (!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;
     }
 
@@ -737,7 +842,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);
@@ -756,7 +861,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;
             }
@@ -771,7 +876,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;
             }
@@ -793,7 +898,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;
             }
@@ -809,7 +914,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;
             }
@@ -1124,14 +1229,14 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
             table.writePublicDefinitions(String16(assets->getPackage()), fp);
             fclose(fp);
         }
-
+#if 0
         NOISY(
               ResTable rt;
               rt.add(resFile->getData(), resFile->getSize(), NULL);
               printf("Generated resources:\n");
               rt.print();
         )
-
+#endif
         // These resources are now considered to be a part of the included
         // resources, for others to reference.
         err = assets->addIncludedResources(resFile);
@@ -1259,10 +1364,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,
@@ -1296,7 +1401,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(),
@@ -1630,3 +1735,246 @@ 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);
+}
+
+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;
+            } 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) {
+                // asdf     --> package.asdf
+                // .asdf  .a.b  --> package.asdf package.a.b
+                // asdf.adsf --> asdf.asdf
+                String8 rule("-keep class ");
+                const char* p = name.string();
+                const char* q = strchr(p, '.');
+                if (p == q) {
+                    rule += pkg;
+                    rule += name;
+                } else if (q == NULL) {
+                    rule += pkg;
+                    rule += ".";
+                    rule += name;
+                } else {
+                    rule += name;
+                }
+
+                String8 location = tag;
+                location += " ";
+                location += assFile->getSourceFile();
+                char lineno[20];
+                sprintf(lineno, ":%d", tree.getLineNumber());
+                location += lineno;
+
+                keep->add(rule, location);
+            }
+        }
+    }
+
+    return NO_ERROR;
+}
+
+status_t
+writeProguardForLayout(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile)
+{
+    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();
+
+    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(), '.')) {
+            String8 rule("-keep class ");
+            rule += tag;
+            rule += " { <init>(...); }";
+
+            String8 location("view ");
+            location += layoutFile->getSourceFile();
+            char lineno[20];
+            sprintf(lineno, ":%d", tree.getLineNumber());
+            location += lineno;
+
+            keep->add(rule, location);
+        }
+    }
+
+    return NO_ERROR;
+}
+
+status_t
+writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
+{
+    status_t err;
+    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();
+        if ((dirName != String8("layout")) && (strncmp(dirName.string(), "layout-", 7) != 0)) {
+            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 = writeProguardForLayout(keep, files.valueAt(j));
+                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;
+}