]> git.saurik.com Git - android/aapt.git/blobdiff - ResourceTable.cpp
am 39b58ba2: resolved conflicts for merge of bc2fb7b9 to jb-dev-plus-aosp
[android/aapt.git] / ResourceTable.cpp
index 25ab1474d94863c3509dcc3da54f246f98954e24..22b75d883eb7b81ec4ef4adaa723975e64aaa2d5 100644 (file)
@@ -7,9 +7,10 @@
 #include "ResourceTable.h"
 
 #include "XMLNode.h"
+#include "ResourceFilter.h"
 
+#include <androidfw/ResourceTypes.h>
 #include <utils/ByteOrder.h>
-#include <utils/ResourceTypes.h>
 #include <stdarg.h>
 
 #define NOISY(x) //x
@@ -27,6 +28,20 @@ status_t compileXmlFile(const sp<AaptAssets>& assets,
     return compileXmlFile(assets, root, target, table, options);
 }
 
+status_t compileXmlFile(const sp<AaptAssets>& assets,
+                        const sp<AaptFile>& target,
+                        const sp<AaptFile>& outTarget,
+                        ResourceTable* table,
+                        int options)
+{
+    sp<XMLNode> root = XMLNode::parse(target);
+    if (root == NULL) {
+        return UNKNOWN_ERROR;
+    }
+    
+    return compileXmlFile(assets, root, outTarget, table, options);
+}
+
 status_t compileXmlFile(const sp<AaptAssets>& assets,
                         const sp<XMLNode>& root,
                         const sp<AaptFile>& target,
@@ -39,6 +54,10 @@ status_t compileXmlFile(const sp<AaptAssets>& assets,
         root->removeWhitespace(false, NULL);
     }
 
+    if ((options&XML_COMPILE_UTF8) != 0) {
+        root->setUTF8(true);
+    }
+
     bool hasErrors = false;
     
     if ((options&XML_COMPILE_ASSIGN_ATTRIBUTE_IDS) != 0) {
@@ -378,7 +397,7 @@ static status_t compileAttribute(const sp<AaptFile>& in,
         }
         attr.createIfNeeded(outTable);
         if (!attr.hasErrors) {
-            char buf[10];
+            char buf[11];
             sprintf(buf, "%d", l10n_required);
             err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident,
                     String16(""), String16("^l10n"), String16(buf), NULL, NULL);
@@ -480,22 +499,22 @@ static status_t compileAttribute(const sp<AaptFile>& in,
                     enumOrFlagsComment.append((attr.type&ResTable_map::TYPE_ENUM)
                                        ? String16(" be one of the following constant values.")
                                        : String16(" be one or more (separated by '|') of the following constant values."));
-                    enumOrFlagsComment.append(String16("</p>\n<table border=\"2\" width=\"85%\" align=\"center\" frame=\"hsides\" rules=\"all\" cellpadding=\"5\">\n"
+                    enumOrFlagsComment.append(String16("</p>\n<table>\n"
                                                 "<colgroup align=\"left\" />\n"
                                                 "<colgroup align=\"left\" />\n"
                                                 "<colgroup align=\"left\" />\n"
-                                                "<tr><th>Constant<th>Value<th>Description</tr>"));
+                                                "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>"));
                 }
                 
-                enumOrFlagsComment.append(String16("\n<tr><th><code>"));
+                enumOrFlagsComment.append(String16("\n<tr><td><code>"));
                 enumOrFlagsComment.append(itemIdent);
-                enumOrFlagsComment.append(String16("</code><td>"));
+                enumOrFlagsComment.append(String16("</code></td><td>"));
                 enumOrFlagsComment.append(value);
-                enumOrFlagsComment.append(String16("<td>"));
+                enumOrFlagsComment.append(String16("</td><td>"));
                 if (block.getComment(&len)) {
                     enumOrFlagsComment.append(String16(block.getComment(&len)));
                 }
-                enumOrFlagsComment.append(String16("</tr>"));
+                enumOrFlagsComment.append(String16("</td></tr>"));
                 
                 err = outTable->addBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
                                        myPackage,
@@ -555,6 +574,8 @@ status_t parseAndAddBag(Bundle* bundle,
                         const String16& parentIdent,
                         const String16& itemIdent,
                         int32_t curFormat,
+                        bool isFormatted,
+                        const String16& product,
                         bool pseudolocalize,
                         const bool overwrite,
                         ResourceTable* outTable)
@@ -565,7 +586,7 @@ status_t parseAndAddBag(Bundle* bundle,
     String16 str;
     Vector<StringPool::entry_style_span> spans;
     err = parseStyledString(bundle, in->getPrintableSource().string(),
-                            block, item16, &str, &spans,
+                            block, item16, &str, &spans, isFormatted,
                             pseudolocalize);
     if (err != NO_ERROR) {
         return err;
@@ -587,6 +608,32 @@ status_t parseAndAddBag(Bundle* bundle,
     return err;
 }
 
+/*
+ * Returns true if needle is one of the elements in the comma-separated list
+ * haystack, false otherwise.
+ */
+bool isInProductList(const String16& needle, const String16& haystack) {
+    const char16_t *needle2 = needle.string();
+    const char16_t *haystack2 = haystack.string();
+    size_t needlesize = needle.size();
+
+    while (*haystack2 != '\0') {
+        if (strncmp16(haystack2, needle2, needlesize) == 0) {
+            if (haystack2[needlesize] == '\0' || haystack2[needlesize] == ',') {
+                return true;
+            }
+        }
+
+        while (*haystack2 != '\0' && *haystack2 != ',') {
+            haystack2++;
+        }
+        if (*haystack2 == ',') {
+            haystack2++;
+        }
+    }
+
+    return false;
+}
 
 status_t parseAndAddEntry(Bundle* bundle,
                         const sp<AaptFile>& in,
@@ -598,6 +645,8 @@ status_t parseAndAddEntry(Bundle* bundle,
                         const String16& curTag,
                         bool curIsStyled,
                         int32_t curFormat,
+                        bool isFormatted,
+                        const String16& product,
                         bool pseudolocalize,
                         const bool overwrite,
                         ResourceTable* outTable)
@@ -608,12 +657,53 @@ status_t parseAndAddEntry(Bundle* bundle,
     Vector<StringPool::entry_style_span> spans;
     err = parseStyledString(bundle, in->getPrintableSource().string(), block,
                             curTag, &str, curIsStyled ? &spans : NULL,
-                            pseudolocalize);
+                            isFormatted, pseudolocalize);
 
     if (err < NO_ERROR) { 
         return err;
     }
 
+    /*
+     * If a product type was specified on the command line
+     * and also in the string, and the two are not the same,
+     * return without adding the string.
+     */
+
+    const char *bundleProduct = bundle->getProduct();
+    if (bundleProduct == NULL) {
+        bundleProduct = "";
+    }
+
+    if (product.size() != 0) {
+        /*
+         * If the command-line-specified product is empty, only "default"
+         * matches.  Other variants are skipped.  This is so generation
+         * of the R.java file when the product is not known is predictable.
+         */
+
+        if (bundleProduct[0] == '\0') {
+            if (strcmp16(String16("default").string(), product.string()) != 0) {
+                return NO_ERROR;
+            }
+        } else {
+            /*
+             * The command-line product is not empty.
+             * If the product for this string is on the command-line list,
+             * it matches.  "default" also matches, but only if nothing
+             * else has matched already.
+             */
+
+            if (isInProductList(product, String16(bundleProduct))) {
+                ;
+            } else if (strcmp16(String16("default").string(), product.string()) == 0 &&
+                       !outTable->hasBagOrEntry(myPackage, curType, ident, config)) {
+                ;
+            } else {
+                return NO_ERROR;
+            }
+        }
+    }
+
     NOISY(printf("Adding resource entry l=%c%c c=%c%c orien=%d d=%d id=%s: %s\n",
                  config.language[0], config.language[1],
                  config.country[0], config.country[1],
@@ -661,7 +751,10 @@ status_t compileResourceFile(Bundle* bundle,
     const String16 string_array16("string-array");
     const String16 integer_array16("integer-array");
     const String16 public16("public");
+    const String16 public_padding16("public-padding");
     const String16 private_symbols16("private-symbols");
+    const String16 java_symbol16("java-symbol");
+    const String16 add_resource16("add-resource");
     const String16 skip16("skip");
     const String16 eat_comment16("eat-comment");
 
@@ -689,13 +782,19 @@ status_t compileResourceFile(Bundle* bundle,
     // useful attribute names and special values
     const String16 name16("name");
     const String16 translatable16("translatable");
+    const String16 formatted16("formatted");
     const String16 false16("false");
 
     const String16 myPackage(assets->getPackage());
 
     bool hasErrors = false;
-    
-    uint32_t nextPublicId = 0;
+
+    bool fileIsTranslatable = true;
+    if (strstr(in->getPrintableSource().string(), "donottranslate") != NULL) {
+        fileIsTranslatable = false;
+    }
+
+    DefaultKeyedVector<String16, uint32_t> nextPublicId(0);
 
     ResXMLTree::event_code_t code;
     do {
@@ -728,8 +827,10 @@ status_t compileResourceFile(Bundle* bundle,
             String16 curType;
             int32_t curFormat = ResTable_map::TYPE_ANY;
             bool curIsBag = false;
+            bool curIsBagReplaceOnOverwrite = false;
             bool curIsStyled = false;
             bool curIsPseudolocalizable = false;
+            bool curIsFormatted = fileIsTranslatable;
             bool localHasErrors = false;
 
             if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
@@ -784,15 +885,15 @@ status_t compileResourceFile(Bundle* bundle,
                         hasErrors = localHasErrors = true;
                     } else {
                         ident = identValue.data;
-                        nextPublicId = ident+1;
+                        nextPublicId.replaceValueFor(type, ident+1);
                     }
-                } else if (nextPublicId == 0) {
+                } else if (nextPublicId.indexOfKey(type) < 0) {
                     srcPos.error("No 'id' attribute supplied <public>,"
                             " and no previous id defined in this file.\n");
                     hasErrors = localHasErrors = true;
                 } else if (!localHasErrors) {
-                    ident = nextPublicId;
-                    nextPublicId++;
+                    ident = nextPublicId.valueFor(type);
+                    nextPublicId.replaceValueFor(type, ident+1);
                 }
 
                 if (!localHasErrors) {
@@ -826,6 +927,116 @@ status_t compileResourceFile(Bundle* bundle,
                 }
                 continue;
 
+            } else if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) {
+                SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
+            
+                String16 type;
+                ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
+                if (typeIdx < 0) {
+                    srcPos.error("A 'type' attribute is required for <public-padding>\n");
+                    hasErrors = localHasErrors = true;
+                }
+                type = String16(block.getAttributeStringValue(typeIdx, &len));
+
+                String16 name;
+                ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
+                if (nameIdx < 0) {
+                    srcPos.error("A 'name' attribute is required for <public-padding>\n");
+                    hasErrors = localHasErrors = true;
+                }
+                name = String16(block.getAttributeStringValue(nameIdx, &len));
+
+                uint32_t start = 0;
+                ssize_t startIdx = block.indexOfAttribute(NULL, "start");
+                if (startIdx >= 0) {
+                    const char16_t* startStr = block.getAttributeStringValue(startIdx, &len);
+                    Res_value startValue;
+                    if (!ResTable::stringToInt(startStr, len, &startValue)) {
+                        srcPos.error("Given 'start' attribute is not an integer: %s\n",
+                                String8(block.getAttributeStringValue(startIdx, &len)).string());
+                        hasErrors = localHasErrors = true;
+                    } else {
+                        start = startValue.data;
+                    }
+                } else if (nextPublicId.indexOfKey(type) < 0) {
+                    srcPos.error("No 'start' attribute supplied <public-padding>,"
+                            " and no previous id defined in this file.\n");
+                    hasErrors = localHasErrors = true;
+                } else if (!localHasErrors) {
+                    start = nextPublicId.valueFor(type);
+                }
+
+                uint32_t end = 0;
+                ssize_t endIdx = block.indexOfAttribute(NULL, "end");
+                if (endIdx >= 0) {
+                    const char16_t* endStr = block.getAttributeStringValue(endIdx, &len);
+                    Res_value endValue;
+                    if (!ResTable::stringToInt(endStr, len, &endValue)) {
+                        srcPos.error("Given 'end' attribute is not an integer: %s\n",
+                                String8(block.getAttributeStringValue(endIdx, &len)).string());
+                        hasErrors = localHasErrors = true;
+                    } else {
+                        end = endValue.data;
+                    }
+                } else {
+                    srcPos.error("No 'end' attribute supplied <public-padding>\n");
+                    hasErrors = localHasErrors = true;
+                }
+
+                if (end >= start) {
+                    nextPublicId.replaceValueFor(type, end+1);
+                } else {
+                    srcPos.error("Padding start '%ul' is after end '%ul'\n",
+                            start, end);
+                    hasErrors = localHasErrors = true;
+                }
+                
+                String16 comment(
+                    block.getComment(&len) ? block.getComment(&len) : nulStr);
+                for (uint32_t curIdent=start; curIdent<=end; curIdent++) {
+                    if (localHasErrors) {
+                        break;
+                    }
+                    String16 curName(name);
+                    char buf[64];
+                    sprintf(buf, "%d", (int)(end-curIdent+1));
+                    curName.append(String16(buf));
+                    
+                    err = outTable->addEntry(srcPos, myPackage, type, curName,
+                                             String16("padding"), NULL, &curParams, false,
+                                             ResTable_map::TYPE_STRING, overwrite);
+                    if (err < NO_ERROR) {
+                        hasErrors = localHasErrors = true;
+                        break;
+                    }
+                    err = outTable->addPublic(srcPos, myPackage, type,
+                            curName, curIdent);
+                    if (err < NO_ERROR) {
+                        hasErrors = localHasErrors = true;
+                        break;
+                    }
+                    sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
+                    if (symbols != NULL) {
+                        symbols = symbols->addNestedSymbol(String8(type), srcPos);
+                    }
+                    if (symbols != NULL) {
+                        symbols->makeSymbolPublic(String8(curName), srcPos);
+                        symbols->appendComment(String8(curName), comment, srcPos);
+                    } else {
+                        srcPos.error("Unable to create symbols!\n");
+                        hasErrors = localHasErrors = true;
+                    }
+                }
+
+                while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+                    if (code == ResXMLTree::END_TAG) {
+                        if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) {
+                            break;
+                        }
+                    }
+                }
+                continue;
+
             } else if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
                 String16 pkg;
                 ssize_t pkgIdx = block.indexOfAttribute(NULL, "package");
@@ -848,6 +1059,79 @@ status_t compileResourceFile(Bundle* bundle,
                 }
                 continue;
 
+            } else if (strcmp16(block.getElementName(&len), java_symbol16.string()) == 0) {
+                SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
+            
+                String16 type;
+                ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
+                if (typeIdx < 0) {
+                    srcPos.error("A 'type' attribute is required for <public>\n");
+                    hasErrors = localHasErrors = true;
+                }
+                type = String16(block.getAttributeStringValue(typeIdx, &len));
+
+                String16 name;
+                ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
+                if (nameIdx < 0) {
+                    srcPos.error("A 'name' attribute is required for <public>\n");
+                    hasErrors = localHasErrors = true;
+                }
+                name = String16(block.getAttributeStringValue(nameIdx, &len));
+
+                sp<AaptSymbols> symbols = assets->getJavaSymbolsFor(String8("R"));
+                if (symbols != NULL) {
+                    symbols = symbols->addNestedSymbol(String8(type), srcPos);
+                }
+                if (symbols != NULL) {
+                    symbols->makeSymbolJavaSymbol(String8(name), srcPos);
+                    String16 comment(
+                        block.getComment(&len) ? block.getComment(&len) : nulStr);
+                    symbols->appendComment(String8(name), comment, srcPos);
+                } else {
+                    srcPos.error("Unable to create symbols!\n");
+                    hasErrors = localHasErrors = true;
+                }
+
+                while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+                    if (code == ResXMLTree::END_TAG) {
+                        if (strcmp16(block.getElementName(&len), java_symbol16.string()) == 0) {
+                            break;
+                        }
+                    }
+                }
+                continue;
+
+
+            } else if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) {
+                SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
+            
+                String16 typeName;
+                ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
+                if (typeIdx < 0) {
+                    srcPos.error("A 'type' attribute is required for <add-resource>\n");
+                    hasErrors = localHasErrors = true;
+                }
+                typeName = String16(block.getAttributeStringValue(typeIdx, &len));
+
+                String16 name;
+                ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
+                if (nameIdx < 0) {
+                    srcPos.error("A 'name' attribute is required for <add-resource>\n");
+                    hasErrors = localHasErrors = true;
+                }
+                name = String16(block.getAttributeStringValue(nameIdx, &len));
+
+                outTable->canAddEntry(srcPos, myPackage, typeName, name);
+
+                while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+                    if (code == ResXMLTree::END_TAG) {
+                        if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) {
+                            break;
+                        }
+                    }
+                }
+                continue;
+                
             } else if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
                 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
                                 
@@ -975,6 +1259,7 @@ status_t compileResourceFile(Bundle* bundle,
                 String8 locale(rawLocale);
                 String16 name;
                 String16 translatable;
+                String16 formatted;
 
                 size_t n = block.getAttributeCount();
                 for (size_t i = 0; i < n; i++) {
@@ -984,11 +1269,14 @@ status_t compileResourceFile(Bundle* bundle,
                         name.setTo(block.getAttributeStringValue(i, &length));
                     } else if (strcmp16(attr, translatable16.string()) == 0) {
                         translatable.setTo(block.getAttributeStringValue(i, &length));
+                    } else if (strcmp16(attr, formatted16.string()) == 0) {
+                        formatted.setTo(block.getAttributeStringValue(i, &length));
                     }
                 }
                 
                 if (name.size() > 0) {
                     if (translatable == false16) {
+                        curIsFormatted = false;
                         // Untranslatable strings must only exist in the default [empty] locale
                         if (locale.size() > 0) {
                             fprintf(stderr, "aapt: warning: string '%s' in %s marked untranslatable but exists"
@@ -1006,6 +1294,10 @@ status_t compileResourceFile(Bundle* bundle,
                     } else {
                         outTable->addLocalization(name, locale);
                     }
+
+                    if (formatted == false16) {
+                        curIsFormatted = false;
+                    }
                 }
 
                 curTag = &string16;
@@ -1060,6 +1352,7 @@ status_t compileResourceFile(Bundle* bundle,
                 curTag = &array16;
                 curType = array16;
                 curIsBag = true;
+                curIsBagReplaceOnOverwrite = true;
                 ssize_t formatIdx = block.indexOfAttribute(NULL, "format");
                 if (formatIdx >= 0) {
                     String16 formatStr = String16(block.getAttributeStringValue(
@@ -1074,16 +1367,34 @@ status_t compileResourceFile(Bundle* bundle,
                     }
                 }
             } else if (strcmp16(block.getElementName(&len), string_array16.string()) == 0) {
+                // Check whether these strings need valid formats.
+                // (simplified form of what string16 does above)
+                size_t n = block.getAttributeCount();
+                for (size_t i = 0; i < n; i++) {
+                    size_t length;
+                    const uint16_t* attr = block.getAttributeName(i, &length);
+                    if (strcmp16(attr, translatable16.string()) == 0
+                            || strcmp16(attr, formatted16.string()) == 0) {
+                        const uint16_t* value = block.getAttributeStringValue(i, &length);
+                        if (strcmp16(value, false16.string()) == 0) {
+                            curIsFormatted = false;
+                            break;
+                        }
+                    }
+                }
+
                 curTag = &string_array16;
                 curType = array16;
                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
                 curIsBag = true;
+                curIsBagReplaceOnOverwrite = true;
                 curIsPseudolocalizable = true;
             } else if (strcmp16(block.getElementName(&len), integer_array16.string()) == 0) {
                 curTag = &integer_array16;
                 curType = array16;
                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
                 curIsBag = true;
+                curIsBagReplaceOnOverwrite = true;
             } else {
                 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
                         "Found tag %s where item is expected\n",
@@ -1102,6 +1413,12 @@ status_t compileResourceFile(Bundle* bundle,
                 hasErrors = localHasErrors = true;
             }
 
+            String16 product;
+            identIdx = block.indexOfAttribute(NULL, "product");
+            if (identIdx >= 0) {
+                product = String16(block.getAttributeStringValue(identIdx, &len));
+            }
+
             String16 comment(block.getComment(&len) ? block.getComment(&len) : nulStr);
             
             if (curIsBag) {
@@ -1118,9 +1435,10 @@ status_t compileResourceFile(Bundle* bundle,
                 }
 
                 if (!localHasErrors) {
-                    err = outTable->startBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
-                                             myPackage, curType, ident, parentIdent, &curParams, 
-                                             overwrite);
+                    err = outTable->startBag(SourcePos(in->getPrintableSource(),
+                            block.getLineNumber()), myPackage, curType, ident,
+                            parentIdent, &curParams,
+                            overwrite, curIsBagReplaceOnOverwrite);
                     if (err != NO_ERROR) {
                         hasErrors = localHasErrors = true;
                     }
@@ -1191,8 +1509,8 @@ status_t compileResourceFile(Bundle* bundle,
                         block.getPosition(&parserPosition);
 
                         err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType,
-                                ident, parentIdent, itemIdent, curFormat, 
-                                false, overwrite, outTable);
+                                ident, parentIdent, itemIdent, curFormat, curIsFormatted,
+                                product, false, overwrite, outTable);
                         if (err == NO_ERROR) {
                             if (curIsPseudolocalizable && localeIsDefined(curParams)
                                     && bundle->getPseudolocalize()) {
@@ -1200,8 +1518,8 @@ status_t compileResourceFile(Bundle* bundle,
 #if 1
                                 block.setPosition(parserPosition);
                                 err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage,
-                                        curType, ident, parentIdent, itemIdent, curFormat, true,
-                                        overwrite, outTable);
+                                        curType, ident, parentIdent, itemIdent, curFormat,
+                                        curIsFormatted, product, true, overwrite, outTable);
 #endif
                             }
                         } 
@@ -1224,7 +1542,8 @@ status_t compileResourceFile(Bundle* bundle,
                 block.getPosition(&parserPosition);
 
                 err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident,
-                        *curTag, curIsStyled, curFormat, false, overwrite, outTable);
+                        *curTag, curIsStyled, curFormat, curIsFormatted,
+                        product, false, overwrite, outTable);
 
                 if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR?
                     hasErrors = localHasErrors = true;
@@ -1235,7 +1554,9 @@ status_t compileResourceFile(Bundle* bundle,
                         // pseudolocalize here
                         block.setPosition(parserPosition);
                         err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType,
-                                ident, *curTag, curIsStyled, curFormat, true, overwrite, outTable);
+                                ident, *curTag, curIsStyled, curFormat,
+                                curIsFormatted, product,
+                                true, overwrite, outTable);
                         if (err != NO_ERROR) {
                             hasErrors = localHasErrors = true;
                         }
@@ -1400,8 +1721,9 @@ status_t ResourceTable::addEntry(const SourcePos& sourcePos,
                String8(value).string());
     }
 #endif
-    
-    sp<Entry> e = getEntry(package, type, name, sourcePos, params, doSetIndex);
+
+    sp<Entry> e = getEntry(package, type, name, sourcePos, overwrite,
+                           params, doSetIndex);
     if (e == NULL) {
         return UNKNOWN_ERROR;
     }
@@ -1418,6 +1740,7 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos,
                                  const String16& name,
                                  const String16& bagParent,
                                  const ResTable_config* params,
+                                 bool overlay,
                                  bool replace, bool isId)
 {
     status_t result = NO_ERROR;
@@ -1438,21 +1761,30 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos,
                sourcePos.file.striing(), sourcePos.line, String8(type).string());
     }
 #endif
-    
-    sp<Entry> e = getEntry(package, type, name, sourcePos, params);
+    if (overlay && !mBundle->getAutoAddOverlay() && !hasBagOrEntry(package, type, name)) {
+        bool canAdd = false;
+        sp<Package> p = mPackages.valueFor(package);
+        if (p != NULL) {
+            sp<Type> t = p->getTypes().valueFor(type);
+            if (t != NULL) {
+                if (t->getCanAddEntries().indexOf(name) >= 0) {
+                    canAdd = true;
+                }
+            }
+        }
+        if (!canAdd) {
+            sourcePos.error("Resource does not already exist in overlay at '%s'; use <add-resource> to add.\n",
+                            String8(name).string());
+            return UNKNOWN_ERROR;
+        }
+    }
+    sp<Entry> e = getEntry(package, type, name, sourcePos, overlay, params);
     if (e == NULL) {
         return UNKNOWN_ERROR;
     }
     
     // If a parent is explicitly specified, set it.
     if (bagParent.size() > 0) {
-        String16 curPar = e->getParent();
-        if (curPar.size() > 0 && curPar != bagParent) {
-            sourcePos.error("Conflicting parents specified, was '%s', now '%s'\n",
-                            String8(e->getParent()).string(),
-                            String8(bagParent).string());
-            return UNKNOWN_ERROR;
-        }
         e->setParent(bagParent);
     }
 
@@ -1460,7 +1792,7 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos,
         return result;
     }
 
-    if (replace) { 
+    if (overlay && replace) { 
         return e->emptyBag(sourcePos);
     }
     return result;
@@ -1493,21 +1825,13 @@ status_t ResourceTable::addBag(const SourcePos& sourcePos,
                sourcePos.file.striing(), sourcePos.line, String8(type).string());
     }
 #endif
-    
-    sp<Entry> e = getEntry(package, type, name, sourcePos, params);
+    sp<Entry> e = getEntry(package, type, name, sourcePos, replace, params);
     if (e == NULL) {
         return UNKNOWN_ERROR;
     }
 
     // If a parent is explicitly specified, set it.
     if (bagParent.size() > 0) {
-        String16 curPar = e->getParent();
-        if (curPar.size() > 0 && curPar != bagParent) {
-            sourcePos.error("Conflicting parents specified, was '%s', now '%s'\n",
-                    String8(e->getParent()).string(),
-                    String8(bagParent).string());
-            return UNKNOWN_ERROR;
-        }
         e->setParent(bagParent);
     }
 
@@ -1544,6 +1868,37 @@ bool ResourceTable::hasBagOrEntry(const String16& package,
     return false;
 }
 
+bool ResourceTable::hasBagOrEntry(const String16& package,
+                                  const String16& type,
+                                  const String16& name,
+                                  const ResTable_config& config) const
+{
+    // First look for this in the included resources...
+    uint32_t rid = mAssets->getIncludedResources()
+        .identifierForName(name.string(), name.size(),
+                           type.string(), type.size(),
+                           package.string(), package.size());
+    if (rid != 0) {
+        return true;
+    }
+
+    sp<Package> p = mPackages.valueFor(package);
+    if (p != NULL) {
+        sp<Type> t = p->getTypes().valueFor(type);
+        if (t != NULL) {
+            sp<ConfigList> c =  t->getConfigs().valueFor(name);
+            if (c != NULL) {
+                sp<Entry> e = c->getEntries().valueFor(config);
+                if (e != NULL) {
+                    return true;
+                }
+            }
+        }
+    }
+
+    return false;
+}
+
 bool ResourceTable::hasBagOrEntry(const String16& ref,
                                   const String16* defType,
                                   const String16* defPackage)
@@ -1603,6 +1958,15 @@ bool ResourceTable::appendTypeComment(const String16& package,
     return false;
 }
 
+void ResourceTable::canAddEntry(const SourcePos& pos,
+        const String16& package, const String16& type, const String16& name)
+{
+    sp<Type> t = getType(package, type, pos);
+    if (t != NULL) {
+        t->canAddEntry(name);
+    }
+}
+
 size_t ResourceTable::size() const {
     return mPackages.size();
 }
@@ -1675,10 +2039,11 @@ uint32_t ResourceTable::getResId(const String16& ref,
                                  bool onlyPublic) const
 {
     String16 package, type, name;
+    bool refOnlyPublic = true;
     if (!ResTable::expandResourceRef(
         ref.string(), ref.size(), &package, &type, &name,
         defType, defPackage ? defPackage:&mAssetsPackage,
-        outErrorMsg)) {
+        outErrorMsg, &refOnlyPublic)) {
         NOISY(printf("Expanding resource: ref=%s\n",
                      String8(ref).string()));
         NOISY(printf("Expanding resource: defType=%s\n",
@@ -1691,7 +2056,7 @@ uint32_t ResourceTable::getResId(const String16& ref,
                      String8(name).string()));
         return 0;
     }
-    uint32_t res = getResId(package, type, name, onlyPublic);
+    uint32_t res = getResId(package, type, name, onlyPublic && refOnlyPublic);
     NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n",
                  String8(package).string(), String8(type).string(),
                  String8(name).string(), res));
@@ -1726,7 +2091,8 @@ bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool,
                                   uint32_t attrID,
                                   const Vector<StringPool::entry_style_span>* style,
                                   String16* outStr, void* accessorCookie,
-                                  uint32_t attrType)
+                                  uint32_t attrType, const String8* configTypeName,
+                                  const ConfigDescription* config)
 {
     String16 finalStr;
 
@@ -1754,10 +2120,19 @@ bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool,
     if (outValue->dataType == outValue->TYPE_STRING) {
         // Should do better merging styles.
         if (pool) {
+            String8 configStr;
+            if (config != NULL) {
+                configStr = config->toString();
+            } else {
+                configStr = "(null)";
+            }
+            NOISY(printf("Adding to pool string style #%d config %s: %s\n",
+                    style != NULL ? style->size() : 0,
+                    configStr.string(), String8(finalStr).string()));
             if (style != NULL && style->size() > 0) {
-                outValue->data = pool->add(finalStr, *style);
+                outValue->data = pool->add(finalStr, *style, configTypeName, config);
             } else {
-                outValue->data = pool->add(finalStr, true);
+                outValue->data = pool->add(finalStr, true, configTypeName, config);
             }
         } else {
             // Caller will fill this in later.
@@ -1929,10 +2304,8 @@ bool ResourceTable::getAttributeFlags(
 
         const char16_t* end = name + nameLen;
         const char16_t* pos = name;
-        bool failed = false;
-        while (pos < end && !failed) {
+        while (pos < end) {
             const char16_t* start = pos;
-            end++;
             while (pos < end && *pos != '|') {
                 pos++;
             }
@@ -1958,9 +2331,7 @@ bool ResourceTable::getAttributeFlags(
                 // Didn't find this flag identifier.
                 return false;
             }
-            if (pos < end) {
-                pos++;
-            }
+            pos++;
         }
 
         return true;
@@ -2156,7 +2527,7 @@ ResourceTable::validateLocalizations(void)
         if (configSet.count(defaultLocale) == 0) {
             fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:",
                     String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]);
-            for (set<String8>::iterator locales = configSet.begin();
+            for (set<String8>::const_iterator locales = configSet.begin();
                  locales != configSet.end();
                  locales++) {
                 fprintf(stdout, " %s", (*locales).string());
@@ -2191,13 +2562,12 @@ ResourceTable::validateLocalizations(void)
                         String8 region(config.string(), 2);
                         if (configSet.find(region) == configSet.end()) {
                             if (configSet.count(defaultLocale) == 0) {
-                                fprintf(stdout, "aapt: error: "
-                                        "*** string '%s' has no default or required localization "
+                                fprintf(stdout, "aapt: warning: "
+                                        "**** string '%s' has no default or required localization "
                                         "for '%s' in %s\n",
                                         String8(nameIter->first).string(),
                                         config.string(),
                                         mBundle->getResourceSourceDirs()[0]);
-                                err = UNKNOWN_ERROR;
                             }
                         }
                     }
@@ -2209,117 +2579,6 @@ ResourceTable::validateLocalizations(void)
     return err;
 }
 
-
-status_t
-ResourceFilter::parse(const char* arg)
-{
-    if (arg == NULL) {
-        return 0;
-    }
-
-    const char* p = arg;
-    const char* q;
-
-    while (true) {
-        q = strchr(p, ',');
-        if (q == NULL) {
-            q = p + strlen(p);
-        }
-
-        String8 part(p, q-p);
-
-        if (part == "zz_ZZ") {
-            mContainsPseudo = true;
-        }
-        int axis;
-        uint32_t value;
-        if (AaptGroupEntry::parseNamePart(part, &axis, &value)) {
-            fprintf(stderr, "Invalid configuration: %s\n", arg);
-            fprintf(stderr, "                       ");
-            for (int i=0; i<p-arg; i++) {
-                fprintf(stderr, " ");
-            }
-            for (int i=0; i<q-p; i++) {
-                fprintf(stderr, "^");
-            }
-            fprintf(stderr, "\n");
-            return 1;
-        }
-
-        ssize_t index = mData.indexOfKey(axis);
-        if (index < 0) {
-            mData.add(axis, SortedVector<uint32_t>());
-        }
-        SortedVector<uint32_t>& sv = mData.editValueFor(axis);
-        sv.add(value);
-        // if it's a locale with a region, also match an unmodified locale of the
-        // same language
-        if (axis == AXIS_LANGUAGE) {
-            if (value & 0xffff0000) {
-                sv.add(value & 0x0000ffff);
-            }
-        }
-        p = q;
-        if (!*p) break;
-        p++;
-    }
-
-    return NO_ERROR;
-}
-
-bool
-ResourceFilter::match(int axis, uint32_t value)
-{
-    if (value == 0) {
-        // they didn't specify anything so take everything
-        return true;
-    }
-    ssize_t index = mData.indexOfKey(axis);
-    if (index < 0) {
-        // we didn't request anything on this axis so take everything
-        return true;
-    }
-    const SortedVector<uint32_t>& sv = mData.valueAt(index);
-    return sv.indexOf(value) >= 0;
-}
-
-bool
-ResourceFilter::match(const ResTable_config& config)
-{
-    if (config.locale) {
-        uint32_t locale = (config.country[1] << 24) | (config.country[0] << 16)
-                | (config.language[1] << 8) | (config.language[0]);
-        if (!match(AXIS_LANGUAGE, locale)) {
-            return false;
-        }
-    }
-    if (!match(AXIS_ORIENTATION, config.orientation)) {
-        return false;
-    }
-    if (!match(AXIS_DENSITY, config.density)) {
-        return false;
-    }
-    if (!match(AXIS_TOUCHSCREEN, config.touchscreen)) {
-        return false;
-    }
-    if (!match(AXIS_KEYSHIDDEN, config.inputFlags)) {
-        return false;
-    }
-    if (!match(AXIS_KEYBOARD, config.keyboard)) {
-        return false;
-    }
-    if (!match(AXIS_NAVIGATION, config.navigation)) {
-        return false;
-    }
-    if (!match(AXIS_SCREENSIZE, config.screenSize)) {
-        return false;
-    }
-    if (!match(AXIS_VERSION, config.version)) {
-        return false;
-    }
-    return true;
-}
-
 status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
 {
     ResourceFilter filter;
@@ -2328,12 +2587,19 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
         return err;
     }
 
+    const ConfigDescription nullConfig;
+
     const size_t N = mOrderedPackages.size();
     size_t pi;
 
+    const static String16 mipmap16("mipmap");
+
+    bool useUTF8 = !bundle->getUTF16StringsOption();
+
     // Iterate through all data, collecting all values (strings,
     // references, etc).
-    StringPool valueStrings;
+    StringPool valueStrings(useUTF8);
+    Vector<sp<Entry> > allEntries;
     for (pi=0; pi<N; pi++) {
         sp<Package> p = mOrderedPackages.itemAt(pi);
         if (p->getTypes().size() == 0) {
@@ -2341,8 +2607,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
             continue;
         }
 
-        StringPool typeStrings;
-        StringPool keyStrings;
+        StringPool typeStrings(useUTF8);
+        StringPool keyStrings(useUTF8);
 
         const size_t N = p->getOrderedTypes().size();
         for (size_t ti=0; ti<N; ti++) {
@@ -2351,7 +2617,23 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                 typeStrings.add(String16("<empty>"), false);
                 continue;
             }
-            typeStrings.add(t->getName(), false);
+            const String16 typeName(t->getName());
+            typeStrings.add(typeName, false);
+
+            // This is a hack to tweak the sorting order of the final strings,
+            // to put stuff that is generally not language-specific first.
+            String8 configTypeName(typeName);
+            if (configTypeName == "drawable" || configTypeName == "layout"
+                    || configTypeName == "color" || configTypeName == "anim"
+                    || configTypeName == "interpolator" || configTypeName == "animator"
+                    || configTypeName == "xml" || configTypeName == "menu"
+                    || configTypeName == "mipmap" || configTypeName == "raw") {
+                configTypeName = "1complex";
+            } else {
+                configTypeName = "2value";
+            }
+
+            const bool filterable = (typeName != mipmap16);
 
             const size_t N = t->getOrderedConfigs().size();
             for (size_t ci=0; ci<N; ci++) {
@@ -2362,7 +2644,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                 const size_t N = c->getEntries().size();
                 for (size_t ei=0; ei<N; ei++) {
                     ConfigDescription config = c->getEntries().keyAt(ei);
-                    if (!filter.match(config)) {
+                    if (filterable && !filter.match(config)) {
                         continue;
                     }
                     sp<Entry> e = c->getEntries().valueAt(ei);
@@ -2370,10 +2652,21 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                         continue;
                     }
                     e->setNameIndex(keyStrings.add(e->getName(), true));
-                    status_t err = e->prepareFlatten(&valueStrings, this);
+
+                    // If this entry has no values for other configs,
+                    // and is the default config, then it is special.  Otherwise
+                    // we want to add it with the config info.
+                    ConfigDescription* valueConfig = NULL;
+                    if (N != 1 || config == nullConfig) {
+                        valueConfig = &config;
+                    }
+
+                    status_t err = e->prepareFlatten(&valueStrings, this,
+                            &configTypeName, &config);
                     if (err != NO_ERROR) {
                         return err;
                     }
+                    allEntries.add(e);
                 }
             }
         }
@@ -2382,6 +2675,17 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
         p->setKeyStrings(keyStrings.createStringBlock());
     }
 
+    if (bundle->getOutputAPKFile() != NULL) {
+        // Now we want to sort the value strings for better locality.  This will
+        // cause the positions of the strings to change, so we need to go back
+        // through out resource entries and update them accordingly.  Only need
+        // to do this if actually writing the output file.
+        valueStrings.sortByConfig();
+        for (pi=0; pi<allEntries.size(); pi++) {
+            allEntries[pi]->remapStringValue(&valueStrings);
+        }
+    }
+
     ssize_t strAmt = 0;
     
     // Now build the array of package chunks.
@@ -2442,6 +2746,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                                 "Type name %s not found",
                                 String8(typeName).string());
 
+            const bool filterable = (typeName != mipmap16);
+
             const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
             
             // First write the typeSpec chunk, containing information about
@@ -2466,7 +2772,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                     (((uint8_t*)data->editData())
                         + typeSpecStart + sizeof(ResTable_typeSpec));
                 memset(typeSpecFlags, 0, sizeof(uint32_t)*N);
-                        
+
                 for (size_t ei=0; ei<N; ei++) {
                     sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
                     if (cl->getPublic()) {
@@ -2474,11 +2780,11 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                     }
                     const size_t CN = cl->getEntries().size();
                     for (size_t ci=0; ci<CN; ci++) {
-                        if (!filter.match(cl->getEntries().keyAt(ci))) {
+                        if (filterable && !filter.match(cl->getEntries().keyAt(ci))) {
                             continue;
                         }
                         for (size_t cj=ci+1; cj<CN; cj++) {
-                            if (!filter.match(cl->getEntries().keyAt(cj))) {
+                            if (filterable && !filter.match(cl->getEntries().keyAt(cj))) {
                                 continue;
                             }
                             typeSpecFlags[ei] |= htodl(
@@ -2498,7 +2804,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                 ConfigDescription config = t->getUniqueConfigs().itemAt(ci);
 
                 NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
-                     "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
+                     "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
+                     "sw%ddp w%ddp h%ddp\n",
                       ti+1,
                       config.mcc, config.mnc,
                       config.language[0] ? config.language[0] : '-',
@@ -2506,15 +2813,19 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                       config.country[0] ? config.country[0] : '-',
                       config.country[1] ? config.country[1] : '-',
                       config.orientation,
+                      config.uiMode,
                       config.touchscreen,
                       config.density,
                       config.keyboard,
                       config.inputFlags,
                       config.navigation,
                       config.screenWidth,
-                      config.screenHeight));
+                      config.screenHeight,
+                      config.smallestScreenWidthDp,
+                      config.screenWidthDp,
+                      config.screenHeightDp));
                       
-                if (!filter.match(config)) {
+                if (filterable && !filter.match(config)) {
                     continue;
                 }
                 
@@ -2535,7 +2846,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                 tHeader->entriesStart = htodl(typeSize);
                 tHeader->config = config;
                 NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
-                     "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
+                     "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
+                     "sw%ddp w%ddp h%ddp\n",
                       ti+1,
                       tHeader->config.mcc, tHeader->config.mnc,
                       tHeader->config.language[0] ? tHeader->config.language[0] : '-',
@@ -2543,13 +2855,17 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                       tHeader->config.country[0] ? tHeader->config.country[0] : '-',
                       tHeader->config.country[1] ? tHeader->config.country[1] : '-',
                       tHeader->config.orientation,
+                      tHeader->config.uiMode,
                       tHeader->config.touchscreen,
                       tHeader->config.density,
                       tHeader->config.keyboard,
                       tHeader->config.inputFlags,
                       tHeader->config.navigation,
                       tHeader->config.screenWidth,
-                      tHeader->config.screenHeight));
+                      tHeader->config.screenHeight,
+                      tHeader->config.smallestScreenWidthDp,
+                      tHeader->config.screenWidthDp,
+                      tHeader->config.screenHeightDp));
                 tHeader->config.swapHtoD();
 
                 // Build the entries inside of this type.
@@ -2777,7 +3093,7 @@ status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos,
                         mItem.sourcePos.file.string(), mItem.sourcePos.line);
         return UNKNOWN_ERROR;
     }
-    
+
     mType = TYPE_ITEM;
     mItem = item;
     mItemFormat = format;
@@ -2909,14 +3225,16 @@ status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table,
     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
 }
 
-status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table)
+status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table,
+        const String8* configTypeName, const ConfigDescription* config)
 {
     if (mType == TYPE_ITEM) {
         Item& it = mItem;
         AccessorCookie ac(it.sourcePos, String8(mName), String8(it.value));
         if (!table->stringToValue(&it.parsedValue, strings,
                                   it.value, false, true, 0,
-                                  &it.style, NULL, &ac, mItemFormat)) {
+                                  &it.style, NULL, &ac, mItemFormat,
+                                  configTypeName, config)) {
             return UNKNOWN_ERROR;
         }
     } else if (mType == TYPE_BAG) {
@@ -2927,7 +3245,8 @@ status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable
             AccessorCookie ac(it.sourcePos, String8(key), String8(it.value));
             if (!table->stringToValue(&it.parsedValue, strings,
                                       it.value, false, true, it.bagKeyId,
-                                      &it.style, NULL, &ac, it.format)) {
+                                      &it.style, NULL, &ac, it.format,
+                                      configTypeName, config)) {
                 return UNKNOWN_ERROR;
             }
         }
@@ -2939,6 +3258,29 @@ status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable
     return NO_ERROR;
 }
 
+status_t ResourceTable::Entry::remapStringValue(StringPool* strings)
+{
+    if (mType == TYPE_ITEM) {
+        Item& it = mItem;
+        if (it.parsedValue.dataType == Res_value::TYPE_STRING) {
+            it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data);
+        }
+    } else if (mType == TYPE_BAG) {
+        const size_t N = mBag.size();
+        for (size_t i=0; i<N; i++) {
+            Item& it = mBag.editValueAt(i);
+            if (it.parsedValue.dataType == Res_value::TYPE_STRING) {
+                it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data);
+            }
+        }
+    } else {
+        mPos.error("Error: entry %s is not a single item or a bag.\n",
+                   String8(mName).string());
+        return UNKNOWN_ERROR;
+    }
+    return NO_ERROR;
+}
+
 ssize_t ResourceTable::Entry::flatten(Bundle* bundle, const sp<AaptFile>& data, bool isPublic)
 {
     size_t amt = 0;
@@ -3094,14 +3436,27 @@ status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos,
     return NO_ERROR;
 }
 
+void ResourceTable::Type::canAddEntry(const String16& name)
+{
+    mCanAddEntries.add(name);
+}
+
 sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
                                                        const SourcePos& sourcePos,
                                                        const ResTable_config* config,
-                                                       bool doSetIndex)
+                                                       bool doSetIndex,
+                                                       bool overlay,
+                                                       bool autoAddOverlay)
 {
     int pos = -1;
     sp<ConfigList> c = mConfigs.valueFor(entry);
     if (c == NULL) {
+        if (overlay && !autoAddOverlay && mCanAddEntries.indexOf(entry) < 0) {
+            sourcePos.error("Resource at %s appears in overlay but not"
+                            " in the base package; use <add-resource> to add.\n",
+                            String8(entry).string());
+            return NULL;
+        }
         c = new ConfigList(entry, sourcePos);
         mConfigs.add(entry, c);
         pos = (int)mOrderedConfigs.size();
@@ -3118,7 +3473,8 @@ sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
     if (e == NULL) {
         if (config != NULL) {
             NOISY(printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
-                    "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
+                    "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
+                    "sw%ddp w%ddp h%ddp\n",
                       sourcePos.file.string(), sourcePos.line,
                       config->mcc, config->mnc,
                       config->language[0] ? config->language[0] : '-',
@@ -3132,7 +3488,10 @@ sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
                       config->inputFlags,
                       config->navigation,
                       config->screenWidth,
-                      config->screenHeight));
+                      config->screenHeight,
+                      config->smallestScreenWidthDp,
+                      config->screenWidthDp,
+                      config->screenHeightDp));
         } else {
             NOISY(printf("New entry at %s:%d: NULL config\n",
                       sourcePos.file.string(), sourcePos.line));
@@ -3400,6 +3759,7 @@ sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package,
                                                  const String16& type,
                                                  const String16& name,
                                                  const SourcePos& sourcePos,
+                                                 bool overlay,
                                                  const ResTable_config* config,
                                                  bool doSetIndex)
 {
@@ -3407,7 +3767,7 @@ sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package,
     if (t == NULL) {
         return NULL;
     }
-    return t->getEntry(name, sourcePos, config, doSetIndex);
+    return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay());
 }
 
 sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
@@ -3426,26 +3786,26 @@ sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
 
     }
     if (p == NULL) {
-        fprintf(stderr, "WARNING: Package not found for resource #%08x\n", resID);
+        fprintf(stderr, "warning: Package not found for resource #%08x\n", resID);
         return NULL;
     }
 
     int tid = Res_GETTYPE(resID);
     if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) {
-        fprintf(stderr, "WARNING: Type not found for resource #%08x\n", resID);
+        fprintf(stderr, "warning: Type not found for resource #%08x\n", resID);
         return NULL;
     }
     sp<Type> t = p->getOrderedTypes()[tid];
 
     int eid = Res_GETENTRY(resID);
     if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) {
-        fprintf(stderr, "WARNING: Entry not found for resource #%08x\n", resID);
+        fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
         return NULL;
     }
 
     sp<ConfigList> c = t->getOrderedConfigs()[eid];
     if (c == NULL) {
-        fprintf(stderr, "WARNING: Entry not found for resource #%08x\n", resID);
+        fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
         return NULL;
     }
     
@@ -3453,7 +3813,7 @@ sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
     if (config) cdesc = *config;
     sp<Entry> e = c->getEntries().valueFor(cdesc);
     if (c == NULL) {
-        fprintf(stderr, "WARNING: Entry configuration not found for resource #%08x\n", resID);
+        fprintf(stderr, "warning: Entry configuration not found for resource #%08x\n", resID);
         return NULL;
     }
     
@@ -3471,7 +3831,7 @@ const ResourceTable::Item* ResourceTable::getItem(uint32_t resID, uint32_t attrI
     for (size_t i=0; i<N; i++) {
         const Item& it = e->getBag().valueAt(i);
         if (it.bagKeyId == 0) {
-            fprintf(stderr, "WARNING: ID not yet assigned to '%s' in bag '%s'\n",
+            fprintf(stderr, "warning: ID not yet assigned to '%s' in bag '%s'\n",
                     String8(e->getName()).string(),
                     String8(e->getBag().keyAt(i)).string());
         }
@@ -3499,7 +3859,7 @@ bool ResourceTable::getItemValue(
                     break;
                 }
             }
-            fprintf(stderr, "WARNING: Circular reference detected in key '%s' of bag '%s'\n",
+            fprintf(stderr, "warning: Circular reference detected in key '%s' of bag '%s'\n",
                     String8(e->getName()).string(),
                     String8(e->getBag().keyAt(i)).string());
             return false;