Home | History | Annotate | Download | only in aapt
      1 //
      2 // Copyright 2006 The Android Open Source Project
      3 //
      4 // Build resource files from raw assets.
      5 //
      6 
      7 #include "ResourceTable.h"
      8 
      9 #include "AaptUtil.h"
     10 #include "XMLNode.h"
     11 #include "ResourceFilter.h"
     12 #include "ResourceIdCache.h"
     13 #include "SdkConstants.h"
     14 
     15 #include <algorithm>
     16 #include <androidfw/ResourceTypes.h>
     17 #include <utils/ByteOrder.h>
     18 #include <utils/TypeHelpers.h>
     19 #include <stdarg.h>
     20 
     21 // SSIZE: mingw does not have signed size_t == ssize_t.
     22 // STATUST: mingw does seem to redefine UNKNOWN_ERROR from our enum value, so a cast is necessary.
     23 #if !defined(_WIN32)
     24 #  define SSIZE(x) x
     25 #  define STATUST(x) x
     26 #else
     27 #  define SSIZE(x) (signed size_t)x
     28 #  define STATUST(x) (status_t)x
     29 #endif
     30 
     31 // Set to true for noisy debug output.
     32 static const bool kIsDebug = false;
     33 
     34 #if PRINT_STRING_METRICS
     35 static const bool kPrintStringMetrics = true;
     36 #else
     37 static const bool kPrintStringMetrics = false;
     38 #endif
     39 
     40 static const char* kAttrPrivateType = "^attr-private";
     41 
     42 status_t compileXmlFile(const Bundle* bundle,
     43                         const sp<AaptAssets>& assets,
     44                         const String16& resourceName,
     45                         const sp<AaptFile>& target,
     46                         ResourceTable* table,
     47                         int options)
     48 {
     49     sp<XMLNode> root = XMLNode::parse(target);
     50     if (root == NULL) {
     51         return UNKNOWN_ERROR;
     52     }
     53 
     54     return compileXmlFile(bundle, assets, resourceName, root, target, table, options);
     55 }
     56 
     57 status_t compileXmlFile(const Bundle* bundle,
     58                         const sp<AaptAssets>& assets,
     59                         const String16& resourceName,
     60                         const sp<AaptFile>& target,
     61                         const sp<AaptFile>& outTarget,
     62                         ResourceTable* table,
     63                         int options)
     64 {
     65     sp<XMLNode> root = XMLNode::parse(target);
     66     if (root == NULL) {
     67         return UNKNOWN_ERROR;
     68     }
     69 
     70     return compileXmlFile(bundle, assets, resourceName, root, outTarget, table, options);
     71 }
     72 
     73 status_t compileXmlFile(const Bundle* bundle,
     74                         const sp<AaptAssets>& assets,
     75                         const String16& resourceName,
     76                         const sp<XMLNode>& root,
     77                         const sp<AaptFile>& target,
     78                         ResourceTable* table,
     79                         int options)
     80 {
     81     if ((options&XML_COMPILE_STRIP_WHITESPACE) != 0) {
     82         root->removeWhitespace(true, NULL);
     83     } else  if ((options&XML_COMPILE_COMPACT_WHITESPACE) != 0) {
     84         root->removeWhitespace(false, NULL);
     85     }
     86 
     87     if ((options&XML_COMPILE_UTF8) != 0) {
     88         root->setUTF8(true);
     89     }
     90 
     91     bool hasErrors = false;
     92 
     93     if ((options&XML_COMPILE_ASSIGN_ATTRIBUTE_IDS) != 0) {
     94         status_t err = root->assignResourceIds(assets, table);
     95         if (err != NO_ERROR) {
     96             hasErrors = true;
     97         }
     98     }
     99 
    100     status_t err = root->parseValues(assets, table);
    101     if (err != NO_ERROR) {
    102         hasErrors = true;
    103     }
    104 
    105     if (hasErrors) {
    106         return UNKNOWN_ERROR;
    107     }
    108 
    109     if (table->modifyForCompat(bundle, resourceName, target, root) != NO_ERROR) {
    110         return UNKNOWN_ERROR;
    111     }
    112 
    113     if (kIsDebug) {
    114         printf("Input XML Resource:\n");
    115         root->print();
    116     }
    117     err = root->flatten(target,
    118             (options&XML_COMPILE_STRIP_COMMENTS) != 0,
    119             (options&XML_COMPILE_STRIP_RAW_VALUES) != 0);
    120     if (err != NO_ERROR) {
    121         return err;
    122     }
    123 
    124     if (kIsDebug) {
    125         printf("Output XML Resource:\n");
    126         ResXMLTree tree;
    127         tree.setTo(target->getData(), target->getSize());
    128         printXMLBlock(&tree);
    129     }
    130 
    131     target->setCompressionMethod(ZipEntry::kCompressDeflated);
    132 
    133     return err;
    134 }
    135 
    136 struct flag_entry
    137 {
    138     const char16_t* name;
    139     size_t nameLen;
    140     uint32_t value;
    141     const char* description;
    142 };
    143 
    144 static const char16_t referenceArray[] =
    145     { 'r', 'e', 'f', 'e', 'r', 'e', 'n', 'c', 'e' };
    146 static const char16_t stringArray[] =
    147     { 's', 't', 'r', 'i', 'n', 'g' };
    148 static const char16_t integerArray[] =
    149     { 'i', 'n', 't', 'e', 'g', 'e', 'r' };
    150 static const char16_t booleanArray[] =
    151     { 'b', 'o', 'o', 'l', 'e', 'a', 'n' };
    152 static const char16_t colorArray[] =
    153     { 'c', 'o', 'l', 'o', 'r' };
    154 static const char16_t floatArray[] =
    155     { 'f', 'l', 'o', 'a', 't' };
    156 static const char16_t dimensionArray[] =
    157     { 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n' };
    158 static const char16_t fractionArray[] =
    159     { 'f', 'r', 'a', 'c', 't', 'i', 'o', 'n' };
    160 static const char16_t enumArray[] =
    161     { 'e', 'n', 'u', 'm' };
    162 static const char16_t flagsArray[] =
    163     { 'f', 'l', 'a', 'g', 's' };
    164 
    165 static const flag_entry gFormatFlags[] = {
    166     { referenceArray, sizeof(referenceArray)/2, ResTable_map::TYPE_REFERENCE,
    167       "a reference to another resource, in the form \"<code>@[+][<i>package</i>:]<i>type</i>:<i>name</i></code>\"\n"
    168       "or to a theme attribute in the form \"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>\"."},
    169     { stringArray, sizeof(stringArray)/2, ResTable_map::TYPE_STRING,
    170       "a string value, using '\\\\;' to escape characters such as '\\\\n' or '\\\\uxxxx' for a unicode character." },
    171     { integerArray, sizeof(integerArray)/2, ResTable_map::TYPE_INTEGER,
    172       "an integer value, such as \"<code>100</code>\"." },
    173     { booleanArray, sizeof(booleanArray)/2, ResTable_map::TYPE_BOOLEAN,
    174       "a boolean value, either \"<code>true</code>\" or \"<code>false</code>\"." },
    175     { colorArray, sizeof(colorArray)/2, ResTable_map::TYPE_COLOR,
    176       "a color value, in the form of \"<code>#<i>rgb</i></code>\", \"<code>#<i>argb</i></code>\",\n"
    177       "\"<code>#<i>rrggbb</i></code>\", or \"<code>#<i>aarrggbb</i></code>\"." },
    178     { floatArray, sizeof(floatArray)/2, ResTable_map::TYPE_FLOAT,
    179       "a floating point value, such as \"<code>1.2</code>\"."},
    180     { dimensionArray, sizeof(dimensionArray)/2, ResTable_map::TYPE_DIMENSION,
    181       "a dimension value, which is a floating point number appended with a unit such as \"<code>14.5sp</code>\".\n"
    182       "Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),\n"
    183       "in (inches), mm (millimeters)." },
    184     { fractionArray, sizeof(fractionArray)/2, ResTable_map::TYPE_FRACTION,
    185       "a fractional value, which is a floating point number appended with either % or %p, such as \"<code>14.5%</code>\".\n"
    186       "The % suffix always means a percentage of the base size; the optional %p suffix provides a size relative to\n"
    187       "some parent container." },
    188     { enumArray, sizeof(enumArray)/2, ResTable_map::TYPE_ENUM, NULL },
    189     { flagsArray, sizeof(flagsArray)/2, ResTable_map::TYPE_FLAGS, NULL },
    190     { NULL, 0, 0, NULL }
    191 };
    192 
    193 static const char16_t suggestedArray[] = { 's', 'u', 'g', 'g', 'e', 's', 't', 'e', 'd' };
    194 
    195 static const flag_entry l10nRequiredFlags[] = {
    196     { suggestedArray, sizeof(suggestedArray)/2, ResTable_map::L10N_SUGGESTED, NULL },
    197     { NULL, 0, 0, NULL }
    198 };
    199 
    200 static const char16_t nulStr[] = { 0 };
    201 
    202 static uint32_t parse_flags(const char16_t* str, size_t len,
    203                              const flag_entry* flags, bool* outError = NULL)
    204 {
    205     while (len > 0 && isspace(*str)) {
    206         str++;
    207         len--;
    208     }
    209     while (len > 0 && isspace(str[len-1])) {
    210         len--;
    211     }
    212 
    213     const char16_t* const end = str + len;
    214     uint32_t value = 0;
    215 
    216     while (str < end) {
    217         const char16_t* div = str;
    218         while (div < end && *div != '|') {
    219             div++;
    220         }
    221 
    222         const flag_entry* cur = flags;
    223         while (cur->name) {
    224             if (strzcmp16(cur->name, cur->nameLen, str, div-str) == 0) {
    225                 value |= cur->value;
    226                 break;
    227             }
    228             cur++;
    229         }
    230 
    231         if (!cur->name) {
    232             if (outError) *outError = true;
    233             return 0;
    234         }
    235 
    236         str = div < end ? div+1 : div;
    237     }
    238 
    239     if (outError) *outError = false;
    240     return value;
    241 }
    242 
    243 static String16 mayOrMust(int type, int flags)
    244 {
    245     if ((type&(~flags)) == 0) {
    246         return String16("<p>Must");
    247     }
    248 
    249     return String16("<p>May");
    250 }
    251 
    252 static void appendTypeInfo(ResourceTable* outTable, const String16& pkg,
    253         const String16& typeName, const String16& ident, int type,
    254         const flag_entry* flags)
    255 {
    256     bool hadType = false;
    257     while (flags->name) {
    258         if ((type&flags->value) != 0 && flags->description != NULL) {
    259             String16 fullMsg(mayOrMust(type, flags->value));
    260             fullMsg.append(String16(" be "));
    261             fullMsg.append(String16(flags->description));
    262             outTable->appendTypeComment(pkg, typeName, ident, fullMsg);
    263             hadType = true;
    264         }
    265         flags++;
    266     }
    267     if (hadType && (type&ResTable_map::TYPE_REFERENCE) == 0) {
    268         outTable->appendTypeComment(pkg, typeName, ident,
    269                 String16("<p>This may also be a reference to a resource (in the form\n"
    270                          "\"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>\") or\n"
    271                          "theme attribute (in the form\n"
    272                          "\"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>\")\n"
    273                          "containing a value of this type."));
    274     }
    275 }
    276 
    277 struct PendingAttribute
    278 {
    279     const String16 myPackage;
    280     const SourcePos sourcePos;
    281     const bool appendComment;
    282     int32_t type;
    283     String16 ident;
    284     String16 comment;
    285     bool hasErrors;
    286     bool added;
    287 
    288     PendingAttribute(String16 _package, const sp<AaptFile>& in,
    289             ResXMLTree& block, bool _appendComment)
    290         : myPackage(_package)
    291         , sourcePos(in->getPrintableSource(), block.getLineNumber())
    292         , appendComment(_appendComment)
    293         , type(ResTable_map::TYPE_ANY)
    294         , hasErrors(false)
    295         , added(false)
    296     {
    297     }
    298 
    299     status_t createIfNeeded(ResourceTable* outTable)
    300     {
    301         if (added || hasErrors) {
    302             return NO_ERROR;
    303         }
    304         added = true;
    305 
    306         String16 attr16("attr");
    307 
    308         if (outTable->hasBagOrEntry(myPackage, attr16, ident)) {
    309             sourcePos.error("Attribute \"%s\" has already been defined\n",
    310                     String8(ident).string());
    311             hasErrors = true;
    312             return UNKNOWN_ERROR;
    313         }
    314 
    315         char numberStr[16];
    316         sprintf(numberStr, "%d", type);
    317         status_t err = outTable->addBag(sourcePos, myPackage,
    318                 attr16, ident, String16(""),
    319                 String16("^type"),
    320                 String16(numberStr), NULL, NULL);
    321         if (err != NO_ERROR) {
    322             hasErrors = true;
    323             return err;
    324         }
    325         outTable->appendComment(myPackage, attr16, ident, comment, appendComment);
    326         //printf("Attribute %s comment: %s\n", String8(ident).string(),
    327         //     String8(comment).string());
    328         return err;
    329     }
    330 };
    331 
    332 static status_t compileAttribute(const sp<AaptFile>& in,
    333                                  ResXMLTree& block,
    334                                  const String16& myPackage,
    335                                  ResourceTable* outTable,
    336                                  String16* outIdent = NULL,
    337                                  bool inStyleable = false)
    338 {
    339     PendingAttribute attr(myPackage, in, block, inStyleable);
    340 
    341     const String16 attr16("attr");
    342     const String16 id16("id");
    343 
    344     // Attribute type constants.
    345     const String16 enum16("enum");
    346     const String16 flag16("flag");
    347 
    348     ResXMLTree::event_code_t code;
    349     size_t len;
    350     status_t err;
    351 
    352     ssize_t identIdx = block.indexOfAttribute(NULL, "name");
    353     if (identIdx >= 0) {
    354         attr.ident = String16(block.getAttributeStringValue(identIdx, &len));
    355         if (outIdent) {
    356             *outIdent = attr.ident;
    357         }
    358     } else {
    359         attr.sourcePos.error("A 'name' attribute is required for <attr>\n");
    360         attr.hasErrors = true;
    361     }
    362 
    363     attr.comment = String16(
    364             block.getComment(&len) ? block.getComment(&len) : nulStr);
    365 
    366     ssize_t typeIdx = block.indexOfAttribute(NULL, "format");
    367     if (typeIdx >= 0) {
    368         String16 typeStr = String16(block.getAttributeStringValue(typeIdx, &len));
    369         attr.type = parse_flags(typeStr.string(), typeStr.size(), gFormatFlags);
    370         if (attr.type == 0) {
    371             attr.sourcePos.error("Tag <attr> 'format' attribute value \"%s\" not valid\n",
    372                     String8(typeStr).string());
    373             attr.hasErrors = true;
    374         }
    375         attr.createIfNeeded(outTable);
    376     } else if (!inStyleable) {
    377         // Attribute definitions outside of styleables always define the
    378         // attribute as a generic value.
    379         attr.createIfNeeded(outTable);
    380     }
    381 
    382     //printf("Attribute %s: type=0x%08x\n", String8(attr.ident).string(), attr.type);
    383 
    384     ssize_t minIdx = block.indexOfAttribute(NULL, "min");
    385     if (minIdx >= 0) {
    386         String16 val = String16(block.getAttributeStringValue(minIdx, &len));
    387         if (!ResTable::stringToInt(val.string(), val.size(), NULL)) {
    388             attr.sourcePos.error("Tag <attr> 'min' attribute must be a number, not \"%s\"\n",
    389                     String8(val).string());
    390             attr.hasErrors = true;
    391         }
    392         attr.createIfNeeded(outTable);
    393         if (!attr.hasErrors) {
    394             err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident,
    395                     String16(""), String16("^min"), String16(val), NULL, NULL);
    396             if (err != NO_ERROR) {
    397                 attr.hasErrors = true;
    398             }
    399         }
    400     }
    401 
    402     ssize_t maxIdx = block.indexOfAttribute(NULL, "max");
    403     if (maxIdx >= 0) {
    404         String16 val = String16(block.getAttributeStringValue(maxIdx, &len));
    405         if (!ResTable::stringToInt(val.string(), val.size(), NULL)) {
    406             attr.sourcePos.error("Tag <attr> 'max' attribute must be a number, not \"%s\"\n",
    407                     String8(val).string());
    408             attr.hasErrors = true;
    409         }
    410         attr.createIfNeeded(outTable);
    411         if (!attr.hasErrors) {
    412             err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident,
    413                     String16(""), String16("^max"), String16(val), NULL, NULL);
    414             attr.hasErrors = true;
    415         }
    416     }
    417 
    418     if ((minIdx >= 0 || maxIdx >= 0) && (attr.type&ResTable_map::TYPE_INTEGER) == 0) {
    419         attr.sourcePos.error("Tag <attr> must have format=integer attribute if using max or min\n");
    420         attr.hasErrors = true;
    421     }
    422 
    423     ssize_t l10nIdx = block.indexOfAttribute(NULL, "localization");
    424     if (l10nIdx >= 0) {
    425         const char16_t* str = block.getAttributeStringValue(l10nIdx, &len);
    426         bool error;
    427         uint32_t l10n_required = parse_flags(str, len, l10nRequiredFlags, &error);
    428         if (error) {
    429             attr.sourcePos.error("Tag <attr> 'localization' attribute value \"%s\" not valid\n",
    430                     String8(str).string());
    431             attr.hasErrors = true;
    432         }
    433         attr.createIfNeeded(outTable);
    434         if (!attr.hasErrors) {
    435             char buf[11];
    436             sprintf(buf, "%d", l10n_required);
    437             err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident,
    438                     String16(""), String16("^l10n"), String16(buf), NULL, NULL);
    439             if (err != NO_ERROR) {
    440                 attr.hasErrors = true;
    441             }
    442         }
    443     }
    444 
    445     String16 enumOrFlagsComment;
    446 
    447     while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
    448         if (code == ResXMLTree::START_TAG) {
    449             uint32_t localType = 0;
    450             if (strcmp16(block.getElementName(&len), enum16.string()) == 0) {
    451                 localType = ResTable_map::TYPE_ENUM;
    452             } else if (strcmp16(block.getElementName(&len), flag16.string()) == 0) {
    453                 localType = ResTable_map::TYPE_FLAGS;
    454             } else {
    455                 SourcePos(in->getPrintableSource(), block.getLineNumber())
    456                         .error("Tag <%s> can not appear inside <attr>, only <enum> or <flag>\n",
    457                         String8(block.getElementName(&len)).string());
    458                 return UNKNOWN_ERROR;
    459             }
    460 
    461             attr.createIfNeeded(outTable);
    462 
    463             if (attr.type == ResTable_map::TYPE_ANY) {
    464                 // No type was explicitly stated, so supplying enum tags
    465                 // implicitly creates an enum or flag.
    466                 attr.type = 0;
    467             }
    468 
    469             if ((attr.type&(ResTable_map::TYPE_ENUM|ResTable_map::TYPE_FLAGS)) == 0) {
    470                 // Wasn't originally specified as an enum, so update its type.
    471                 attr.type |= localType;
    472                 if (!attr.hasErrors) {
    473                     char numberStr[16];
    474                     sprintf(numberStr, "%d", attr.type);
    475                     err = outTable->addBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
    476                             myPackage, attr16, attr.ident, String16(""),
    477                             String16("^type"), String16(numberStr), NULL, NULL, true);
    478                     if (err != NO_ERROR) {
    479                         attr.hasErrors = true;
    480                     }
    481                 }
    482             } else if ((uint32_t)(attr.type&(ResTable_map::TYPE_ENUM|ResTable_map::TYPE_FLAGS)) != localType) {
    483                 if (localType == ResTable_map::TYPE_ENUM) {
    484                     SourcePos(in->getPrintableSource(), block.getLineNumber())
    485                             .error("<enum> attribute can not be used inside a flags format\n");
    486                     attr.hasErrors = true;
    487                 } else {
    488                     SourcePos(in->getPrintableSource(), block.getLineNumber())
    489                             .error("<flag> attribute can not be used inside a enum format\n");
    490                     attr.hasErrors = true;
    491                 }
    492             }
    493 
    494             String16 itemIdent;
    495             ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "name");
    496             if (itemIdentIdx >= 0) {
    497                 itemIdent = String16(block.getAttributeStringValue(itemIdentIdx, &len));
    498             } else {
    499                 SourcePos(in->getPrintableSource(), block.getLineNumber())
    500                         .error("A 'name' attribute is required for <enum> or <flag>\n");
    501                 attr.hasErrors = true;
    502             }
    503 
    504             String16 value;
    505             ssize_t valueIdx = block.indexOfAttribute(NULL, "value");
    506             if (valueIdx >= 0) {
    507                 value = String16(block.getAttributeStringValue(valueIdx, &len));
    508             } else {
    509                 SourcePos(in->getPrintableSource(), block.getLineNumber())
    510                         .error("A 'value' attribute is required for <enum> or <flag>\n");
    511                 attr.hasErrors = true;
    512             }
    513             if (!attr.hasErrors && !ResTable::stringToInt(value.string(), value.size(), NULL)) {
    514                 SourcePos(in->getPrintableSource(), block.getLineNumber())
    515                         .error("Tag <enum> or <flag> 'value' attribute must be a number,"
    516                         " not \"%s\"\n",
    517                         String8(value).string());
    518                 attr.hasErrors = true;
    519             }
    520 
    521             if (!attr.hasErrors) {
    522                 if (enumOrFlagsComment.size() == 0) {
    523                     enumOrFlagsComment.append(mayOrMust(attr.type,
    524                             ResTable_map::TYPE_ENUM|ResTable_map::TYPE_FLAGS));
    525                     enumOrFlagsComment.append((attr.type&ResTable_map::TYPE_ENUM)
    526                                        ? String16(" be one of the following constant values.")
    527                                        : String16(" be one or more (separated by '|') of the following constant values."));
    528                     enumOrFlagsComment.append(String16("</p>\n<table>\n"
    529                                                 "<colgroup align=\"left\" />\n"
    530                                                 "<colgroup align=\"left\" />\n"
    531                                                 "<colgroup align=\"left\" />\n"
    532                                                 "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>"));
    533                 }
    534 
    535                 enumOrFlagsComment.append(String16("\n<tr><td><code>"));
    536                 enumOrFlagsComment.append(itemIdent);
    537                 enumOrFlagsComment.append(String16("</code></td><td>"));
    538                 enumOrFlagsComment.append(value);
    539                 enumOrFlagsComment.append(String16("</td><td>"));
    540                 if (block.getComment(&len)) {
    541                     enumOrFlagsComment.append(String16(block.getComment(&len)));
    542                 }
    543                 enumOrFlagsComment.append(String16("</td></tr>"));
    544 
    545                 err = outTable->addBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
    546                                        myPackage,
    547                                        attr16, attr.ident, String16(""),
    548                                        itemIdent, value, NULL, NULL, false, true);
    549                 if (err != NO_ERROR) {
    550                     attr.hasErrors = true;
    551                 }
    552             }
    553         } else if (code == ResXMLTree::END_TAG) {
    554             if (strcmp16(block.getElementName(&len), attr16.string()) == 0) {
    555                 break;
    556             }
    557             if ((attr.type&ResTable_map::TYPE_ENUM) != 0) {
    558                 if (strcmp16(block.getElementName(&len), enum16.string()) != 0) {
    559                     SourcePos(in->getPrintableSource(), block.getLineNumber())
    560                             .error("Found tag </%s> where </enum> is expected\n",
    561                             String8(block.getElementName(&len)).string());
    562                     return UNKNOWN_ERROR;
    563                 }
    564             } else {
    565                 if (strcmp16(block.getElementName(&len), flag16.string()) != 0) {
    566                     SourcePos(in->getPrintableSource(), block.getLineNumber())
    567                             .error("Found tag </%s> where </flag> is expected\n",
    568                             String8(block.getElementName(&len)).string());
    569                     return UNKNOWN_ERROR;
    570                 }
    571             }
    572         }
    573     }
    574 
    575     if (!attr.hasErrors && attr.added) {
    576         appendTypeInfo(outTable, myPackage, attr16, attr.ident, attr.type, gFormatFlags);
    577     }
    578 
    579     if (!attr.hasErrors && enumOrFlagsComment.size() > 0) {
    580         enumOrFlagsComment.append(String16("\n</table>"));
    581         outTable->appendTypeComment(myPackage, attr16, attr.ident, enumOrFlagsComment);
    582     }
    583 
    584 
    585     return NO_ERROR;
    586 }
    587 
    588 bool localeIsDefined(const ResTable_config& config)
    589 {
    590     return config.locale == 0;
    591 }
    592 
    593 status_t parseAndAddBag(Bundle* bundle,
    594                         const sp<AaptFile>& in,
    595                         ResXMLTree* block,
    596                         const ResTable_config& config,
    597                         const String16& myPackage,
    598                         const String16& curType,
    599                         const String16& ident,
    600                         const String16& parentIdent,
    601                         const String16& itemIdent,
    602                         int32_t curFormat,
    603                         bool isFormatted,
    604                         const String16& /* product */,
    605                         PseudolocalizationMethod pseudolocalize,
    606                         const bool overwrite,
    607                         ResourceTable* outTable)
    608 {
    609     status_t err;
    610     const String16 item16("item");
    611 
    612     String16 str;
    613     Vector<StringPool::entry_style_span> spans;
    614     err = parseStyledString(bundle, in->getPrintableSource().string(),
    615                             block, item16, &str, &spans, isFormatted,
    616                             pseudolocalize);
    617     if (err != NO_ERROR) {
    618         return err;
    619     }
    620 
    621     if (kIsDebug) {
    622         printf("Adding resource bag entry l=%c%c c=%c%c orien=%d d=%d "
    623                 " pid=%s, bag=%s, id=%s: %s\n",
    624                 config.language[0], config.language[1],
    625                 config.country[0], config.country[1],
    626                 config.orientation, config.density,
    627                 String8(parentIdent).string(),
    628                 String8(ident).string(),
    629                 String8(itemIdent).string(),
    630                 String8(str).string());
    631     }
    632 
    633     err = outTable->addBag(SourcePos(in->getPrintableSource(), block->getLineNumber()),
    634                            myPackage, curType, ident, parentIdent, itemIdent, str,
    635                            &spans, &config, overwrite, false, curFormat);
    636     return err;
    637 }
    638 
    639 /*
    640  * Returns true if needle is one of the elements in the comma-separated list
    641  * haystack, false otherwise.
    642  */
    643 bool isInProductList(const String16& needle, const String16& haystack) {
    644     const char16_t *needle2 = needle.string();
    645     const char16_t *haystack2 = haystack.string();
    646     size_t needlesize = needle.size();
    647 
    648     while (*haystack2 != '\0') {
    649         if (strncmp16(haystack2, needle2, needlesize) == 0) {
    650             if (haystack2[needlesize] == '\0' || haystack2[needlesize] == ',') {
    651                 return true;
    652             }
    653         }
    654 
    655         while (*haystack2 != '\0' && *haystack2 != ',') {
    656             haystack2++;
    657         }
    658         if (*haystack2 == ',') {
    659             haystack2++;
    660         }
    661     }
    662 
    663     return false;
    664 }
    665 
    666 /*
    667  * A simple container that holds a resource type and name. It is ordered first by type then
    668  * by name.
    669  */
    670 struct type_ident_pair_t {
    671     String16 type;
    672     String16 ident;
    673 
    674     type_ident_pair_t() { };
    675     type_ident_pair_t(const String16& t, const String16& i) : type(t), ident(i) { }
    676     type_ident_pair_t(const type_ident_pair_t& o) : type(o.type), ident(o.ident) { }
    677     inline bool operator < (const type_ident_pair_t& o) const {
    678         int cmp = compare_type(type, o.type);
    679         if (cmp < 0) {
    680             return true;
    681         } else if (cmp > 0) {
    682             return false;
    683         } else {
    684             return strictly_order_type(ident, o.ident);
    685         }
    686     }
    687 };
    688 
    689 
    690 status_t parseAndAddEntry(Bundle* bundle,
    691                         const sp<AaptFile>& in,
    692                         ResXMLTree* block,
    693                         const ResTable_config& config,
    694                         const String16& myPackage,
    695                         const String16& curType,
    696                         const String16& ident,
    697                         const String16& curTag,
    698                         bool curIsStyled,
    699                         int32_t curFormat,
    700                         bool isFormatted,
    701                         const String16& product,
    702                         PseudolocalizationMethod pseudolocalize,
    703                         const bool overwrite,
    704                         KeyedVector<type_ident_pair_t, bool>* skippedResourceNames,
    705                         ResourceTable* outTable)
    706 {
    707     status_t err;
    708 
    709     String16 str;
    710     Vector<StringPool::entry_style_span> spans;
    711     err = parseStyledString(bundle, in->getPrintableSource().string(), block,
    712                             curTag, &str, curIsStyled ? &spans : NULL,
    713                             isFormatted, pseudolocalize);
    714 
    715     if (err < NO_ERROR) {
    716         return err;
    717     }
    718 
    719     /*
    720      * If a product type was specified on the command line
    721      * and also in the string, and the two are not the same,
    722      * return without adding the string.
    723      */
    724 
    725     const char *bundleProduct = bundle->getProduct();
    726     if (bundleProduct == NULL) {
    727         bundleProduct = "";
    728     }
    729 
    730     if (product.size() != 0) {
    731         /*
    732          * If the command-line-specified product is empty, only "default"
    733          * matches.  Other variants are skipped.  This is so generation
    734          * of the R.java file when the product is not known is predictable.
    735          */
    736 
    737         if (bundleProduct[0] == '\0') {
    738             if (strcmp16(String16("default").string(), product.string()) != 0) {
    739                 /*
    740                  * This string has a product other than 'default'. Do not add it,
    741                  * but record it so that if we do not see the same string with
    742                  * product 'default' or no product, then report an error.
    743                  */
    744                 skippedResourceNames->replaceValueFor(
    745                         type_ident_pair_t(curType, ident), true);
    746                 return NO_ERROR;
    747             }
    748         } else {
    749             /*
    750              * The command-line product is not empty.
    751              * If the product for this string is on the command-line list,
    752              * it matches.  "default" also matches, but only if nothing
    753              * else has matched already.
    754              */
    755 
    756             if (isInProductList(product, String16(bundleProduct))) {
    757                 ;
    758             } else if (strcmp16(String16("default").string(), product.string()) == 0 &&
    759                        !outTable->hasBagOrEntry(myPackage, curType, ident, config)) {
    760                 ;
    761             } else {
    762                 return NO_ERROR;
    763             }
    764         }
    765     }
    766 
    767     if (kIsDebug) {
    768         printf("Adding resource entry l=%c%c c=%c%c orien=%d d=%d id=%s: %s\n",
    769                 config.language[0], config.language[1],
    770                 config.country[0], config.country[1],
    771                 config.orientation, config.density,
    772                 String8(ident).string(), String8(str).string());
    773     }
    774 
    775     err = outTable->addEntry(SourcePos(in->getPrintableSource(), block->getLineNumber()),
    776                              myPackage, curType, ident, str, &spans, &config,
    777                              false, curFormat, overwrite);
    778 
    779     return err;
    780 }
    781 
    782 status_t compileResourceFile(Bundle* bundle,
    783                              const sp<AaptAssets>& assets,
    784                              const sp<AaptFile>& in,
    785                              const ResTable_config& defParams,
    786                              const bool overwrite,
    787                              ResourceTable* outTable)
    788 {
    789     ResXMLTree block;
    790     status_t err = parseXMLResource(in, &block, false, true);
    791     if (err != NO_ERROR) {
    792         return err;
    793     }
    794 
    795     // Top-level tag.
    796     const String16 resources16("resources");
    797 
    798     // Identifier declaration tags.
    799     const String16 declare_styleable16("declare-styleable");
    800     const String16 attr16("attr");
    801 
    802     // Data creation organizational tags.
    803     const String16 string16("string");
    804     const String16 drawable16("drawable");
    805     const String16 color16("color");
    806     const String16 bool16("bool");
    807     const String16 integer16("integer");
    808     const String16 dimen16("dimen");
    809     const String16 fraction16("fraction");
    810     const String16 style16("style");
    811     const String16 plurals16("plurals");
    812     const String16 array16("array");
    813     const String16 string_array16("string-array");
    814     const String16 integer_array16("integer-array");
    815     const String16 public16("public");
    816     const String16 public_padding16("public-padding");
    817     const String16 private_symbols16("private-symbols");
    818     const String16 java_symbol16("java-symbol");
    819     const String16 add_resource16("add-resource");
    820     const String16 skip16("skip");
    821     const String16 eat_comment16("eat-comment");
    822 
    823     // Data creation tags.
    824     const String16 bag16("bag");
    825     const String16 item16("item");
    826 
    827     // Attribute type constants.
    828     const String16 enum16("enum");
    829 
    830     // plural values
    831     const String16 other16("other");
    832     const String16 quantityOther16("^other");
    833     const String16 zero16("zero");
    834     const String16 quantityZero16("^zero");
    835     const String16 one16("one");
    836     const String16 quantityOne16("^one");
    837     const String16 two16("two");
    838     const String16 quantityTwo16("^two");
    839     const String16 few16("few");
    840     const String16 quantityFew16("^few");
    841     const String16 many16("many");
    842     const String16 quantityMany16("^many");
    843 
    844     // useful attribute names and special values
    845     const String16 name16("name");
    846     const String16 translatable16("translatable");
    847     const String16 formatted16("formatted");
    848     const String16 false16("false");
    849 
    850     const String16 myPackage(assets->getPackage());
    851 
    852     bool hasErrors = false;
    853 
    854     bool fileIsTranslatable = true;
    855     if (strstr(in->getPrintableSource().string(), "donottranslate") != NULL) {
    856         fileIsTranslatable = false;
    857     }
    858 
    859     DefaultKeyedVector<String16, uint32_t> nextPublicId(0);
    860 
    861     // Stores the resource names that were skipped. Typically this happens when
    862     // AAPT is invoked without a product specified and a resource has no
    863     // 'default' product attribute.
    864     KeyedVector<type_ident_pair_t, bool> skippedResourceNames;
    865 
    866     ResXMLTree::event_code_t code;
    867     do {
    868         code = block.next();
    869     } while (code == ResXMLTree::START_NAMESPACE);
    870 
    871     size_t len;
    872     if (code != ResXMLTree::START_TAG) {
    873         SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
    874                 "No start tag found\n");
    875         return UNKNOWN_ERROR;
    876     }
    877     if (strcmp16(block.getElementName(&len), resources16.string()) != 0) {
    878         SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
    879                 "Invalid start tag %s\n", String8(block.getElementName(&len)).string());
    880         return UNKNOWN_ERROR;
    881     }
    882 
    883     ResTable_config curParams(defParams);
    884 
    885     ResTable_config pseudoParams(curParams);
    886         pseudoParams.language[0] = 'e';
    887         pseudoParams.language[1] = 'n';
    888         pseudoParams.country[0] = 'X';
    889         pseudoParams.country[1] = 'A';
    890 
    891     ResTable_config pseudoBidiParams(curParams);
    892         pseudoBidiParams.language[0] = 'a';
    893         pseudoBidiParams.language[1] = 'r';
    894         pseudoBidiParams.country[0] = 'X';
    895         pseudoBidiParams.country[1] = 'B';
    896 
    897     // We should skip resources for pseudolocales if they were
    898     // already added automatically. This is a fix for a transition period when
    899     // manually pseudolocalized resources may be expected.
    900     // TODO: remove this check after next SDK version release.
    901     if ((bundle->getPseudolocalize() & PSEUDO_ACCENTED &&
    902          curParams.locale == pseudoParams.locale) ||
    903         (bundle->getPseudolocalize() & PSEUDO_BIDI &&
    904          curParams.locale == pseudoBidiParams.locale)) {
    905         SourcePos(in->getPrintableSource(), 0).warning(
    906                 "Resource file %s is skipped as pseudolocalization"
    907                 " was done automatically.",
    908                 in->getPrintableSource().string());
    909         return NO_ERROR;
    910     }
    911 
    912     while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
    913         if (code == ResXMLTree::START_TAG) {
    914             const String16* curTag = NULL;
    915             String16 curType;
    916             String16 curName;
    917             int32_t curFormat = ResTable_map::TYPE_ANY;
    918             bool curIsBag = false;
    919             bool curIsBagReplaceOnOverwrite = false;
    920             bool curIsStyled = false;
    921             bool curIsPseudolocalizable = false;
    922             bool curIsFormatted = fileIsTranslatable;
    923             bool localHasErrors = false;
    924 
    925             if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
    926                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
    927                         && code != ResXMLTree::BAD_DOCUMENT) {
    928                     if (code == ResXMLTree::END_TAG) {
    929                         if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
    930                             break;
    931                         }
    932                     }
    933                 }
    934                 continue;
    935 
    936             } else if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
    937                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
    938                         && code != ResXMLTree::BAD_DOCUMENT) {
    939                     if (code == ResXMLTree::END_TAG) {
    940                         if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
    941                             break;
    942                         }
    943                     }
    944                 }
    945                 continue;
    946 
    947             } else if (strcmp16(block.getElementName(&len), public16.string()) == 0) {
    948                 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
    949 
    950                 String16 type;
    951                 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
    952                 if (typeIdx < 0) {
    953                     srcPos.error("A 'type' attribute is required for <public>\n");
    954                     hasErrors = localHasErrors = true;
    955                 }
    956                 type = String16(block.getAttributeStringValue(typeIdx, &len));
    957 
    958                 String16 name;
    959                 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
    960                 if (nameIdx < 0) {
    961                     srcPos.error("A 'name' attribute is required for <public>\n");
    962                     hasErrors = localHasErrors = true;
    963                 }
    964                 name = String16(block.getAttributeStringValue(nameIdx, &len));
    965 
    966                 uint32_t ident = 0;
    967                 ssize_t identIdx = block.indexOfAttribute(NULL, "id");
    968                 if (identIdx >= 0) {
    969                     const char16_t* identStr = block.getAttributeStringValue(identIdx, &len);
    970                     Res_value identValue;
    971                     if (!ResTable::stringToInt(identStr, len, &identValue)) {
    972                         srcPos.error("Given 'id' attribute is not an integer: %s\n",
    973                                 String8(block.getAttributeStringValue(identIdx, &len)).string());
    974                         hasErrors = localHasErrors = true;
    975                     } else {
    976                         ident = identValue.data;
    977                         nextPublicId.replaceValueFor(type, ident+1);
    978                     }
    979                 } else if (nextPublicId.indexOfKey(type) < 0) {
    980                     srcPos.error("No 'id' attribute supplied <public>,"
    981                             " and no previous id defined in this file.\n");
    982                     hasErrors = localHasErrors = true;
    983                 } else if (!localHasErrors) {
    984                     ident = nextPublicId.valueFor(type);
    985                     nextPublicId.replaceValueFor(type, ident+1);
    986                 }
    987 
    988                 if (!localHasErrors) {
    989                     err = outTable->addPublic(srcPos, myPackage, type, name, ident);
    990                     if (err < NO_ERROR) {
    991                         hasErrors = localHasErrors = true;
    992                     }
    993                 }
    994                 if (!localHasErrors) {
    995                     sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
    996                     if (symbols != NULL) {
    997                         symbols = symbols->addNestedSymbol(String8(type), srcPos);
    998                     }
    999                     if (symbols != NULL) {
   1000                         symbols->makeSymbolPublic(String8(name), srcPos);
   1001                         String16 comment(
   1002                             block.getComment(&len) ? block.getComment(&len) : nulStr);
   1003                         symbols->appendComment(String8(name), comment, srcPos);
   1004                     } else {
   1005                         srcPos.error("Unable to create symbols!\n");
   1006                         hasErrors = localHasErrors = true;
   1007                     }
   1008                 }
   1009 
   1010                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   1011                     if (code == ResXMLTree::END_TAG) {
   1012                         if (strcmp16(block.getElementName(&len), public16.string()) == 0) {
   1013                             break;
   1014                         }
   1015                     }
   1016                 }
   1017                 continue;
   1018 
   1019             } else if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) {
   1020                 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
   1021 
   1022                 String16 type;
   1023                 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
   1024                 if (typeIdx < 0) {
   1025                     srcPos.error("A 'type' attribute is required for <public-padding>\n");
   1026                     hasErrors = localHasErrors = true;
   1027                 }
   1028                 type = String16(block.getAttributeStringValue(typeIdx, &len));
   1029 
   1030                 String16 name;
   1031                 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
   1032                 if (nameIdx < 0) {
   1033                     srcPos.error("A 'name' attribute is required for <public-padding>\n");
   1034                     hasErrors = localHasErrors = true;
   1035                 }
   1036                 name = String16(block.getAttributeStringValue(nameIdx, &len));
   1037 
   1038                 uint32_t start = 0;
   1039                 ssize_t startIdx = block.indexOfAttribute(NULL, "start");
   1040                 if (startIdx >= 0) {
   1041                     const char16_t* startStr = block.getAttributeStringValue(startIdx, &len);
   1042                     Res_value startValue;
   1043                     if (!ResTable::stringToInt(startStr, len, &startValue)) {
   1044                         srcPos.error("Given 'start' attribute is not an integer: %s\n",
   1045                                 String8(block.getAttributeStringValue(startIdx, &len)).string());
   1046                         hasErrors = localHasErrors = true;
   1047                     } else {
   1048                         start = startValue.data;
   1049                     }
   1050                 } else if (nextPublicId.indexOfKey(type) < 0) {
   1051                     srcPos.error("No 'start' attribute supplied <public-padding>,"
   1052                             " and no previous id defined in this file.\n");
   1053                     hasErrors = localHasErrors = true;
   1054                 } else if (!localHasErrors) {
   1055                     start = nextPublicId.valueFor(type);
   1056                 }
   1057 
   1058                 uint32_t end = 0;
   1059                 ssize_t endIdx = block.indexOfAttribute(NULL, "end");
   1060                 if (endIdx >= 0) {
   1061                     const char16_t* endStr = block.getAttributeStringValue(endIdx, &len);
   1062                     Res_value endValue;
   1063                     if (!ResTable::stringToInt(endStr, len, &endValue)) {
   1064                         srcPos.error("Given 'end' attribute is not an integer: %s\n",
   1065                                 String8(block.getAttributeStringValue(endIdx, &len)).string());
   1066                         hasErrors = localHasErrors = true;
   1067                     } else {
   1068                         end = endValue.data;
   1069                     }
   1070                 } else {
   1071                     srcPos.error("No 'end' attribute supplied <public-padding>\n");
   1072                     hasErrors = localHasErrors = true;
   1073                 }
   1074 
   1075                 if (end >= start) {
   1076                     nextPublicId.replaceValueFor(type, end+1);
   1077                 } else {
   1078                     srcPos.error("Padding start '%ul' is after end '%ul'\n",
   1079                             start, end);
   1080                     hasErrors = localHasErrors = true;
   1081                 }
   1082 
   1083                 String16 comment(
   1084                     block.getComment(&len) ? block.getComment(&len) : nulStr);
   1085                 for (uint32_t curIdent=start; curIdent<=end; curIdent++) {
   1086                     if (localHasErrors) {
   1087                         break;
   1088                     }
   1089                     String16 curName(name);
   1090                     char buf[64];
   1091                     sprintf(buf, "%d", (int)(end-curIdent+1));
   1092                     curName.append(String16(buf));
   1093 
   1094                     err = outTable->addEntry(srcPos, myPackage, type, curName,
   1095                                              String16("padding"), NULL, &curParams, false,
   1096                                              ResTable_map::TYPE_STRING, overwrite);
   1097                     if (err < NO_ERROR) {
   1098                         hasErrors = localHasErrors = true;
   1099                         break;
   1100                     }
   1101                     err = outTable->addPublic(srcPos, myPackage, type,
   1102                             curName, curIdent);
   1103                     if (err < NO_ERROR) {
   1104                         hasErrors = localHasErrors = true;
   1105                         break;
   1106                     }
   1107                     sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
   1108                     if (symbols != NULL) {
   1109                         symbols = symbols->addNestedSymbol(String8(type), srcPos);
   1110                     }
   1111                     if (symbols != NULL) {
   1112                         symbols->makeSymbolPublic(String8(curName), srcPos);
   1113                         symbols->appendComment(String8(curName), comment, srcPos);
   1114                     } else {
   1115                         srcPos.error("Unable to create symbols!\n");
   1116                         hasErrors = localHasErrors = true;
   1117                     }
   1118                 }
   1119 
   1120                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   1121                     if (code == ResXMLTree::END_TAG) {
   1122                         if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) {
   1123                             break;
   1124                         }
   1125                     }
   1126                 }
   1127                 continue;
   1128 
   1129             } else if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
   1130                 String16 pkg;
   1131                 ssize_t pkgIdx = block.indexOfAttribute(NULL, "package");
   1132                 if (pkgIdx < 0) {
   1133                     SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1134                             "A 'package' attribute is required for <private-symbols>\n");
   1135                     hasErrors = localHasErrors = true;
   1136                 }
   1137                 pkg = String16(block.getAttributeStringValue(pkgIdx, &len));
   1138                 if (!localHasErrors) {
   1139                     assets->setSymbolsPrivatePackage(String8(pkg));
   1140                 }
   1141 
   1142                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   1143                     if (code == ResXMLTree::END_TAG) {
   1144                         if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
   1145                             break;
   1146                         }
   1147                     }
   1148                 }
   1149                 continue;
   1150 
   1151             } else if (strcmp16(block.getElementName(&len), java_symbol16.string()) == 0) {
   1152                 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
   1153 
   1154                 String16 type;
   1155                 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
   1156                 if (typeIdx < 0) {
   1157                     srcPos.error("A 'type' attribute is required for <public>\n");
   1158                     hasErrors = localHasErrors = true;
   1159                 }
   1160                 type = String16(block.getAttributeStringValue(typeIdx, &len));
   1161 
   1162                 String16 name;
   1163                 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
   1164                 if (nameIdx < 0) {
   1165                     srcPos.error("A 'name' attribute is required for <public>\n");
   1166                     hasErrors = localHasErrors = true;
   1167                 }
   1168                 name = String16(block.getAttributeStringValue(nameIdx, &len));
   1169 
   1170                 sp<AaptSymbols> symbols = assets->getJavaSymbolsFor(String8("R"));
   1171                 if (symbols != NULL) {
   1172                     symbols = symbols->addNestedSymbol(String8(type), srcPos);
   1173                 }
   1174                 if (symbols != NULL) {
   1175                     symbols->makeSymbolJavaSymbol(String8(name), srcPos);
   1176                     String16 comment(
   1177                         block.getComment(&len) ? block.getComment(&len) : nulStr);
   1178                     symbols->appendComment(String8(name), comment, srcPos);
   1179                 } else {
   1180                     srcPos.error("Unable to create symbols!\n");
   1181                     hasErrors = localHasErrors = true;
   1182                 }
   1183 
   1184                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   1185                     if (code == ResXMLTree::END_TAG) {
   1186                         if (strcmp16(block.getElementName(&len), java_symbol16.string()) == 0) {
   1187                             break;
   1188                         }
   1189                     }
   1190                 }
   1191                 continue;
   1192 
   1193 
   1194             } else if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) {
   1195                 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
   1196 
   1197                 String16 typeName;
   1198                 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
   1199                 if (typeIdx < 0) {
   1200                     srcPos.error("A 'type' attribute is required for <add-resource>\n");
   1201                     hasErrors = localHasErrors = true;
   1202                 }
   1203                 typeName = String16(block.getAttributeStringValue(typeIdx, &len));
   1204 
   1205                 String16 name;
   1206                 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
   1207                 if (nameIdx < 0) {
   1208                     srcPos.error("A 'name' attribute is required for <add-resource>\n");
   1209                     hasErrors = localHasErrors = true;
   1210                 }
   1211                 name = String16(block.getAttributeStringValue(nameIdx, &len));
   1212 
   1213                 outTable->canAddEntry(srcPos, myPackage, typeName, name);
   1214 
   1215                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   1216                     if (code == ResXMLTree::END_TAG) {
   1217                         if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) {
   1218                             break;
   1219                         }
   1220                     }
   1221                 }
   1222                 continue;
   1223 
   1224             } else if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
   1225                 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
   1226 
   1227                 String16 ident;
   1228                 ssize_t identIdx = block.indexOfAttribute(NULL, "name");
   1229                 if (identIdx < 0) {
   1230                     srcPos.error("A 'name' attribute is required for <declare-styleable>\n");
   1231                     hasErrors = localHasErrors = true;
   1232                 }
   1233                 ident = String16(block.getAttributeStringValue(identIdx, &len));
   1234 
   1235                 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
   1236                 if (!localHasErrors) {
   1237                     if (symbols != NULL) {
   1238                         symbols = symbols->addNestedSymbol(String8("styleable"), srcPos);
   1239                     }
   1240                     sp<AaptSymbols> styleSymbols = symbols;
   1241                     if (symbols != NULL) {
   1242                         symbols = symbols->addNestedSymbol(String8(ident), srcPos);
   1243                     }
   1244                     if (symbols == NULL) {
   1245                         srcPos.error("Unable to create symbols!\n");
   1246                         return UNKNOWN_ERROR;
   1247                     }
   1248 
   1249                     String16 comment(
   1250                         block.getComment(&len) ? block.getComment(&len) : nulStr);
   1251                     styleSymbols->appendComment(String8(ident), comment, srcPos);
   1252                 } else {
   1253                     symbols = NULL;
   1254                 }
   1255 
   1256                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   1257                     if (code == ResXMLTree::START_TAG) {
   1258                         if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
   1259                             while ((code=block.next()) != ResXMLTree::END_DOCUMENT
   1260                                    && code != ResXMLTree::BAD_DOCUMENT) {
   1261                                 if (code == ResXMLTree::END_TAG) {
   1262                                     if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
   1263                                         break;
   1264                                     }
   1265                                 }
   1266                             }
   1267                             continue;
   1268                         } else if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
   1269                             while ((code=block.next()) != ResXMLTree::END_DOCUMENT
   1270                                    && code != ResXMLTree::BAD_DOCUMENT) {
   1271                                 if (code == ResXMLTree::END_TAG) {
   1272                                     if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
   1273                                         break;
   1274                                     }
   1275                                 }
   1276                             }
   1277                             continue;
   1278                         } else if (strcmp16(block.getElementName(&len), attr16.string()) != 0) {
   1279                             SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1280                                     "Tag <%s> can not appear inside <declare-styleable>, only <attr>\n",
   1281                                     String8(block.getElementName(&len)).string());
   1282                             return UNKNOWN_ERROR;
   1283                         }
   1284 
   1285                         String16 comment(
   1286                             block.getComment(&len) ? block.getComment(&len) : nulStr);
   1287                         String16 itemIdent;
   1288                         err = compileAttribute(in, block, myPackage, outTable, &itemIdent, true);
   1289                         if (err != NO_ERROR) {
   1290                             hasErrors = localHasErrors = true;
   1291                         }
   1292 
   1293                         if (symbols != NULL) {
   1294                             SourcePos srcPos(String8(in->getPrintableSource()), block.getLineNumber());
   1295                             symbols->addSymbol(String8(itemIdent), 0, srcPos);
   1296                             symbols->appendComment(String8(itemIdent), comment, srcPos);
   1297                             //printf("Attribute %s comment: %s\n", String8(itemIdent).string(),
   1298                             //     String8(comment).string());
   1299                         }
   1300                     } else if (code == ResXMLTree::END_TAG) {
   1301                         if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
   1302                             break;
   1303                         }
   1304 
   1305                         SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1306                                 "Found tag </%s> where </attr> is expected\n",
   1307                                 String8(block.getElementName(&len)).string());
   1308                         return UNKNOWN_ERROR;
   1309                     }
   1310                 }
   1311                 continue;
   1312 
   1313             } else if (strcmp16(block.getElementName(&len), attr16.string()) == 0) {
   1314                 err = compileAttribute(in, block, myPackage, outTable, NULL);
   1315                 if (err != NO_ERROR) {
   1316                     hasErrors = true;
   1317                 }
   1318                 continue;
   1319 
   1320             } else if (strcmp16(block.getElementName(&len), item16.string()) == 0) {
   1321                 curTag = &item16;
   1322                 ssize_t attri = block.indexOfAttribute(NULL, "type");
   1323                 if (attri >= 0) {
   1324                     curType = String16(block.getAttributeStringValue(attri, &len));
   1325                     ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
   1326                     if (nameIdx >= 0) {
   1327                         curName = String16(block.getAttributeStringValue(nameIdx, &len));
   1328                     }
   1329                     ssize_t formatIdx = block.indexOfAttribute(NULL, "format");
   1330                     if (formatIdx >= 0) {
   1331                         String16 formatStr = String16(block.getAttributeStringValue(
   1332                                 formatIdx, &len));
   1333                         curFormat = parse_flags(formatStr.string(), formatStr.size(),
   1334                                                 gFormatFlags);
   1335                         if (curFormat == 0) {
   1336                             SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1337                                     "Tag <item> 'format' attribute value \"%s\" not valid\n",
   1338                                     String8(formatStr).string());
   1339                             hasErrors = localHasErrors = true;
   1340                         }
   1341                     }
   1342                 } else {
   1343                     SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1344                             "A 'type' attribute is required for <item>\n");
   1345                     hasErrors = localHasErrors = true;
   1346                 }
   1347                 curIsStyled = true;
   1348             } else if (strcmp16(block.getElementName(&len), string16.string()) == 0) {
   1349                 // Note the existence and locale of every string we process
   1350                 char rawLocale[RESTABLE_MAX_LOCALE_LEN];
   1351                 curParams.getBcp47Locale(rawLocale);
   1352                 String8 locale(rawLocale);
   1353                 String16 name;
   1354                 String16 translatable;
   1355                 String16 formatted;
   1356 
   1357                 size_t n = block.getAttributeCount();
   1358                 for (size_t i = 0; i < n; i++) {
   1359                     size_t length;
   1360                     const char16_t* attr = block.getAttributeName(i, &length);
   1361                     if (strcmp16(attr, name16.string()) == 0) {
   1362                         name.setTo(block.getAttributeStringValue(i, &length));
   1363                     } else if (strcmp16(attr, translatable16.string()) == 0) {
   1364                         translatable.setTo(block.getAttributeStringValue(i, &length));
   1365                     } else if (strcmp16(attr, formatted16.string()) == 0) {
   1366                         formatted.setTo(block.getAttributeStringValue(i, &length));
   1367                     }
   1368                 }
   1369 
   1370                 if (name.size() > 0) {
   1371                     if (locale.size() == 0) {
   1372                         outTable->addDefaultLocalization(name);
   1373                     }
   1374                     if (translatable == false16) {
   1375                         curIsFormatted = false;
   1376                         // Untranslatable strings must only exist in the default [empty] locale
   1377                         if (locale.size() > 0) {
   1378                             SourcePos(in->getPrintableSource(), block.getLineNumber()).warning(
   1379                                     "string '%s' marked untranslatable but exists in locale '%s'\n",
   1380                                     String8(name).string(),
   1381                                     locale.string());
   1382                             // hasErrors = localHasErrors = true;
   1383                         } else {
   1384                             // Intentionally empty block:
   1385                             //
   1386                             // Don't add untranslatable strings to the localization table; that
   1387                             // way if we later see localizations of them, they'll be flagged as
   1388                             // having no default translation.
   1389                         }
   1390                     } else {
   1391                         outTable->addLocalization(
   1392                                 name,
   1393                                 locale,
   1394                                 SourcePos(in->getPrintableSource(), block.getLineNumber()));
   1395                     }
   1396 
   1397                     if (formatted == false16) {
   1398                         curIsFormatted = false;
   1399                     }
   1400                 }
   1401 
   1402                 curTag = &string16;
   1403                 curType = string16;
   1404                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
   1405                 curIsStyled = true;
   1406                 curIsPseudolocalizable = fileIsTranslatable && (translatable != false16);
   1407             } else if (strcmp16(block.getElementName(&len), drawable16.string()) == 0) {
   1408                 curTag = &drawable16;
   1409                 curType = drawable16;
   1410                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR;
   1411             } else if (strcmp16(block.getElementName(&len), color16.string()) == 0) {
   1412                 curTag = &color16;
   1413                 curType = color16;
   1414                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR;
   1415             } else if (strcmp16(block.getElementName(&len), bool16.string()) == 0) {
   1416                 curTag = &bool16;
   1417                 curType = bool16;
   1418                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_BOOLEAN;
   1419             } else if (strcmp16(block.getElementName(&len), integer16.string()) == 0) {
   1420                 curTag = &integer16;
   1421                 curType = integer16;
   1422                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
   1423             } else if (strcmp16(block.getElementName(&len), dimen16.string()) == 0) {
   1424                 curTag = &dimen16;
   1425                 curType = dimen16;
   1426                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_DIMENSION;
   1427             } else if (strcmp16(block.getElementName(&len), fraction16.string()) == 0) {
   1428                 curTag = &fraction16;
   1429                 curType = fraction16;
   1430                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_FRACTION;
   1431             } else if (strcmp16(block.getElementName(&len), bag16.string()) == 0) {
   1432                 curTag = &bag16;
   1433                 curIsBag = true;
   1434                 ssize_t attri = block.indexOfAttribute(NULL, "type");
   1435                 if (attri >= 0) {
   1436                     curType = String16(block.getAttributeStringValue(attri, &len));
   1437                 } else {
   1438                     SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1439                             "A 'type' attribute is required for <bag>\n");
   1440                     hasErrors = localHasErrors = true;
   1441                 }
   1442             } else if (strcmp16(block.getElementName(&len), style16.string()) == 0) {
   1443                 curTag = &style16;
   1444                 curType = style16;
   1445                 curIsBag = true;
   1446             } else if (strcmp16(block.getElementName(&len), plurals16.string()) == 0) {
   1447                 curTag = &plurals16;
   1448                 curType = plurals16;
   1449                 curIsBag = true;
   1450                 curIsPseudolocalizable = fileIsTranslatable;
   1451             } else if (strcmp16(block.getElementName(&len), array16.string()) == 0) {
   1452                 curTag = &array16;
   1453                 curType = array16;
   1454                 curIsBag = true;
   1455                 curIsBagReplaceOnOverwrite = true;
   1456                 ssize_t formatIdx = block.indexOfAttribute(NULL, "format");
   1457                 if (formatIdx >= 0) {
   1458                     String16 formatStr = String16(block.getAttributeStringValue(
   1459                             formatIdx, &len));
   1460                     curFormat = parse_flags(formatStr.string(), formatStr.size(),
   1461                                             gFormatFlags);
   1462                     if (curFormat == 0) {
   1463                         SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1464                                 "Tag <array> 'format' attribute value \"%s\" not valid\n",
   1465                                 String8(formatStr).string());
   1466                         hasErrors = localHasErrors = true;
   1467                     }
   1468                 }
   1469             } else if (strcmp16(block.getElementName(&len), string_array16.string()) == 0) {
   1470                 // Check whether these strings need valid formats.
   1471                 // (simplified form of what string16 does above)
   1472                 bool isTranslatable = false;
   1473                 size_t n = block.getAttributeCount();
   1474 
   1475                 // Pseudolocalizable by default, unless this string array isn't
   1476                 // translatable.
   1477                 for (size_t i = 0; i < n; i++) {
   1478                     size_t length;
   1479                     const char16_t* attr = block.getAttributeName(i, &length);
   1480                     if (strcmp16(attr, formatted16.string()) == 0) {
   1481                         const char16_t* value = block.getAttributeStringValue(i, &length);
   1482                         if (strcmp16(value, false16.string()) == 0) {
   1483                             curIsFormatted = false;
   1484                         }
   1485                     } else if (strcmp16(attr, translatable16.string()) == 0) {
   1486                         const char16_t* value = block.getAttributeStringValue(i, &length);
   1487                         if (strcmp16(value, false16.string()) == 0) {
   1488                             isTranslatable = false;
   1489                         }
   1490                     }
   1491                 }
   1492 
   1493                 curTag = &string_array16;
   1494                 curType = array16;
   1495                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
   1496                 curIsBag = true;
   1497                 curIsBagReplaceOnOverwrite = true;
   1498                 curIsPseudolocalizable = isTranslatable && fileIsTranslatable;
   1499             } else if (strcmp16(block.getElementName(&len), integer_array16.string()) == 0) {
   1500                 curTag = &integer_array16;
   1501                 curType = array16;
   1502                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
   1503                 curIsBag = true;
   1504                 curIsBagReplaceOnOverwrite = true;
   1505             } else {
   1506                 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1507                         "Found tag %s where item is expected\n",
   1508                         String8(block.getElementName(&len)).string());
   1509                 return UNKNOWN_ERROR;
   1510             }
   1511 
   1512             String16 ident;
   1513             ssize_t identIdx = block.indexOfAttribute(NULL, "name");
   1514             if (identIdx >= 0) {
   1515                 ident = String16(block.getAttributeStringValue(identIdx, &len));
   1516             } else {
   1517                 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1518                         "A 'name' attribute is required for <%s>\n",
   1519                         String8(*curTag).string());
   1520                 hasErrors = localHasErrors = true;
   1521             }
   1522 
   1523             String16 product;
   1524             identIdx = block.indexOfAttribute(NULL, "product");
   1525             if (identIdx >= 0) {
   1526                 product = String16(block.getAttributeStringValue(identIdx, &len));
   1527             }
   1528 
   1529             String16 comment(block.getComment(&len) ? block.getComment(&len) : nulStr);
   1530 
   1531             if (curIsBag) {
   1532                 // Figure out the parent of this bag...
   1533                 String16 parentIdent;
   1534                 ssize_t parentIdentIdx = block.indexOfAttribute(NULL, "parent");
   1535                 if (parentIdentIdx >= 0) {
   1536                     parentIdent = String16(block.getAttributeStringValue(parentIdentIdx, &len));
   1537                 } else {
   1538                     ssize_t sep = ident.findLast('.');
   1539                     if (sep >= 0) {
   1540                         parentIdent.setTo(ident, sep);
   1541                     }
   1542                 }
   1543 
   1544                 if (!localHasErrors) {
   1545                     err = outTable->startBag(SourcePos(in->getPrintableSource(),
   1546                             block.getLineNumber()), myPackage, curType, ident,
   1547                             parentIdent, &curParams,
   1548                             overwrite, curIsBagReplaceOnOverwrite);
   1549                     if (err != NO_ERROR) {
   1550                         hasErrors = localHasErrors = true;
   1551                     }
   1552                 }
   1553 
   1554                 ssize_t elmIndex = 0;
   1555                 char elmIndexStr[14];
   1556                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
   1557                         && code != ResXMLTree::BAD_DOCUMENT) {
   1558 
   1559                     if (code == ResXMLTree::START_TAG) {
   1560                         if (strcmp16(block.getElementName(&len), item16.string()) != 0) {
   1561                             SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1562                                     "Tag <%s> can not appear inside <%s>, only <item>\n",
   1563                                     String8(block.getElementName(&len)).string(),
   1564                                     String8(*curTag).string());
   1565                             return UNKNOWN_ERROR;
   1566                         }
   1567 
   1568                         String16 itemIdent;
   1569                         if (curType == array16) {
   1570                             sprintf(elmIndexStr, "^index_%d", (int)elmIndex++);
   1571                             itemIdent = String16(elmIndexStr);
   1572                         } else if (curType == plurals16) {
   1573                             ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "quantity");
   1574                             if (itemIdentIdx >= 0) {
   1575                                 String16 quantity16(block.getAttributeStringValue(itemIdentIdx, &len));
   1576                                 if (quantity16 == other16) {
   1577                                     itemIdent = quantityOther16;
   1578                                 }
   1579                                 else if (quantity16 == zero16) {
   1580                                     itemIdent = quantityZero16;
   1581                                 }
   1582                                 else if (quantity16 == one16) {
   1583                                     itemIdent = quantityOne16;
   1584                                 }
   1585                                 else if (quantity16 == two16) {
   1586                                     itemIdent = quantityTwo16;
   1587                                 }
   1588                                 else if (quantity16 == few16) {
   1589                                     itemIdent = quantityFew16;
   1590                                 }
   1591                                 else if (quantity16 == many16) {
   1592                                     itemIdent = quantityMany16;
   1593                                 }
   1594                                 else {
   1595                                     SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1596                                             "Illegal 'quantity' attribute is <item> inside <plurals>\n");
   1597                                     hasErrors = localHasErrors = true;
   1598                                 }
   1599                             } else {
   1600                                 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1601                                         "A 'quantity' attribute is required for <item> inside <plurals>\n");
   1602                                 hasErrors = localHasErrors = true;
   1603                             }
   1604                         } else {
   1605                             ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "name");
   1606                             if (itemIdentIdx >= 0) {
   1607                                 itemIdent = String16(block.getAttributeStringValue(itemIdentIdx, &len));
   1608                             } else {
   1609                                 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1610                                         "A 'name' attribute is required for <item>\n");
   1611                                 hasErrors = localHasErrors = true;
   1612                             }
   1613                         }
   1614 
   1615                         ResXMLParser::ResXMLPosition parserPosition;
   1616                         block.getPosition(&parserPosition);
   1617 
   1618                         err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType,
   1619                                 ident, parentIdent, itemIdent, curFormat, curIsFormatted,
   1620                                 product, NO_PSEUDOLOCALIZATION, overwrite, outTable);
   1621                         if (err == NO_ERROR) {
   1622                             if (curIsPseudolocalizable && localeIsDefined(curParams)
   1623                                     && bundle->getPseudolocalize() > 0) {
   1624                                 // pseudolocalize here
   1625                                 if ((PSEUDO_ACCENTED & bundle->getPseudolocalize()) ==
   1626                                    PSEUDO_ACCENTED) {
   1627                                     block.setPosition(parserPosition);
   1628                                     err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage,
   1629                                             curType, ident, parentIdent, itemIdent, curFormat,
   1630                                             curIsFormatted, product, PSEUDO_ACCENTED,
   1631                                             overwrite, outTable);
   1632                                 }
   1633                                 if ((PSEUDO_BIDI & bundle->getPseudolocalize()) ==
   1634                                    PSEUDO_BIDI) {
   1635                                     block.setPosition(parserPosition);
   1636                                     err = parseAndAddBag(bundle, in, &block, pseudoBidiParams, myPackage,
   1637                                             curType, ident, parentIdent, itemIdent, curFormat,
   1638                                             curIsFormatted, product, PSEUDO_BIDI,
   1639                                             overwrite, outTable);
   1640                                 }
   1641                             }
   1642                         }
   1643                         if (err != NO_ERROR) {
   1644                             hasErrors = localHasErrors = true;
   1645                         }
   1646                     } else if (code == ResXMLTree::END_TAG) {
   1647                         if (strcmp16(block.getElementName(&len), curTag->string()) != 0) {
   1648                             SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1649                                     "Found tag </%s> where </%s> is expected\n",
   1650                                     String8(block.getElementName(&len)).string(),
   1651                                     String8(*curTag).string());
   1652                             return UNKNOWN_ERROR;
   1653                         }
   1654                         break;
   1655                     }
   1656                 }
   1657             } else {
   1658                 ResXMLParser::ResXMLPosition parserPosition;
   1659                 block.getPosition(&parserPosition);
   1660 
   1661                 err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident,
   1662                         *curTag, curIsStyled, curFormat, curIsFormatted,
   1663                         product, NO_PSEUDOLOCALIZATION, overwrite, &skippedResourceNames, outTable);
   1664 
   1665                 if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR?
   1666                     hasErrors = localHasErrors = true;
   1667                 }
   1668                 else if (err == NO_ERROR) {
   1669                     if (curType == string16 && !curParams.language[0] && !curParams.country[0]) {
   1670                         outTable->addDefaultLocalization(curName);
   1671                     }
   1672                     if (curIsPseudolocalizable && localeIsDefined(curParams)
   1673                             && bundle->getPseudolocalize() > 0) {
   1674                         // pseudolocalize here
   1675                         if ((PSEUDO_ACCENTED & bundle->getPseudolocalize()) ==
   1676                            PSEUDO_ACCENTED) {
   1677                             block.setPosition(parserPosition);
   1678                             err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType,
   1679                                     ident, *curTag, curIsStyled, curFormat,
   1680                                     curIsFormatted, product,
   1681                                     PSEUDO_ACCENTED, overwrite, &skippedResourceNames, outTable);
   1682                         }
   1683                         if ((PSEUDO_BIDI & bundle->getPseudolocalize()) ==
   1684                            PSEUDO_BIDI) {
   1685                             block.setPosition(parserPosition);
   1686                             err = parseAndAddEntry(bundle, in, &block, pseudoBidiParams,
   1687                                     myPackage, curType, ident, *curTag, curIsStyled, curFormat,
   1688                                     curIsFormatted, product,
   1689                                     PSEUDO_BIDI, overwrite, &skippedResourceNames, outTable);
   1690                         }
   1691                         if (err != NO_ERROR) {
   1692                             hasErrors = localHasErrors = true;
   1693                         }
   1694                     }
   1695                 }
   1696             }
   1697 
   1698 #if 0
   1699             if (comment.size() > 0) {
   1700                 printf("Comment for @%s:%s/%s: %s\n", String8(myPackage).string(),
   1701                        String8(curType).string(), String8(ident).string(),
   1702                        String8(comment).string());
   1703             }
   1704 #endif
   1705             if (!localHasErrors) {
   1706                 outTable->appendComment(myPackage, curType, ident, comment, false);
   1707             }
   1708         }
   1709         else if (code == ResXMLTree::END_TAG) {
   1710             if (strcmp16(block.getElementName(&len), resources16.string()) != 0) {
   1711                 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1712                         "Unexpected end tag %s\n", String8(block.getElementName(&len)).string());
   1713                 return UNKNOWN_ERROR;
   1714             }
   1715         }
   1716         else if (code == ResXMLTree::START_NAMESPACE || code == ResXMLTree::END_NAMESPACE) {
   1717         }
   1718         else if (code == ResXMLTree::TEXT) {
   1719             if (isWhitespace(block.getText(&len))) {
   1720                 continue;
   1721             }
   1722             SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1723                     "Found text \"%s\" where item tag is expected\n",
   1724                     String8(block.getText(&len)).string());
   1725             return UNKNOWN_ERROR;
   1726         }
   1727     }
   1728 
   1729     // For every resource defined, there must be exist one variant with a product attribute
   1730     // set to 'default' (or no product attribute at all).
   1731     // We check to see that for every resource that was ignored because of a mismatched
   1732     // product attribute, some product variant of that resource was processed.
   1733     for (size_t i = 0; i < skippedResourceNames.size(); i++) {
   1734         if (skippedResourceNames[i]) {
   1735             const type_ident_pair_t& p = skippedResourceNames.keyAt(i);
   1736             if (!outTable->hasBagOrEntry(myPackage, p.type, p.ident)) {
   1737                 const char* bundleProduct =
   1738                         (bundle->getProduct() == NULL) ? "" : bundle->getProduct();
   1739                 fprintf(stderr, "In resource file %s: %s\n",
   1740                         in->getPrintableSource().string(),
   1741                         curParams.toString().string());
   1742 
   1743                 fprintf(stderr, "\t%s '%s' does not match product %s.\n"
   1744                         "\tYou may have forgotten to include a 'default' product variant"
   1745                         " of the resource.\n",
   1746                         String8(p.type).string(), String8(p.ident).string(),
   1747                         bundleProduct[0] == 0 ? "default" : bundleProduct);
   1748                 return UNKNOWN_ERROR;
   1749             }
   1750         }
   1751     }
   1752 
   1753     return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR;
   1754 }
   1755 
   1756 ResourceTable::ResourceTable(Bundle* bundle, const String16& assetsPackage, ResourceTable::PackageType type)
   1757     : mAssetsPackage(assetsPackage)
   1758     , mPackageType(type)
   1759     , mTypeIdOffset(0)
   1760     , mNumLocal(0)
   1761     , mBundle(bundle)
   1762 {
   1763     ssize_t packageId = -1;
   1764     switch (mPackageType) {
   1765         case App:
   1766         case AppFeature:
   1767             packageId = 0x7f;
   1768             break;
   1769 
   1770         case System:
   1771             packageId = 0x01;
   1772             break;
   1773 
   1774         case SharedLibrary:
   1775             packageId = 0x00;
   1776             break;
   1777 
   1778         default:
   1779             assert(0);
   1780             break;
   1781     }
   1782     sp<Package> package = new Package(mAssetsPackage, packageId);
   1783     mPackages.add(assetsPackage, package);
   1784     mOrderedPackages.add(package);
   1785 
   1786     // Every resource table always has one first entry, the bag attributes.
   1787     const SourcePos unknown(String8("????"), 0);
   1788     getType(mAssetsPackage, String16("attr"), unknown);
   1789 }
   1790 
   1791 static uint32_t findLargestTypeIdForPackage(const ResTable& table, const String16& packageName) {
   1792     const size_t basePackageCount = table.getBasePackageCount();
   1793     for (size_t i = 0; i < basePackageCount; i++) {
   1794         if (packageName == table.getBasePackageName(i)) {
   1795             return table.getLastTypeIdForPackage(i);
   1796         }
   1797     }
   1798     return 0;
   1799 }
   1800 
   1801 status_t ResourceTable::addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets)
   1802 {
   1803     status_t err = assets->buildIncludedResources(bundle);
   1804     if (err != NO_ERROR) {
   1805         return err;
   1806     }
   1807 
   1808     mAssets = assets;
   1809     mTypeIdOffset = findLargestTypeIdForPackage(assets->getIncludedResources(), mAssetsPackage);
   1810 
   1811     const String8& featureAfter = bundle->getFeatureAfterPackage();
   1812     if (!featureAfter.isEmpty()) {
   1813         AssetManager featureAssetManager;
   1814         if (!featureAssetManager.addAssetPath(featureAfter, NULL)) {
   1815             fprintf(stderr, "ERROR: Feature package '%s' not found.\n",
   1816                     featureAfter.string());
   1817             return UNKNOWN_ERROR;
   1818         }
   1819 
   1820         const ResTable& featureTable = featureAssetManager.getResources(false);
   1821         mTypeIdOffset = std::max(mTypeIdOffset,
   1822                 findLargestTypeIdForPackage(featureTable, mAssetsPackage));
   1823     }
   1824 
   1825     return NO_ERROR;
   1826 }
   1827 
   1828 status_t ResourceTable::addPublic(const SourcePos& sourcePos,
   1829                                   const String16& package,
   1830                                   const String16& type,
   1831                                   const String16& name,
   1832                                   const uint32_t ident)
   1833 {
   1834     uint32_t rid = mAssets->getIncludedResources()
   1835         .identifierForName(name.string(), name.size(),
   1836                            type.string(), type.size(),
   1837                            package.string(), package.size());
   1838     if (rid != 0) {
   1839         sourcePos.error("Error declaring public resource %s/%s for included package %s\n",
   1840                 String8(type).string(), String8(name).string(),
   1841                 String8(package).string());
   1842         return UNKNOWN_ERROR;
   1843     }
   1844 
   1845     sp<Type> t = getType(package, type, sourcePos);
   1846     if (t == NULL) {
   1847         return UNKNOWN_ERROR;
   1848     }
   1849     return t->addPublic(sourcePos, name, ident);
   1850 }
   1851 
   1852 status_t ResourceTable::addEntry(const SourcePos& sourcePos,
   1853                                  const String16& package,
   1854                                  const String16& type,
   1855                                  const String16& name,
   1856                                  const String16& value,
   1857                                  const Vector<StringPool::entry_style_span>* style,
   1858                                  const ResTable_config* params,
   1859                                  const bool doSetIndex,
   1860                                  const int32_t format,
   1861                                  const bool overwrite)
   1862 {
   1863     uint32_t rid = mAssets->getIncludedResources()
   1864         .identifierForName(name.string(), name.size(),
   1865                            type.string(), type.size(),
   1866                            package.string(), package.size());
   1867     if (rid != 0) {
   1868         sourcePos.error("Resource entry %s/%s is already defined in package %s.",
   1869                 String8(type).string(), String8(name).string(), String8(package).string());
   1870         return UNKNOWN_ERROR;
   1871     }
   1872 
   1873     sp<Entry> e = getEntry(package, type, name, sourcePos, overwrite,
   1874                            params, doSetIndex);
   1875     if (e == NULL) {
   1876         return UNKNOWN_ERROR;
   1877     }
   1878     status_t err = e->setItem(sourcePos, value, style, format, overwrite);
   1879     if (err == NO_ERROR) {
   1880         mNumLocal++;
   1881     }
   1882     return err;
   1883 }
   1884 
   1885 status_t ResourceTable::startBag(const SourcePos& sourcePos,
   1886                                  const String16& package,
   1887                                  const String16& type,
   1888                                  const String16& name,
   1889                                  const String16& bagParent,
   1890                                  const ResTable_config* params,
   1891                                  bool overlay,
   1892                                  bool replace, bool /* isId */)
   1893 {
   1894     status_t result = NO_ERROR;
   1895 
   1896     // Check for adding entries in other packages...  for now we do
   1897     // nothing.  We need to do the right thing here to support skinning.
   1898     uint32_t rid = mAssets->getIncludedResources()
   1899     .identifierForName(name.string(), name.size(),
   1900                        type.string(), type.size(),
   1901                        package.string(), package.size());
   1902     if (rid != 0) {
   1903         sourcePos.error("Resource entry %s/%s is already defined in package %s.",
   1904                 String8(type).string(), String8(name).string(), String8(package).string());
   1905         return UNKNOWN_ERROR;
   1906     }
   1907 
   1908     if (overlay && !mBundle->getAutoAddOverlay() && !hasBagOrEntry(package, type, name)) {
   1909         bool canAdd = false;
   1910         sp<Package> p = mPackages.valueFor(package);
   1911         if (p != NULL) {
   1912             sp<Type> t = p->getTypes().valueFor(type);
   1913             if (t != NULL) {
   1914                 if (t->getCanAddEntries().indexOf(name) >= 0) {
   1915                     canAdd = true;
   1916                 }
   1917             }
   1918         }
   1919         if (!canAdd) {
   1920             sourcePos.error("Resource does not already exist in overlay at '%s'; use <add-resource> to add.\n",
   1921                             String8(name).string());
   1922             return UNKNOWN_ERROR;
   1923         }
   1924     }
   1925     sp<Entry> e = getEntry(package, type, name, sourcePos, overlay, params);
   1926     if (e == NULL) {
   1927         return UNKNOWN_ERROR;
   1928     }
   1929 
   1930     // If a parent is explicitly specified, set it.
   1931     if (bagParent.size() > 0) {
   1932         e->setParent(bagParent);
   1933     }
   1934 
   1935     if ((result = e->makeItABag(sourcePos)) != NO_ERROR) {
   1936         return result;
   1937     }
   1938 
   1939     if (overlay && replace) {
   1940         return e->emptyBag(sourcePos);
   1941     }
   1942     return result;
   1943 }
   1944 
   1945 status_t ResourceTable::addBag(const SourcePos& sourcePos,
   1946                                const String16& package,
   1947                                const String16& type,
   1948                                const String16& name,
   1949                                const String16& bagParent,
   1950                                const String16& bagKey,
   1951                                const String16& value,
   1952                                const Vector<StringPool::entry_style_span>* style,
   1953                                const ResTable_config* params,
   1954                                bool replace, bool isId, const int32_t format)
   1955 {
   1956     // Check for adding entries in other packages...  for now we do
   1957     // nothing.  We need to do the right thing here to support skinning.
   1958     uint32_t rid = mAssets->getIncludedResources()
   1959         .identifierForName(name.string(), name.size(),
   1960                            type.string(), type.size(),
   1961                            package.string(), package.size());
   1962     if (rid != 0) {
   1963         return NO_ERROR;
   1964     }
   1965 
   1966 #if 0
   1967     if (name == String16("left")) {
   1968         printf("Adding bag left: file=%s, line=%d, type=%s\n",
   1969                sourcePos.file.striing(), sourcePos.line, String8(type).string());
   1970     }
   1971 #endif
   1972     sp<Entry> e = getEntry(package, type, name, sourcePos, replace, params);
   1973     if (e == NULL) {
   1974         return UNKNOWN_ERROR;
   1975     }
   1976 
   1977     // If a parent is explicitly specified, set it.
   1978     if (bagParent.size() > 0) {
   1979         e->setParent(bagParent);
   1980     }
   1981 
   1982     const bool first = e->getBag().indexOfKey(bagKey) < 0;
   1983     status_t err = e->addToBag(sourcePos, bagKey, value, style, replace, isId, format);
   1984     if (err == NO_ERROR && first) {
   1985         mNumLocal++;
   1986     }
   1987     return err;
   1988 }
   1989 
   1990 bool ResourceTable::hasBagOrEntry(const String16& package,
   1991                                   const String16& type,
   1992                                   const String16& name) const
   1993 {
   1994     // First look for this in the included resources...
   1995     uint32_t rid = mAssets->getIncludedResources()
   1996         .identifierForName(name.string(), name.size(),
   1997                            type.string(), type.size(),
   1998                            package.string(), package.size());
   1999     if (rid != 0) {
   2000         return true;
   2001     }
   2002 
   2003     sp<Package> p = mPackages.valueFor(package);
   2004     if (p != NULL) {
   2005         sp<Type> t = p->getTypes().valueFor(type);
   2006         if (t != NULL) {
   2007             sp<ConfigList> c =  t->getConfigs().valueFor(name);
   2008             if (c != NULL) return true;
   2009         }
   2010     }
   2011 
   2012     return false;
   2013 }
   2014 
   2015 bool ResourceTable::hasBagOrEntry(const String16& package,
   2016                                   const String16& type,
   2017                                   const String16& name,
   2018                                   const ResTable_config& config) const
   2019 {
   2020     // First look for this in the included resources...
   2021     uint32_t rid = mAssets->getIncludedResources()
   2022         .identifierForName(name.string(), name.size(),
   2023                            type.string(), type.size(),
   2024                            package.string(), package.size());
   2025     if (rid != 0) {
   2026         return true;
   2027     }
   2028 
   2029     sp<Package> p = mPackages.valueFor(package);
   2030     if (p != NULL) {
   2031         sp<Type> t = p->getTypes().valueFor(type);
   2032         if (t != NULL) {
   2033             sp<ConfigList> c =  t->getConfigs().valueFor(name);
   2034             if (c != NULL) {
   2035                 sp<Entry> e = c->getEntries().valueFor(config);
   2036                 if (e != NULL) {
   2037                     return true;
   2038                 }
   2039             }
   2040         }
   2041     }
   2042 
   2043     return false;
   2044 }
   2045 
   2046 bool ResourceTable::hasBagOrEntry(const String16& ref,
   2047                                   const String16* defType,
   2048                                   const String16* defPackage)
   2049 {
   2050     String16 package, type, name;
   2051     if (!ResTable::expandResourceRef(ref.string(), ref.size(), &package, &type, &name,
   2052                 defType, defPackage ? defPackage:&mAssetsPackage, NULL)) {
   2053         return false;
   2054     }
   2055     return hasBagOrEntry(package, type, name);
   2056 }
   2057 
   2058 bool ResourceTable::appendComment(const String16& package,
   2059                                   const String16& type,
   2060                                   const String16& name,
   2061                                   const String16& comment,
   2062                                   bool onlyIfEmpty)
   2063 {
   2064     if (comment.size() <= 0) {
   2065         return true;
   2066     }
   2067 
   2068     sp<Package> p = mPackages.valueFor(package);
   2069     if (p != NULL) {
   2070         sp<Type> t = p->getTypes().valueFor(type);
   2071         if (t != NULL) {
   2072             sp<ConfigList> c =  t->getConfigs().valueFor(name);
   2073             if (c != NULL) {
   2074                 c->appendComment(comment, onlyIfEmpty);
   2075                 return true;
   2076             }
   2077         }
   2078     }
   2079     return false;
   2080 }
   2081 
   2082 bool ResourceTable::appendTypeComment(const String16& package,
   2083                                       const String16& type,
   2084                                       const String16& name,
   2085                                       const String16& comment)
   2086 {
   2087     if (comment.size() <= 0) {
   2088         return true;
   2089     }
   2090 
   2091     sp<Package> p = mPackages.valueFor(package);
   2092     if (p != NULL) {
   2093         sp<Type> t = p->getTypes().valueFor(type);
   2094         if (t != NULL) {
   2095             sp<ConfigList> c =  t->getConfigs().valueFor(name);
   2096             if (c != NULL) {
   2097                 c->appendTypeComment(comment);
   2098                 return true;
   2099             }
   2100         }
   2101     }
   2102     return false;
   2103 }
   2104 
   2105 void ResourceTable::canAddEntry(const SourcePos& pos,
   2106         const String16& package, const String16& type, const String16& name)
   2107 {
   2108     sp<Type> t = getType(package, type, pos);
   2109     if (t != NULL) {
   2110         t->canAddEntry(name);
   2111     }
   2112 }
   2113 
   2114 size_t ResourceTable::size() const {
   2115     return mPackages.size();
   2116 }
   2117 
   2118 size_t ResourceTable::numLocalResources() const {
   2119     return mNumLocal;
   2120 }
   2121 
   2122 bool ResourceTable::hasResources() const {
   2123     return mNumLocal > 0;
   2124 }
   2125 
   2126 sp<AaptFile> ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter,
   2127         const bool isBase)
   2128 {
   2129     sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
   2130     status_t err = flatten(bundle, filter, data, isBase);
   2131     return err == NO_ERROR ? data : NULL;
   2132 }
   2133 
   2134 inline uint32_t ResourceTable::getResId(const sp<Package>& p,
   2135                                         const sp<Type>& t,
   2136                                         uint32_t nameId)
   2137 {
   2138     return makeResId(p->getAssignedId(), t->getIndex(), nameId);
   2139 }
   2140 
   2141 uint32_t ResourceTable::getResId(const String16& package,
   2142                                  const String16& type,
   2143                                  const String16& name,
   2144                                  bool onlyPublic) const
   2145 {
   2146     uint32_t id = ResourceIdCache::lookup(package, type, name, onlyPublic);
   2147     if (id != 0) return id;     // cache hit
   2148 
   2149     // First look for this in the included resources...
   2150     uint32_t specFlags = 0;
   2151     uint32_t rid = mAssets->getIncludedResources()
   2152         .identifierForName(name.string(), name.size(),
   2153                            type.string(), type.size(),
   2154                            package.string(), package.size(),
   2155                            &specFlags);
   2156     if (rid != 0) {
   2157         if (onlyPublic) {
   2158             if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) {
   2159                 return 0;
   2160             }
   2161         }
   2162 
   2163         return ResourceIdCache::store(package, type, name, onlyPublic, rid);
   2164     }
   2165 
   2166     sp<Package> p = mPackages.valueFor(package);
   2167     if (p == NULL) return 0;
   2168     sp<Type> t = p->getTypes().valueFor(type);
   2169     if (t == NULL) return 0;
   2170     sp<ConfigList> c = t->getConfigs().valueFor(name);
   2171     if (c == NULL) {
   2172         if (type != String16("attr")) {
   2173             return 0;
   2174         }
   2175         t = p->getTypes().valueFor(String16(kAttrPrivateType));
   2176         if (t == NULL) return 0;
   2177         c = t->getConfigs().valueFor(name);
   2178         if (c == NULL) return 0;
   2179     }
   2180     int32_t ei = c->getEntryIndex();
   2181     if (ei < 0) return 0;
   2182 
   2183     return ResourceIdCache::store(package, type, name, onlyPublic,
   2184             getResId(p, t, ei));
   2185 }
   2186 
   2187 uint32_t ResourceTable::getResId(const String16& ref,
   2188                                  const String16* defType,
   2189                                  const String16* defPackage,
   2190                                  const char** outErrorMsg,
   2191                                  bool onlyPublic) const
   2192 {
   2193     String16 package, type, name;
   2194     bool refOnlyPublic = true;
   2195     if (!ResTable::expandResourceRef(
   2196         ref.string(), ref.size(), &package, &type, &name,
   2197         defType, defPackage ? defPackage:&mAssetsPackage,
   2198         outErrorMsg, &refOnlyPublic)) {
   2199         if (kIsDebug) {
   2200             printf("Expanding resource: ref=%s\n", String8(ref).string());
   2201             printf("Expanding resource: defType=%s\n",
   2202                     defType ? String8(*defType).string() : "NULL");
   2203             printf("Expanding resource: defPackage=%s\n",
   2204                     defPackage ? String8(*defPackage).string() : "NULL");
   2205             printf("Expanding resource: ref=%s\n", String8(ref).string());
   2206             printf("Expanded resource: p=%s, t=%s, n=%s, res=0\n",
   2207                     String8(package).string(), String8(type).string(),
   2208                     String8(name).string());
   2209         }
   2210         return 0;
   2211     }
   2212     uint32_t res = getResId(package, type, name, onlyPublic && refOnlyPublic);
   2213     if (kIsDebug) {
   2214         printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n",
   2215                 String8(package).string(), String8(type).string(),
   2216                 String8(name).string(), res);
   2217     }
   2218     if (res == 0) {
   2219         if (outErrorMsg)
   2220             *outErrorMsg = "No resource found that matches the given name";
   2221     }
   2222     return res;
   2223 }
   2224 
   2225 bool ResourceTable::isValidResourceName(const String16& s)
   2226 {
   2227     const char16_t* p = s.string();
   2228     bool first = true;
   2229     while (*p) {
   2230         if ((*p >= 'a' && *p <= 'z')
   2231             || (*p >= 'A' && *p <= 'Z')
   2232             || *p == '_'
   2233             || (!first && *p >= '0' && *p <= '9')) {
   2234             first = false;
   2235             p++;
   2236             continue;
   2237         }
   2238         return false;
   2239     }
   2240     return true;
   2241 }
   2242 
   2243 bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool,
   2244                                   const String16& str,
   2245                                   bool preserveSpaces, bool coerceType,
   2246                                   uint32_t attrID,
   2247                                   const Vector<StringPool::entry_style_span>* style,
   2248                                   String16* outStr, void* accessorCookie,
   2249                                   uint32_t attrType, const String8* configTypeName,
   2250                                   const ConfigDescription* config)
   2251 {
   2252     String16 finalStr;
   2253 
   2254     bool res = true;
   2255     if (style == NULL || style->size() == 0) {
   2256         // Text is not styled so it can be any type...  let's figure it out.
   2257         res = mAssets->getIncludedResources()
   2258             .stringToValue(outValue, &finalStr, str.string(), str.size(), preserveSpaces,
   2259                             coerceType, attrID, NULL, &mAssetsPackage, this,
   2260                            accessorCookie, attrType);
   2261     } else {
   2262         // Styled text can only be a string, and while collecting the style
   2263         // information we have already processed that string!
   2264         outValue->size = sizeof(Res_value);
   2265         outValue->res0 = 0;
   2266         outValue->dataType = outValue->TYPE_STRING;
   2267         outValue->data = 0;
   2268         finalStr = str;
   2269     }
   2270 
   2271     if (!res) {
   2272         return false;
   2273     }
   2274 
   2275     if (outValue->dataType == outValue->TYPE_STRING) {
   2276         // Should do better merging styles.
   2277         if (pool) {
   2278             String8 configStr;
   2279             if (config != NULL) {
   2280                 configStr = config->toString();
   2281             } else {
   2282                 configStr = "(null)";
   2283             }
   2284             if (kIsDebug) {
   2285                 printf("Adding to pool string style #%zu config %s: %s\n",
   2286                         style != NULL ? style->size() : 0U,
   2287                         configStr.string(), String8(finalStr).string());
   2288             }
   2289             if (style != NULL && style->size() > 0) {
   2290                 outValue->data = pool->add(finalStr, *style, configTypeName, config);
   2291             } else {
   2292                 outValue->data = pool->add(finalStr, true, configTypeName, config);
   2293             }
   2294         } else {
   2295             // Caller will fill this in later.
   2296             outValue->data = 0;
   2297         }
   2298 
   2299         if (outStr) {
   2300             *outStr = finalStr;
   2301         }
   2302 
   2303     }
   2304 
   2305     return true;
   2306 }
   2307 
   2308 uint32_t ResourceTable::getCustomResource(
   2309     const String16& package, const String16& type, const String16& name) const
   2310 {
   2311     //printf("getCustomResource: %s %s %s\n", String8(package).string(),
   2312     //       String8(type).string(), String8(name).string());
   2313     sp<Package> p = mPackages.valueFor(package);
   2314     if (p == NULL) return 0;
   2315     sp<Type> t = p->getTypes().valueFor(type);
   2316     if (t == NULL) return 0;
   2317     sp<ConfigList> c =  t->getConfigs().valueFor(name);
   2318     if (c == NULL) {
   2319         if (type != String16("attr")) {
   2320             return 0;
   2321         }
   2322         t = p->getTypes().valueFor(String16(kAttrPrivateType));
   2323         if (t == NULL) return 0;
   2324         c = t->getConfigs().valueFor(name);
   2325         if (c == NULL) return 0;
   2326     }
   2327     int32_t ei = c->getEntryIndex();
   2328     if (ei < 0) return 0;
   2329     return getResId(p, t, ei);
   2330 }
   2331 
   2332 uint32_t ResourceTable::getCustomResourceWithCreation(
   2333         const String16& package, const String16& type, const String16& name,
   2334         const bool createIfNotFound)
   2335 {
   2336     uint32_t resId = getCustomResource(package, type, name);
   2337     if (resId != 0 || !createIfNotFound) {
   2338         return resId;
   2339     }
   2340 
   2341     if (mAssetsPackage != package) {
   2342         mCurrentXmlPos.error("creating resource for external package %s: %s/%s.",
   2343                 String8(package).string(), String8(type).string(), String8(name).string());
   2344         if (package == String16("android")) {
   2345             mCurrentXmlPos.printf("did you mean to use @+id instead of @+android:id?");
   2346         }
   2347         return 0;
   2348     }
   2349 
   2350     String16 value("false");
   2351     status_t status = addEntry(mCurrentXmlPos, package, type, name, value, NULL, NULL, true);
   2352     if (status == NO_ERROR) {
   2353         resId = getResId(package, type, name);
   2354         return resId;
   2355     }
   2356     return 0;
   2357 }
   2358 
   2359 uint32_t ResourceTable::getRemappedPackage(uint32_t origPackage) const
   2360 {
   2361     return origPackage;
   2362 }
   2363 
   2364 bool ResourceTable::getAttributeType(uint32_t attrID, uint32_t* outType)
   2365 {
   2366     //printf("getAttributeType #%08x\n", attrID);
   2367     Res_value value;
   2368     if (getItemValue(attrID, ResTable_map::ATTR_TYPE, &value)) {
   2369         //printf("getAttributeType #%08x (%s): #%08x\n", attrID,
   2370         //       String8(getEntry(attrID)->getName()).string(), value.data);
   2371         *outType = value.data;
   2372         return true;
   2373     }
   2374     return false;
   2375 }
   2376 
   2377 bool ResourceTable::getAttributeMin(uint32_t attrID, uint32_t* outMin)
   2378 {
   2379     //printf("getAttributeMin #%08x\n", attrID);
   2380     Res_value value;
   2381     if (getItemValue(attrID, ResTable_map::ATTR_MIN, &value)) {
   2382         *outMin = value.data;
   2383         return true;
   2384     }
   2385     return false;
   2386 }
   2387 
   2388 bool ResourceTable::getAttributeMax(uint32_t attrID, uint32_t* outMax)
   2389 {
   2390     //printf("getAttributeMax #%08x\n", attrID);
   2391     Res_value value;
   2392     if (getItemValue(attrID, ResTable_map::ATTR_MAX, &value)) {
   2393         *outMax = value.data;
   2394         return true;
   2395     }
   2396     return false;
   2397 }
   2398 
   2399 uint32_t ResourceTable::getAttributeL10N(uint32_t attrID)
   2400 {
   2401     //printf("getAttributeL10N #%08x\n", attrID);
   2402     Res_value value;
   2403     if (getItemValue(attrID, ResTable_map::ATTR_L10N, &value)) {
   2404         return value.data;
   2405     }
   2406     return ResTable_map::L10N_NOT_REQUIRED;
   2407 }
   2408 
   2409 bool ResourceTable::getLocalizationSetting()
   2410 {
   2411     return mBundle->getRequireLocalization();
   2412 }
   2413 
   2414 void ResourceTable::reportError(void* accessorCookie, const char* fmt, ...)
   2415 {
   2416     if (accessorCookie != NULL && fmt != NULL) {
   2417         AccessorCookie* ac = (AccessorCookie*)accessorCookie;
   2418         int retval=0;
   2419         char buf[1024];
   2420         va_list ap;
   2421         va_start(ap, fmt);
   2422         retval = vsnprintf(buf, sizeof(buf), fmt, ap);
   2423         va_end(ap);
   2424         ac->sourcePos.error("Error: %s (at '%s' with value '%s').\n",
   2425                             buf, ac->attr.string(), ac->value.string());
   2426     }
   2427 }
   2428 
   2429 bool ResourceTable::getAttributeKeys(
   2430     uint32_t attrID, Vector<String16>* outKeys)
   2431 {
   2432     sp<const Entry> e = getEntry(attrID);
   2433     if (e != NULL) {
   2434         const size_t N = e->getBag().size();
   2435         for (size_t i=0; i<N; i++) {
   2436             const String16& key = e->getBag().keyAt(i);
   2437             if (key.size() > 0 && key.string()[0] != '^') {
   2438                 outKeys->add(key);
   2439             }
   2440         }
   2441         return true;
   2442     }
   2443     return false;
   2444 }
   2445 
   2446 bool ResourceTable::getAttributeEnum(
   2447     uint32_t attrID, const char16_t* name, size_t nameLen,
   2448     Res_value* outValue)
   2449 {
   2450     //printf("getAttributeEnum #%08x %s\n", attrID, String8(name, nameLen).string());
   2451     String16 nameStr(name, nameLen);
   2452     sp<const Entry> e = getEntry(attrID);
   2453     if (e != NULL) {
   2454         const size_t N = e->getBag().size();
   2455         for (size_t i=0; i<N; i++) {
   2456             //printf("Comparing %s to %s\n", String8(name, nameLen).string(),
   2457             //       String8(e->getBag().keyAt(i)).string());
   2458             if (e->getBag().keyAt(i) == nameStr) {
   2459                 return getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, outValue);
   2460             }
   2461         }
   2462     }
   2463     return false;
   2464 }
   2465 
   2466 bool ResourceTable::getAttributeFlags(
   2467     uint32_t attrID, const char16_t* name, size_t nameLen,
   2468     Res_value* outValue)
   2469 {
   2470     outValue->dataType = Res_value::TYPE_INT_HEX;
   2471     outValue->data = 0;
   2472 
   2473     //printf("getAttributeFlags #%08x %s\n", attrID, String8(name, nameLen).string());
   2474     String16 nameStr(name, nameLen);
   2475     sp<const Entry> e = getEntry(attrID);
   2476     if (e != NULL) {
   2477         const size_t N = e->getBag().size();
   2478 
   2479         const char16_t* end = name + nameLen;
   2480         const char16_t* pos = name;
   2481         while (pos < end) {
   2482             const char16_t* start = pos;
   2483             while (pos < end && *pos != '|') {
   2484                 pos++;
   2485             }
   2486 
   2487             String16 nameStr(start, pos-start);
   2488             size_t i;
   2489             for (i=0; i<N; i++) {
   2490                 //printf("Comparing \"%s\" to \"%s\"\n", String8(nameStr).string(),
   2491                 //       String8(e->getBag().keyAt(i)).string());
   2492                 if (e->getBag().keyAt(i) == nameStr) {
   2493                     Res_value val;
   2494                     bool got = getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, &val);
   2495                     if (!got) {
   2496                         return false;
   2497                     }
   2498                     //printf("Got value: 0x%08x\n", val.data);
   2499                     outValue->data |= val.data;
   2500                     break;
   2501                 }
   2502             }
   2503 
   2504             if (i >= N) {
   2505                 // Didn't find this flag identifier.
   2506                 return false;
   2507             }
   2508             pos++;
   2509         }
   2510 
   2511         return true;
   2512     }
   2513     return false;
   2514 }
   2515 
   2516 status_t ResourceTable::assignResourceIds()
   2517 {
   2518     const size_t N = mOrderedPackages.size();
   2519     size_t pi;
   2520     status_t firstError = NO_ERROR;
   2521 
   2522     // First generate all bag attributes and assign indices.
   2523     for (pi=0; pi<N; pi++) {
   2524         sp<Package> p = mOrderedPackages.itemAt(pi);
   2525         if (p == NULL || p->getTypes().size() == 0) {
   2526             // Empty, skip!
   2527             continue;
   2528         }
   2529 
   2530         if (mPackageType == System) {
   2531             p->movePrivateAttrs();
   2532         }
   2533 
   2534         // This has no sense for packages being built as AppFeature (aka with a non-zero offset).
   2535         status_t err = p->applyPublicTypeOrder();
   2536         if (err != NO_ERROR && firstError == NO_ERROR) {
   2537             firstError = err;
   2538         }
   2539 
   2540         // Generate attributes...
   2541         const size_t N = p->getOrderedTypes().size();
   2542         size_t ti;
   2543         for (ti=0; ti<N; ti++) {
   2544             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2545             if (t == NULL) {
   2546                 continue;
   2547             }
   2548             const size_t N = t->getOrderedConfigs().size();
   2549             for (size_t ci=0; ci<N; ci++) {
   2550                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
   2551                 if (c == NULL) {
   2552                     continue;
   2553                 }
   2554                 const size_t N = c->getEntries().size();
   2555                 for (size_t ei=0; ei<N; ei++) {
   2556                     sp<Entry> e = c->getEntries().valueAt(ei);
   2557                     if (e == NULL) {
   2558                         continue;
   2559                     }
   2560                     status_t err = e->generateAttributes(this, p->getName());
   2561                     if (err != NO_ERROR && firstError == NO_ERROR) {
   2562                         firstError = err;
   2563                     }
   2564                 }
   2565             }
   2566         }
   2567 
   2568         uint32_t typeIdOffset = 0;
   2569         if (mPackageType == AppFeature && p->getName() == mAssetsPackage) {
   2570             typeIdOffset = mTypeIdOffset;
   2571         }
   2572 
   2573         const SourcePos unknown(String8("????"), 0);
   2574         sp<Type> attr = p->getType(String16("attr"), unknown);
   2575 
   2576         // Assign indices...
   2577         const size_t typeCount = p->getOrderedTypes().size();
   2578         for (size_t ti = 0; ti < typeCount; ti++) {
   2579             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2580             if (t == NULL) {
   2581                 continue;
   2582             }
   2583 
   2584             err = t->applyPublicEntryOrder();
   2585             if (err != NO_ERROR && firstError == NO_ERROR) {
   2586                 firstError = err;
   2587             }
   2588 
   2589             const size_t N = t->getOrderedConfigs().size();
   2590             t->setIndex(ti + 1 + typeIdOffset);
   2591 
   2592             LOG_ALWAYS_FATAL_IF(ti == 0 && attr != t,
   2593                                 "First type is not attr!");
   2594 
   2595             for (size_t ei=0; ei<N; ei++) {
   2596                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ei);
   2597                 if (c == NULL) {
   2598                     continue;
   2599                 }
   2600                 c->setEntryIndex(ei);
   2601             }
   2602         }
   2603 
   2604 
   2605         // Assign resource IDs to keys in bags...
   2606         for (size_t ti = 0; ti < typeCount; ti++) {
   2607             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2608             if (t == NULL) {
   2609                 continue;
   2610             }
   2611 
   2612             const size_t N = t->getOrderedConfigs().size();
   2613             for (size_t ci=0; ci<N; ci++) {
   2614                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
   2615                 if (c == NULL) {
   2616                     continue;
   2617                 }
   2618                 //printf("Ordered config #%d: %p\n", ci, c.get());
   2619                 const size_t N = c->getEntries().size();
   2620                 for (size_t ei=0; ei<N; ei++) {
   2621                     sp<Entry> e = c->getEntries().valueAt(ei);
   2622                     if (e == NULL) {
   2623                         continue;
   2624                     }
   2625                     status_t err = e->assignResourceIds(this, p->getName());
   2626                     if (err != NO_ERROR && firstError == NO_ERROR) {
   2627                         firstError = err;
   2628                     }
   2629                 }
   2630             }
   2631         }
   2632     }
   2633     return firstError;
   2634 }
   2635 
   2636 status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols,
   2637         bool skipSymbolsWithoutDefaultLocalization) {
   2638     const size_t N = mOrderedPackages.size();
   2639     const String8 defaultLocale;
   2640     const String16 stringType("string");
   2641     size_t pi;
   2642 
   2643     for (pi=0; pi<N; pi++) {
   2644         sp<Package> p = mOrderedPackages.itemAt(pi);
   2645         if (p->getTypes().size() == 0) {
   2646             // Empty, skip!
   2647             continue;
   2648         }
   2649 
   2650         const size_t N = p->getOrderedTypes().size();
   2651         size_t ti;
   2652 
   2653         for (ti=0; ti<N; ti++) {
   2654             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2655             if (t == NULL) {
   2656                 continue;
   2657             }
   2658 
   2659             const size_t N = t->getOrderedConfigs().size();
   2660             sp<AaptSymbols> typeSymbols;
   2661             if (t->getName() == String16(kAttrPrivateType)) {
   2662                 typeSymbols = outSymbols->addNestedSymbol(String8("attr"), t->getPos());
   2663             } else {
   2664                 typeSymbols = outSymbols->addNestedSymbol(String8(t->getName()), t->getPos());
   2665             }
   2666 
   2667             if (typeSymbols == NULL) {
   2668                 return UNKNOWN_ERROR;
   2669             }
   2670 
   2671             for (size_t ci=0; ci<N; ci++) {
   2672                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
   2673                 if (c == NULL) {
   2674                     continue;
   2675                 }
   2676                 uint32_t rid = getResId(p, t, ci);
   2677                 if (rid == 0) {
   2678                     return UNKNOWN_ERROR;
   2679                 }
   2680                 if (Res_GETPACKAGE(rid) + 1 == p->getAssignedId()) {
   2681 
   2682                     if (skipSymbolsWithoutDefaultLocalization &&
   2683                             t->getName() == stringType) {
   2684 
   2685                         // Don't generate symbols for strings without a default localization.
   2686                         if (mHasDefaultLocalization.find(c->getName())
   2687                                 == mHasDefaultLocalization.end()) {
   2688                             // printf("Skip symbol [%08x] %s\n", rid,
   2689                             //          String8(c->getName()).string());
   2690                             continue;
   2691                         }
   2692                     }
   2693 
   2694                     typeSymbols->addSymbol(String8(c->getName()), rid, c->getPos());
   2695 
   2696                     String16 comment(c->getComment());
   2697                     typeSymbols->appendComment(String8(c->getName()), comment, c->getPos());
   2698                     //printf("Type symbol [%08x] %s comment: %s\n", rid,
   2699                     //        String8(c->getName()).string(), String8(comment).string());
   2700                     comment = c->getTypeComment();
   2701                     typeSymbols->appendTypeComment(String8(c->getName()), comment);
   2702                 }
   2703             }
   2704         }
   2705     }
   2706     return NO_ERROR;
   2707 }
   2708 
   2709 
   2710 void
   2711 ResourceTable::addLocalization(const String16& name, const String8& locale, const SourcePos& src)
   2712 {
   2713     mLocalizations[name][locale] = src;
   2714 }
   2715 
   2716 void
   2717 ResourceTable::addDefaultLocalization(const String16& name)
   2718 {
   2719     mHasDefaultLocalization.insert(name);
   2720 }
   2721 
   2722 
   2723 /*!
   2724  * Flag various sorts of localization problems.  '+' indicates checks already implemented;
   2725  * '-' indicates checks that will be implemented in the future.
   2726  *
   2727  * + A localized string for which no default-locale version exists => warning
   2728  * + A string for which no version in an explicitly-requested locale exists => warning
   2729  * + A localized translation of an translateable="false" string => warning
   2730  * - A localized string not provided in every locale used by the table
   2731  */
   2732 status_t
   2733 ResourceTable::validateLocalizations(void)
   2734 {
   2735     status_t err = NO_ERROR;
   2736     const String8 defaultLocale;
   2737 
   2738     // For all strings...
   2739     for (const auto& nameIter : mLocalizations) {
   2740         const std::map<String8, SourcePos>& configSrcMap = nameIter.second;
   2741 
   2742         // Look for strings with no default localization
   2743         if (configSrcMap.count(defaultLocale) == 0) {
   2744             SourcePos().warning("string '%s' has no default translation.",
   2745                     String8(nameIter.first).string());
   2746             if (mBundle->getVerbose()) {
   2747                 for (const auto& locale : configSrcMap) {
   2748                     locale.second.printf("locale %s found", locale.first.string());
   2749                 }
   2750             }
   2751             // !!! TODO: throw an error here in some circumstances
   2752         }
   2753 
   2754         // Check that all requested localizations are present for this string
   2755         if (mBundle->getConfigurations().size() > 0 && mBundle->getRequireLocalization()) {
   2756             const char* allConfigs = mBundle->getConfigurations().string();
   2757             const char* start = allConfigs;
   2758             const char* comma;
   2759 
   2760             std::set<String8> missingConfigs;
   2761             AaptLocaleValue locale;
   2762             do {
   2763                 String8 config;
   2764                 comma = strchr(start, ',');
   2765                 if (comma != NULL) {
   2766                     config.setTo(start, comma - start);
   2767                     start = comma + 1;
   2768                 } else {
   2769                     config.setTo(start);
   2770                 }
   2771 
   2772                 if (!locale.initFromFilterString(config)) {
   2773                     continue;
   2774                 }
   2775 
   2776                 // don't bother with the pseudolocale "en_XA" or "ar_XB"
   2777                 if (config != "en_XA" && config != "ar_XB") {
   2778                     if (configSrcMap.find(config) == configSrcMap.end()) {
   2779                         // okay, no specific localization found.  it's possible that we are
   2780                         // requiring a specific regional localization [e.g. de_DE] but there is an
   2781                         // available string in the generic language localization [e.g. de];
   2782                         // consider that string to have fulfilled the localization requirement.
   2783                         String8 region(config.string(), 2);
   2784                         if (configSrcMap.find(region) == configSrcMap.end() &&
   2785                                 configSrcMap.count(defaultLocale) == 0) {
   2786                             missingConfigs.insert(config);
   2787                         }
   2788                     }
   2789                 }
   2790             } while (comma != NULL);
   2791 
   2792             if (!missingConfigs.empty()) {
   2793                 String8 configStr;
   2794                 for (const auto& iter : missingConfigs) {
   2795                     configStr.appendFormat(" %s", iter.string());
   2796                 }
   2797                 SourcePos().warning("string '%s' is missing %u required localizations:%s",
   2798                         String8(nameIter.first).string(),
   2799                         (unsigned int)missingConfigs.size(),
   2800                         configStr.string());
   2801             }
   2802         }
   2803     }
   2804 
   2805     return err;
   2806 }
   2807 
   2808 status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter,
   2809         const sp<AaptFile>& dest,
   2810         const bool isBase)
   2811 {
   2812     const ConfigDescription nullConfig;
   2813 
   2814     const size_t N = mOrderedPackages.size();
   2815     size_t pi;
   2816 
   2817     const static String16 mipmap16("mipmap");
   2818 
   2819     bool useUTF8 = !bundle->getUTF16StringsOption();
   2820 
   2821     // The libraries this table references.
   2822     Vector<sp<Package> > libraryPackages;
   2823     const ResTable& table = mAssets->getIncludedResources();
   2824     const size_t basePackageCount = table.getBasePackageCount();
   2825     for (size_t i = 0; i < basePackageCount; i++) {
   2826         size_t packageId = table.getBasePackageId(i);
   2827         String16 packageName(table.getBasePackageName(i));
   2828         if (packageId > 0x01 && packageId != 0x7f &&
   2829                 packageName != String16("android")) {
   2830             libraryPackages.add(sp<Package>(new Package(packageName, packageId)));
   2831         }
   2832     }
   2833 
   2834     // Iterate through all data, collecting all values (strings,
   2835     // references, etc).
   2836     StringPool valueStrings(useUTF8);
   2837     Vector<sp<Entry> > allEntries;
   2838     for (pi=0; pi<N; pi++) {
   2839         sp<Package> p = mOrderedPackages.itemAt(pi);
   2840         if (p->getTypes().size() == 0) {
   2841             continue;
   2842         }
   2843 
   2844         StringPool typeStrings(useUTF8);
   2845         StringPool keyStrings(useUTF8);
   2846 
   2847         ssize_t stringsAdded = 0;
   2848         const size_t N = p->getOrderedTypes().size();
   2849         for (size_t ti=0; ti<N; ti++) {
   2850             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2851             if (t == NULL) {
   2852                 typeStrings.add(String16("<empty>"), false);
   2853                 stringsAdded++;
   2854                 continue;
   2855             }
   2856 
   2857             while (stringsAdded < t->getIndex() - 1) {
   2858                 typeStrings.add(String16("<empty>"), false);
   2859                 stringsAdded++;
   2860             }
   2861 
   2862             const String16 typeName(t->getName());
   2863             typeStrings.add(typeName, false);
   2864             stringsAdded++;
   2865 
   2866             // This is a hack to tweak the sorting order of the final strings,
   2867             // to put stuff that is generally not language-specific first.
   2868             String8 configTypeName(typeName);
   2869             if (configTypeName == "drawable" || configTypeName == "layout"
   2870                     || configTypeName == "color" || configTypeName == "anim"
   2871                     || configTypeName == "interpolator" || configTypeName == "animator"
   2872                     || configTypeName == "xml" || configTypeName == "menu"
   2873                     || configTypeName == "mipmap" || configTypeName == "raw") {
   2874                 configTypeName = "1complex";
   2875             } else {
   2876                 configTypeName = "2value";
   2877             }
   2878 
   2879             // mipmaps don't get filtered, so they will
   2880             // allways end up in the base. Make sure they
   2881             // don't end up in a split.
   2882             if (typeName == mipmap16 && !isBase) {
   2883                 continue;
   2884             }
   2885 
   2886             const bool filterable = (typeName != mipmap16);
   2887 
   2888             const size_t N = t->getOrderedConfigs().size();
   2889             for (size_t ci=0; ci<N; ci++) {
   2890                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
   2891                 if (c == NULL) {
   2892                     continue;
   2893                 }
   2894                 const size_t N = c->getEntries().size();
   2895                 for (size_t ei=0; ei<N; ei++) {
   2896                     ConfigDescription config = c->getEntries().keyAt(ei);
   2897                     if (filterable && !filter->match(config)) {
   2898                         continue;
   2899                     }
   2900                     sp<Entry> e = c->getEntries().valueAt(ei);
   2901                     if (e == NULL) {
   2902                         continue;
   2903                     }
   2904                     e->setNameIndex(keyStrings.add(e->getName(), true));
   2905 
   2906                     // If this entry has no values for other configs,
   2907                     // and is the default config, then it is special.  Otherwise
   2908                     // we want to add it with the config info.
   2909                     ConfigDescription* valueConfig = NULL;
   2910                     if (N != 1 || config == nullConfig) {
   2911                         valueConfig = &config;
   2912                     }
   2913 
   2914                     status_t err = e->prepareFlatten(&valueStrings, this,
   2915                             &configTypeName, &config);
   2916                     if (err != NO_ERROR) {
   2917                         return err;
   2918                     }
   2919                     allEntries.add(e);
   2920                 }
   2921             }
   2922         }
   2923 
   2924         p->setTypeStrings(typeStrings.createStringBlock());
   2925         p->setKeyStrings(keyStrings.createStringBlock());
   2926     }
   2927 
   2928     if (bundle->getOutputAPKFile() != NULL) {
   2929         // Now we want to sort the value strings for better locality.  This will
   2930         // cause the positions of the strings to change, so we need to go back
   2931         // through out resource entries and update them accordingly.  Only need
   2932         // to do this if actually writing the output file.
   2933         valueStrings.sortByConfig();
   2934         for (pi=0; pi<allEntries.size(); pi++) {
   2935             allEntries[pi]->remapStringValue(&valueStrings);
   2936         }
   2937     }
   2938 
   2939     ssize_t strAmt = 0;
   2940 
   2941     // Now build the array of package chunks.
   2942     Vector<sp<AaptFile> > flatPackages;
   2943     for (pi=0; pi<N; pi++) {
   2944         sp<Package> p = mOrderedPackages.itemAt(pi);
   2945         if (p->getTypes().size() == 0) {
   2946             // Empty, skip!
   2947             continue;
   2948         }
   2949 
   2950         const size_t N = p->getTypeStrings().size();
   2951 
   2952         const size_t baseSize = sizeof(ResTable_package);
   2953 
   2954         // Start the package data.
   2955         sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
   2956         ResTable_package* header = (ResTable_package*)data->editData(baseSize);
   2957         if (header == NULL) {
   2958             fprintf(stderr, "ERROR: out of memory creating ResTable_package\n");
   2959             return NO_MEMORY;
   2960         }
   2961         memset(header, 0, sizeof(*header));
   2962         header->header.type = htods(RES_TABLE_PACKAGE_TYPE);
   2963         header->header.headerSize = htods(sizeof(*header));
   2964         header->id = htodl(static_cast<uint32_t>(p->getAssignedId()));
   2965         strcpy16_htod(header->name, p->getName().string());
   2966 
   2967         // Write the string blocks.
   2968         const size_t typeStringsStart = data->getSize();
   2969         sp<AaptFile> strFile = p->getTypeStringsData();
   2970         ssize_t amt = data->writeData(strFile->getData(), strFile->getSize());
   2971         if (kPrintStringMetrics) {
   2972             fprintf(stderr, "**** type strings: %zd\n", SSIZE(amt));
   2973         }
   2974         strAmt += amt;
   2975         if (amt < 0) {
   2976             return amt;
   2977         }
   2978         const size_t keyStringsStart = data->getSize();
   2979         strFile = p->getKeyStringsData();
   2980         amt = data->writeData(strFile->getData(), strFile->getSize());
   2981         if (kPrintStringMetrics) {
   2982             fprintf(stderr, "**** key strings: %zd\n", SSIZE(amt));
   2983         }
   2984         strAmt += amt;
   2985         if (amt < 0) {
   2986             return amt;
   2987         }
   2988 
   2989         if (isBase) {
   2990             status_t err = flattenLibraryTable(data, libraryPackages);
   2991             if (err != NO_ERROR) {
   2992                 fprintf(stderr, "ERROR: failed to write library table\n");
   2993                 return err;
   2994             }
   2995         }
   2996 
   2997         // Build the type chunks inside of this package.
   2998         for (size_t ti=0; ti<N; ti++) {
   2999             // Retrieve them in the same order as the type string block.
   3000             size_t len;
   3001             String16 typeName(p->getTypeStrings().stringAt(ti, &len));
   3002             sp<Type> t = p->getTypes().valueFor(typeName);
   3003             LOG_ALWAYS_FATAL_IF(t == NULL && typeName != String16("<empty>"),
   3004                                 "Type name %s not found",
   3005                                 String8(typeName).string());
   3006             if (t == NULL) {
   3007                 continue;
   3008             }
   3009             const bool filterable = (typeName != mipmap16);
   3010             const bool skipEntireType = (typeName == mipmap16 && !isBase);
   3011 
   3012             const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
   3013 
   3014             // Until a non-NO_ENTRY value has been written for a resource,
   3015             // that resource is invalid; validResources[i] represents
   3016             // the item at t->getOrderedConfigs().itemAt(i).
   3017             Vector<bool> validResources;
   3018             validResources.insertAt(false, 0, N);
   3019 
   3020             // First write the typeSpec chunk, containing information about
   3021             // each resource entry in this type.
   3022             {
   3023                 const size_t typeSpecSize = sizeof(ResTable_typeSpec) + sizeof(uint32_t)*N;
   3024                 const size_t typeSpecStart = data->getSize();
   3025                 ResTable_typeSpec* tsHeader = (ResTable_typeSpec*)
   3026                     (((uint8_t*)data->editData(typeSpecStart+typeSpecSize)) + typeSpecStart);
   3027                 if (tsHeader == NULL) {
   3028                     fprintf(stderr, "ERROR: out of memory creating ResTable_typeSpec\n");
   3029                     return NO_MEMORY;
   3030                 }
   3031                 memset(tsHeader, 0, sizeof(*tsHeader));
   3032                 tsHeader->header.type = htods(RES_TABLE_TYPE_SPEC_TYPE);
   3033                 tsHeader->header.headerSize = htods(sizeof(*tsHeader));
   3034                 tsHeader->header.size = htodl(typeSpecSize);
   3035                 tsHeader->id = ti+1;
   3036                 tsHeader->entryCount = htodl(N);
   3037 
   3038                 uint32_t* typeSpecFlags = (uint32_t*)
   3039                     (((uint8_t*)data->editData())
   3040                         + typeSpecStart + sizeof(ResTable_typeSpec));
   3041                 memset(typeSpecFlags, 0, sizeof(uint32_t)*N);
   3042 
   3043                 for (size_t ei=0; ei<N; ei++) {
   3044                     sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
   3045                     if (cl == NULL) {
   3046                         continue;
   3047                     }
   3048 
   3049                     if (cl->getPublic()) {
   3050                         typeSpecFlags[ei] |= htodl(ResTable_typeSpec::SPEC_PUBLIC);
   3051                     }
   3052 
   3053                     if (skipEntireType) {
   3054                         continue;
   3055                     }
   3056 
   3057                     const size_t CN = cl->getEntries().size();
   3058                     for (size_t ci=0; ci<CN; ci++) {
   3059                         if (filterable && !filter->match(cl->getEntries().keyAt(ci))) {
   3060                             continue;
   3061                         }
   3062                         for (size_t cj=ci+1; cj<CN; cj++) {
   3063                             if (filterable && !filter->match(cl->getEntries().keyAt(cj))) {
   3064                                 continue;
   3065                             }
   3066                             typeSpecFlags[ei] |= htodl(
   3067                                 cl->getEntries().keyAt(ci).diff(cl->getEntries().keyAt(cj)));
   3068                         }
   3069                     }
   3070                 }
   3071             }
   3072 
   3073             if (skipEntireType) {
   3074                 continue;
   3075             }
   3076 
   3077             // We need to write one type chunk for each configuration for
   3078             // which we have entries in this type.
   3079             SortedVector<ConfigDescription> uniqueConfigs;
   3080             if (t != NULL) {
   3081                 uniqueConfigs = t->getUniqueConfigs();
   3082             }
   3083 
   3084             const size_t typeSize = sizeof(ResTable_type) + sizeof(uint32_t)*N;
   3085 
   3086             const size_t NC = uniqueConfigs.size();
   3087             for (size_t ci=0; ci<NC; ci++) {
   3088                 const ConfigDescription& config = uniqueConfigs[ci];
   3089 
   3090                 if (kIsDebug) {
   3091                     printf("Writing config %zu config: imsi:%d/%d lang:%c%c cnt:%c%c "
   3092                         "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
   3093                         "sw%ddp w%ddp h%ddp layout:%d\n",
   3094                         ti + 1,
   3095                         config.mcc, config.mnc,
   3096                         config.language[0] ? config.language[0] : '-',
   3097                         config.language[1] ? config.language[1] : '-',
   3098                         config.country[0] ? config.country[0] : '-',
   3099                         config.country[1] ? config.country[1] : '-',
   3100                         config.orientation,
   3101                         config.uiMode,
   3102                         config.touchscreen,
   3103                         config.density,
   3104                         config.keyboard,
   3105                         config.inputFlags,
   3106                         config.navigation,
   3107                         config.screenWidth,
   3108                         config.screenHeight,
   3109                         config.smallestScreenWidthDp,
   3110                         config.screenWidthDp,
   3111                         config.screenHeightDp,
   3112                         config.screenLayout);
   3113                 }
   3114 
   3115                 if (filterable && !filter->match(config)) {
   3116                     continue;
   3117                 }
   3118 
   3119                 const size_t typeStart = data->getSize();
   3120 
   3121                 ResTable_type* tHeader = (ResTable_type*)
   3122                     (((uint8_t*)data->editData(typeStart+typeSize)) + typeStart);
   3123                 if (tHeader == NULL) {
   3124                     fprintf(stderr, "ERROR: out of memory creating ResTable_type\n");
   3125                     return NO_MEMORY;
   3126                 }
   3127 
   3128                 memset(tHeader, 0, sizeof(*tHeader));
   3129                 tHeader->header.type = htods(RES_TABLE_TYPE_TYPE);
   3130                 tHeader->header.headerSize = htods(sizeof(*tHeader));
   3131                 tHeader->id = ti+1;
   3132                 tHeader->entryCount = htodl(N);
   3133                 tHeader->entriesStart = htodl(typeSize);
   3134                 tHeader->config = config;
   3135                 if (kIsDebug) {
   3136                     printf("Writing type %zu config: imsi:%d/%d lang:%c%c cnt:%c%c "
   3137                         "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
   3138                         "sw%ddp w%ddp h%ddp layout:%d\n",
   3139                         ti + 1,
   3140                         tHeader->config.mcc, tHeader->config.mnc,
   3141                         tHeader->config.language[0] ? tHeader->config.language[0] : '-',
   3142                         tHeader->config.language[1] ? tHeader->config.language[1] : '-',
   3143                         tHeader->config.country[0] ? tHeader->config.country[0] : '-',
   3144                         tHeader->config.country[1] ? tHeader->config.country[1] : '-',
   3145                         tHeader->config.orientation,
   3146                         tHeader->config.uiMode,
   3147                         tHeader->config.touchscreen,
   3148                         tHeader->config.density,
   3149                         tHeader->config.keyboard,
   3150                         tHeader->config.inputFlags,
   3151                         tHeader->config.navigation,
   3152                         tHeader->config.screenWidth,
   3153                         tHeader->config.screenHeight,
   3154                         tHeader->config.smallestScreenWidthDp,
   3155                         tHeader->config.screenWidthDp,
   3156                         tHeader->config.screenHeightDp,
   3157                         tHeader->config.screenLayout);
   3158                 }
   3159                 tHeader->config.swapHtoD();
   3160 
   3161                 // Build the entries inside of this type.
   3162                 for (size_t ei=0; ei<N; ei++) {
   3163                     sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
   3164                     sp<Entry> e = NULL;
   3165                     if (cl != NULL) {
   3166                         e = cl->getEntries().valueFor(config);
   3167                     }
   3168 
   3169                     // Set the offset for this entry in its type.
   3170                     uint32_t* index = (uint32_t*)
   3171                         (((uint8_t*)data->editData())
   3172                             + typeStart + sizeof(ResTable_type));
   3173                     if (e != NULL) {
   3174                         index[ei] = htodl(data->getSize()-typeStart-typeSize);
   3175 
   3176                         // Create the entry.
   3177                         ssize_t amt = e->flatten(bundle, data, cl->getPublic());
   3178                         if (amt < 0) {
   3179                             return amt;
   3180                         }
   3181                         validResources.editItemAt(ei) = true;
   3182                     } else {
   3183                         index[ei] = htodl(ResTable_type::NO_ENTRY);
   3184                     }
   3185                 }
   3186 
   3187                 // Fill in the rest of the type information.
   3188                 tHeader = (ResTable_type*)
   3189                     (((uint8_t*)data->editData()) + typeStart);
   3190                 tHeader->header.size = htodl(data->getSize()-typeStart);
   3191             }
   3192 
   3193             // If we're building splits, then each invocation of the flattening
   3194             // step will have 'missing' entries. Don't warn/error for this case.
   3195             if (bundle->getSplitConfigurations().isEmpty()) {
   3196                 bool missing_entry = false;
   3197                 const char* log_prefix = bundle->getErrorOnMissingConfigEntry() ?
   3198                         "error" : "warning";
   3199                 for (size_t i = 0; i < N; ++i) {
   3200                     if (!validResources[i]) {
   3201                         sp<ConfigList> c = t->getOrderedConfigs().itemAt(i);
   3202                         if (c != NULL) {
   3203                             fprintf(stderr, "%s: no entries written for %s/%s (0x%08zx)\n", log_prefix,
   3204                                     String8(typeName).string(), String8(c->getName()).string(),
   3205                                     Res_MAKEID(p->getAssignedId() - 1, ti, i));
   3206                         }
   3207                         missing_entry = true;
   3208                     }
   3209                 }
   3210                 if (bundle->getErrorOnMissingConfigEntry() && missing_entry) {
   3211                     fprintf(stderr, "Error: Missing entries, quit!\n");
   3212                     return NOT_ENOUGH_DATA;
   3213                 }
   3214             }
   3215         }
   3216 
   3217         // Fill in the rest of the package information.
   3218         header = (ResTable_package*)data->editData();
   3219         header->header.size = htodl(data->getSize());
   3220         header->typeStrings = htodl(typeStringsStart);
   3221         header->lastPublicType = htodl(p->getTypeStrings().size());
   3222         header->keyStrings = htodl(keyStringsStart);
   3223         header->lastPublicKey = htodl(p->getKeyStrings().size());
   3224 
   3225         flatPackages.add(data);
   3226     }
   3227 
   3228     // And now write out the final chunks.
   3229     const size_t dataStart = dest->getSize();
   3230 
   3231     {
   3232         // blah
   3233         ResTable_header header;
   3234         memset(&header, 0, sizeof(header));
   3235         header.header.type = htods(RES_TABLE_TYPE);
   3236         header.header.headerSize = htods(sizeof(header));
   3237         header.packageCount = htodl(flatPackages.size());
   3238         status_t err = dest->writeData(&header, sizeof(header));
   3239         if (err != NO_ERROR) {
   3240             fprintf(stderr, "ERROR: out of memory creating ResTable_header\n");
   3241             return err;
   3242         }
   3243     }
   3244 
   3245     ssize_t strStart = dest->getSize();
   3246     status_t err = valueStrings.writeStringBlock(dest);
   3247     if (err != NO_ERROR) {
   3248         return err;
   3249     }
   3250 
   3251     ssize_t amt = (dest->getSize()-strStart);
   3252     strAmt += amt;
   3253     if (kPrintStringMetrics) {
   3254         fprintf(stderr, "**** value strings: %zd\n", SSIZE(amt));
   3255         fprintf(stderr, "**** total strings: %zd\n", SSIZE(strAmt));
   3256     }
   3257 
   3258     for (pi=0; pi<flatPackages.size(); pi++) {
   3259         err = dest->writeData(flatPackages[pi]->getData(),
   3260                               flatPackages[pi]->getSize());
   3261         if (err != NO_ERROR) {
   3262             fprintf(stderr, "ERROR: out of memory creating package chunk for ResTable_header\n");
   3263             return err;
   3264         }
   3265     }
   3266 
   3267     ResTable_header* header = (ResTable_header*)
   3268         (((uint8_t*)dest->getData()) + dataStart);
   3269     header->header.size = htodl(dest->getSize() - dataStart);
   3270 
   3271     if (kPrintStringMetrics) {
   3272         fprintf(stderr, "**** total resource table size: %zu / %zu%% strings\n",
   3273                 dest->getSize(), (size_t)(strAmt*100)/dest->getSize());
   3274     }
   3275 
   3276     return NO_ERROR;
   3277 }
   3278 
   3279 status_t ResourceTable::flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs) {
   3280     // Write out the library table if necessary
   3281     if (libs.size() > 0) {
   3282         if (kIsDebug) {
   3283             fprintf(stderr, "Writing library reference table\n");
   3284         }
   3285 
   3286         const size_t libStart = dest->getSize();
   3287         const size_t count = libs.size();
   3288         ResTable_lib_header* libHeader = (ResTable_lib_header*) dest->editDataInRange(
   3289                 libStart, sizeof(ResTable_lib_header));
   3290 
   3291         memset(libHeader, 0, sizeof(*libHeader));
   3292         libHeader->header.type = htods(RES_TABLE_LIBRARY_TYPE);
   3293         libHeader->header.headerSize = htods(sizeof(*libHeader));
   3294         libHeader->header.size = htodl(sizeof(*libHeader) + (sizeof(ResTable_lib_entry) * count));
   3295         libHeader->count = htodl(count);
   3296 
   3297         // Write the library entries
   3298         for (size_t i = 0; i < count; i++) {
   3299             const size_t entryStart = dest->getSize();
   3300             sp<Package> libPackage = libs[i];
   3301             if (kIsDebug) {
   3302                 fprintf(stderr, "  Entry %s -> 0x%02x\n",
   3303                         String8(libPackage->getName()).string(),
   3304                         (uint8_t)libPackage->getAssignedId());
   3305             }
   3306 
   3307             ResTable_lib_entry* entry = (ResTable_lib_entry*) dest->editDataInRange(
   3308                     entryStart, sizeof(ResTable_lib_entry));
   3309             memset(entry, 0, sizeof(*entry));
   3310             entry->packageId = htodl(libPackage->getAssignedId());
   3311             strcpy16_htod(entry->packageName, libPackage->getName().string());
   3312         }
   3313     }
   3314     return NO_ERROR;
   3315 }
   3316 
   3317 void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp)
   3318 {
   3319     fprintf(fp,
   3320     "<!-- This file contains <public> resource definitions for all\n"
   3321     "     resources that were generated from the source data. -->\n"
   3322     "\n"
   3323     "<resources>\n");
   3324 
   3325     writePublicDefinitions(package, fp, true);
   3326     writePublicDefinitions(package, fp, false);
   3327 
   3328     fprintf(fp,
   3329     "\n"
   3330     "</resources>\n");
   3331 }
   3332 
   3333 void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp, bool pub)
   3334 {
   3335     bool didHeader = false;
   3336 
   3337     sp<Package> pkg = mPackages.valueFor(package);
   3338     if (pkg != NULL) {
   3339         const size_t NT = pkg->getOrderedTypes().size();
   3340         for (size_t i=0; i<NT; i++) {
   3341             sp<Type> t = pkg->getOrderedTypes().itemAt(i);
   3342             if (t == NULL) {
   3343                 continue;
   3344             }
   3345 
   3346             bool didType = false;
   3347 
   3348             const size_t NC = t->getOrderedConfigs().size();
   3349             for (size_t j=0; j<NC; j++) {
   3350                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(j);
   3351                 if (c == NULL) {
   3352                     continue;
   3353                 }
   3354 
   3355                 if (c->getPublic() != pub) {
   3356                     continue;
   3357                 }
   3358 
   3359                 if (!didType) {
   3360                     fprintf(fp, "\n");
   3361                     didType = true;
   3362                 }
   3363                 if (!didHeader) {
   3364                     if (pub) {
   3365                         fprintf(fp,"  <!-- PUBLIC SECTION.  These resources have been declared public.\n");
   3366                         fprintf(fp,"       Changes to these definitions will break binary compatibility. -->\n\n");
   3367                     } else {
   3368                         fprintf(fp,"  <!-- PRIVATE SECTION.  These resources have not been declared public.\n");
   3369                         fprintf(fp,"       You can make them public my moving these lines into a file in res/values. -->\n\n");
   3370                     }
   3371                     didHeader = true;
   3372                 }
   3373                 if (!pub) {
   3374                     const size_t NE = c->getEntries().size();
   3375                     for (size_t k=0; k<NE; k++) {
   3376                         const SourcePos& pos = c->getEntries().valueAt(k)->getPos();
   3377                         if (pos.file != "") {
   3378                             fprintf(fp,"  <!-- Declared at %s:%d -->\n",
   3379                                     pos.file.string(), pos.line);
   3380                         }
   3381                     }
   3382                 }
   3383                 fprintf(fp, "  <public type=\"%s\" name=\"%s\" id=\"0x%08x\" />\n",
   3384                         String8(t->getName()).string(),
   3385                         String8(c->getName()).string(),
   3386                         getResId(pkg, t, c->getEntryIndex()));
   3387             }
   3388         }
   3389     }
   3390 }
   3391 
   3392 ResourceTable::Item::Item(const SourcePos& _sourcePos,
   3393                           bool _isId,
   3394                           const String16& _value,
   3395                           const Vector<StringPool::entry_style_span>* _style,
   3396                           int32_t _format)
   3397     : sourcePos(_sourcePos)
   3398     , isId(_isId)
   3399     , value(_value)
   3400     , format(_format)
   3401     , bagKeyId(0)
   3402     , evaluating(false)
   3403 {
   3404     if (_style) {
   3405         style = *_style;
   3406     }
   3407 }
   3408 
   3409 ResourceTable::Entry::Entry(const Entry& entry)
   3410     : RefBase()
   3411     , mName(entry.mName)
   3412     , mParent(entry.mParent)
   3413     , mType(entry.mType)
   3414     , mItem(entry.mItem)
   3415     , mItemFormat(entry.mItemFormat)
   3416     , mBag(entry.mBag)
   3417     , mNameIndex(entry.mNameIndex)
   3418     , mParentId(entry.mParentId)
   3419     , mPos(entry.mPos) {}
   3420 
   3421 ResourceTable::Entry& ResourceTable::Entry::operator=(const Entry& entry) {
   3422     mName = entry.mName;
   3423     mParent = entry.mParent;
   3424     mType = entry.mType;
   3425     mItem = entry.mItem;
   3426     mItemFormat = entry.mItemFormat;
   3427     mBag = entry.mBag;
   3428     mNameIndex = entry.mNameIndex;
   3429     mParentId = entry.mParentId;
   3430     mPos = entry.mPos;
   3431     return *this;
   3432 }
   3433 
   3434 status_t ResourceTable::Entry::makeItABag(const SourcePos& sourcePos)
   3435 {
   3436     if (mType == TYPE_BAG) {
   3437         return NO_ERROR;
   3438     }
   3439     if (mType == TYPE_UNKNOWN) {
   3440         mType = TYPE_BAG;
   3441         return NO_ERROR;
   3442     }
   3443     sourcePos.error("Resource entry %s is already defined as a single item.\n"
   3444                     "%s:%d: Originally defined here.\n",
   3445                     String8(mName).string(),
   3446                     mItem.sourcePos.file.string(), mItem.sourcePos.line);
   3447     return UNKNOWN_ERROR;
   3448 }
   3449 
   3450 status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos,
   3451                                        const String16& value,
   3452                                        const Vector<StringPool::entry_style_span>* style,
   3453                                        int32_t format,
   3454                                        const bool overwrite)
   3455 {
   3456     Item item(sourcePos, false, value, style);
   3457 
   3458     if (mType == TYPE_BAG) {
   3459         if (mBag.size() == 0) {
   3460             sourcePos.error("Resource entry %s is already defined as a bag.",
   3461                     String8(mName).string());
   3462         } else {
   3463             const Item& item(mBag.valueAt(0));
   3464             sourcePos.error("Resource entry %s is already defined as a bag.\n"
   3465                             "%s:%d: Originally defined here.\n",
   3466                             String8(mName).string(),
   3467                             item.sourcePos.file.string(), item.sourcePos.line);
   3468         }
   3469         return UNKNOWN_ERROR;
   3470     }
   3471     if ( (mType != TYPE_UNKNOWN) && (overwrite == false) ) {
   3472         sourcePos.error("Resource entry %s is already defined.\n"
   3473                         "%s:%d: Originally defined here.\n",
   3474                         String8(mName).string(),
   3475                         mItem.sourcePos.file.string(), mItem.sourcePos.line);
   3476         return UNKNOWN_ERROR;
   3477     }
   3478 
   3479     mType = TYPE_ITEM;
   3480     mItem = item;
   3481     mItemFormat = format;
   3482     return NO_ERROR;
   3483 }
   3484 
   3485 status_t ResourceTable::Entry::addToBag(const SourcePos& sourcePos,
   3486                                         const String16& key, const String16& value,
   3487                                         const Vector<StringPool::entry_style_span>* style,
   3488                                         bool replace, bool isId, int32_t format)
   3489 {
   3490     status_t err = makeItABag(sourcePos);
   3491     if (err != NO_ERROR) {
   3492         return err;
   3493     }
   3494 
   3495     Item item(sourcePos, isId, value, style, format);
   3496 
   3497     // XXX NOTE: there is an error if you try to have a bag with two keys,
   3498     // one an attr and one an id, with the same name.  Not something we
   3499     // currently ever have to worry about.
   3500     ssize_t origKey = mBag.indexOfKey(key);
   3501     if (origKey >= 0) {
   3502         if (!replace) {
   3503             const Item& item(mBag.valueAt(origKey));
   3504             sourcePos.error("Resource entry %s already has bag item %s.\n"
   3505                     "%s:%d: Originally defined here.\n",
   3506                     String8(mName).string(), String8(key).string(),
   3507                     item.sourcePos.file.string(), item.sourcePos.line);
   3508             return UNKNOWN_ERROR;
   3509         }
   3510         //printf("Replacing %s with %s\n",
   3511         //       String8(mBag.valueFor(key).value).string(), String8(value).string());
   3512         mBag.replaceValueFor(key, item);
   3513     }
   3514 
   3515     mBag.add(key, item);
   3516     return NO_ERROR;
   3517 }
   3518 
   3519 status_t ResourceTable::Entry::removeFromBag(const String16& key) {
   3520     if (mType != Entry::TYPE_BAG) {
   3521         return NO_ERROR;
   3522     }
   3523 
   3524     if (mBag.removeItem(key) >= 0) {
   3525         return NO_ERROR;
   3526     }
   3527     return UNKNOWN_ERROR;
   3528 }
   3529 
   3530 status_t ResourceTable::Entry::emptyBag(const SourcePos& sourcePos)
   3531 {
   3532     status_t err = makeItABag(sourcePos);
   3533     if (err != NO_ERROR) {
   3534         return err;
   3535     }
   3536 
   3537     mBag.clear();
   3538     return NO_ERROR;
   3539 }
   3540 
   3541 status_t ResourceTable::Entry::generateAttributes(ResourceTable* table,
   3542                                                   const String16& package)
   3543 {
   3544     const String16 attr16("attr");
   3545     const String16 id16("id");
   3546     const size_t N = mBag.size();
   3547     for (size_t i=0; i<N; i++) {
   3548         const String16& key = mBag.keyAt(i);
   3549         const Item& it = mBag.valueAt(i);
   3550         if (it.isId) {
   3551             if (!table->hasBagOrEntry(key, &id16, &package)) {
   3552                 String16 value("false");
   3553                 if (kIsDebug) {
   3554                     fprintf(stderr, "Generating %s:id/%s\n",
   3555                             String8(package).string(),
   3556                             String8(key).string());
   3557                 }
   3558                 status_t err = table->addEntry(SourcePos(String8("<generated>"), 0), package,
   3559                                                id16, key, value);
   3560                 if (err != NO_ERROR) {
   3561                     return err;
   3562                 }
   3563             }
   3564         } else if (!table->hasBagOrEntry(key, &attr16, &package)) {
   3565 
   3566 #if 1
   3567 //             fprintf(stderr, "ERROR: Bag attribute '%s' has not been defined.\n",
   3568 //                     String8(key).string());
   3569 //             const Item& item(mBag.valueAt(i));
   3570 //             fprintf(stderr, "Referenced from file %s line %d\n",
   3571 //                     item.sourcePos.file.string(), item.sourcePos.line);
   3572 //             return UNKNOWN_ERROR;
   3573 #else
   3574             char numberStr[16];
   3575             sprintf(numberStr, "%d", ResTable_map::TYPE_ANY);
   3576             status_t err = table->addBag(SourcePos("<generated>", 0), package,
   3577                                          attr16, key, String16(""),
   3578                                          String16("^type"),
   3579                                          String16(numberStr), NULL, NULL);
   3580             if (err != NO_ERROR) {
   3581                 return err;
   3582             }
   3583 #endif
   3584         }
   3585     }
   3586     return NO_ERROR;
   3587 }
   3588 
   3589 status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table,
   3590                                                  const String16& /* package */)
   3591 {
   3592     bool hasErrors = false;
   3593 
   3594     if (mType == TYPE_BAG) {
   3595         const char* errorMsg;
   3596         const String16 style16("style");
   3597         const String16 attr16("attr");
   3598         const String16 id16("id");
   3599         mParentId = 0;
   3600         if (mParent.size() > 0) {
   3601             mParentId = table->getResId(mParent, &style16, NULL, &errorMsg);
   3602             if (mParentId == 0) {
   3603                 mPos.error("Error retrieving parent for item: %s '%s'.\n",
   3604                         errorMsg, String8(mParent).string());
   3605                 hasErrors = true;
   3606             }
   3607         }
   3608         const size_t N = mBag.size();
   3609         for (size_t i=0; i<N; i++) {
   3610             const String16& key = mBag.keyAt(i);
   3611             Item& it = mBag.editValueAt(i);
   3612             it.bagKeyId = table->getResId(key,
   3613                     it.isId ? &id16 : &attr16, NULL, &errorMsg);
   3614             //printf("Bag key of %s: #%08x\n", String8(key).string(), it.bagKeyId);
   3615             if (it.bagKeyId == 0) {
   3616                 it.sourcePos.error("Error: %s: %s '%s'.\n", errorMsg,
   3617                         String8(it.isId ? id16 : attr16).string(),
   3618                         String8(key).string());
   3619                 hasErrors = true;
   3620             }
   3621         }
   3622     }
   3623     return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR;
   3624 }
   3625 
   3626 status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table,
   3627         const String8* configTypeName, const ConfigDescription* config)
   3628 {
   3629     if (mType == TYPE_ITEM) {
   3630         Item& it = mItem;
   3631         AccessorCookie ac(it.sourcePos, String8(mName), String8(it.value));
   3632         if (!table->stringToValue(&it.parsedValue, strings,
   3633                                   it.value, false, true, 0,
   3634                                   &it.style, NULL, &ac, mItemFormat,
   3635                                   configTypeName, config)) {
   3636             return UNKNOWN_ERROR;
   3637         }
   3638     } else if (mType == TYPE_BAG) {
   3639         const size_t N = mBag.size();
   3640         for (size_t i=0; i<N; i++) {
   3641             const String16& key = mBag.keyAt(i);
   3642             Item& it = mBag.editValueAt(i);
   3643             AccessorCookie ac(it.sourcePos, String8(key), String8(it.value));
   3644             if (!table->stringToValue(&it.parsedValue, strings,
   3645                                       it.value, false, true, it.bagKeyId,
   3646                                       &it.style, NULL, &ac, it.format,
   3647                                       configTypeName, config)) {
   3648                 return UNKNOWN_ERROR;
   3649             }
   3650         }
   3651     } else {
   3652         mPos.error("Error: entry %s is not a single item or a bag.\n",
   3653                    String8(mName).string());
   3654         return UNKNOWN_ERROR;
   3655     }
   3656     return NO_ERROR;
   3657 }
   3658 
   3659 status_t ResourceTable::Entry::remapStringValue(StringPool* strings)
   3660 {
   3661     if (mType == TYPE_ITEM) {
   3662         Item& it = mItem;
   3663         if (it.parsedValue.dataType == Res_value::TYPE_STRING) {
   3664             it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data);
   3665         }
   3666     } else if (mType == TYPE_BAG) {
   3667         const size_t N = mBag.size();
   3668         for (size_t i=0; i<N; i++) {
   3669             Item& it = mBag.editValueAt(i);
   3670             if (it.parsedValue.dataType == Res_value::TYPE_STRING) {
   3671                 it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data);
   3672             }
   3673         }
   3674     } else {
   3675         mPos.error("Error: entry %s is not a single item or a bag.\n",
   3676                    String8(mName).string());
   3677         return UNKNOWN_ERROR;
   3678     }
   3679     return NO_ERROR;
   3680 }
   3681 
   3682 ssize_t ResourceTable::Entry::flatten(Bundle* /* bundle */, const sp<AaptFile>& data, bool isPublic)
   3683 {
   3684     size_t amt = 0;
   3685     ResTable_entry header;
   3686     memset(&header, 0, sizeof(header));
   3687     header.size = htods(sizeof(header));
   3688     const type ty = mType;
   3689     if (ty == TYPE_BAG) {
   3690         header.flags |= htods(header.FLAG_COMPLEX);
   3691     }
   3692     if (isPublic) {
   3693         header.flags |= htods(header.FLAG_PUBLIC);
   3694     }
   3695     header.key.index = htodl(mNameIndex);
   3696     if (ty != TYPE_BAG) {
   3697         status_t err = data->writeData(&header, sizeof(header));
   3698         if (err != NO_ERROR) {
   3699             fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
   3700             return err;
   3701         }
   3702 
   3703         const Item& it = mItem;
   3704         Res_value par;
   3705         memset(&par, 0, sizeof(par));
   3706         par.size = htods(it.parsedValue.size);
   3707         par.dataType = it.parsedValue.dataType;
   3708         par.res0 = it.parsedValue.res0;
   3709         par.data = htodl(it.parsedValue.data);
   3710         #if 0
   3711         printf("Writing item (%s): type=%d, data=0x%x, res0=0x%x\n",
   3712                String8(mName).string(), it.parsedValue.dataType,
   3713                it.parsedValue.data, par.res0);
   3714         #endif
   3715         err = data->writeData(&par, it.parsedValue.size);
   3716         if (err != NO_ERROR) {
   3717             fprintf(stderr, "ERROR: out of memory creating Res_value\n");
   3718             return err;
   3719         }
   3720         amt += it.parsedValue.size;
   3721     } else {
   3722         size_t N = mBag.size();
   3723         size_t i;
   3724         // Create correct ordering of items.
   3725         KeyedVector<uint32_t, const Item*> items;
   3726         for (i=0; i<N; i++) {
   3727             const Item& it = mBag.valueAt(i);
   3728             items.add(it.bagKeyId, &it);
   3729         }
   3730         N = items.size();
   3731 
   3732         ResTable_map_entry mapHeader;
   3733         memcpy(&mapHeader, &header, sizeof(header));
   3734         mapHeader.size = htods(sizeof(mapHeader));
   3735         mapHeader.parent.ident = htodl(mParentId);
   3736         mapHeader.count = htodl(N);
   3737         status_t err = data->writeData(&mapHeader, sizeof(mapHeader));
   3738         if (err != NO_ERROR) {
   3739             fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
   3740             return err;
   3741         }
   3742 
   3743         for (i=0; i<N; i++) {
   3744             const Item& it = *items.valueAt(i);
   3745             ResTable_map map;
   3746             map.name.ident = htodl(it.bagKeyId);
   3747             map.value.size = htods(it.parsedValue.size);
   3748             map.value.dataType = it.parsedValue.dataType;
   3749             map.value.res0 = it.parsedValue.res0;
   3750             map.value.data = htodl(it.parsedValue.data);
   3751             err = data->writeData(&map, sizeof(map));
   3752             if (err != NO_ERROR) {
   3753                 fprintf(stderr, "ERROR: out of memory creating Res_value\n");
   3754                 return err;
   3755             }
   3756             amt += sizeof(map);
   3757         }
   3758     }
   3759     return amt;
   3760 }
   3761 
   3762 void ResourceTable::ConfigList::appendComment(const String16& comment,
   3763                                               bool onlyIfEmpty)
   3764 {
   3765     if (comment.size() <= 0) {
   3766         return;
   3767     }
   3768     if (onlyIfEmpty && mComment.size() > 0) {
   3769         return;
   3770     }
   3771     if (mComment.size() > 0) {
   3772         mComment.append(String16("\n"));
   3773     }
   3774     mComment.append(comment);
   3775 }
   3776 
   3777 void ResourceTable::ConfigList::appendTypeComment(const String16& comment)
   3778 {
   3779     if (comment.size() <= 0) {
   3780         return;
   3781     }
   3782     if (mTypeComment.size() > 0) {
   3783         mTypeComment.append(String16("\n"));
   3784     }
   3785     mTypeComment.append(comment);
   3786 }
   3787 
   3788 status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos,
   3789                                         const String16& name,
   3790                                         const uint32_t ident)
   3791 {
   3792     #if 0
   3793     int32_t entryIdx = Res_GETENTRY(ident);
   3794     if (entryIdx < 0) {
   3795         sourcePos.error("Public resource %s/%s has an invalid 0 identifier (0x%08x).\n",
   3796                 String8(mName).string(), String8(name).string(), ident);
   3797         return UNKNOWN_ERROR;
   3798     }
   3799     #endif
   3800 
   3801     int32_t typeIdx = Res_GETTYPE(ident);
   3802     if (typeIdx >= 0) {
   3803         typeIdx++;
   3804         if (mPublicIndex > 0 && mPublicIndex != typeIdx) {
   3805             sourcePos.error("Public resource %s/%s has conflicting type codes for its"
   3806                     " public identifiers (0x%x vs 0x%x).\n",
   3807                     String8(mName).string(), String8(name).string(),
   3808                     mPublicIndex, typeIdx);
   3809             return UNKNOWN_ERROR;
   3810         }
   3811         mPublicIndex = typeIdx;
   3812     }
   3813 
   3814     if (mFirstPublicSourcePos == NULL) {
   3815         mFirstPublicSourcePos = new SourcePos(sourcePos);
   3816     }
   3817 
   3818     if (mPublic.indexOfKey(name) < 0) {
   3819         mPublic.add(name, Public(sourcePos, String16(), ident));
   3820     } else {
   3821         Public& p = mPublic.editValueFor(name);
   3822         if (p.ident != ident) {
   3823             sourcePos.error("Public resource %s/%s has conflicting public identifiers"
   3824                     " (0x%08x vs 0x%08x).\n"
   3825                     "%s:%d: Originally defined here.\n",
   3826                     String8(mName).string(), String8(name).string(), p.ident, ident,
   3827                     p.sourcePos.file.string(), p.sourcePos.line);
   3828             return UNKNOWN_ERROR;
   3829         }
   3830     }
   3831 
   3832     return NO_ERROR;
   3833 }
   3834 
   3835 void ResourceTable::Type::canAddEntry(const String16& name)
   3836 {
   3837     mCanAddEntries.add(name);
   3838 }
   3839 
   3840 sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
   3841                                                        const SourcePos& sourcePos,
   3842                                                        const ResTable_config* config,
   3843                                                        bool doSetIndex,
   3844                                                        bool overlay,
   3845                                                        bool autoAddOverlay)
   3846 {
   3847     int pos = -1;
   3848     sp<ConfigList> c = mConfigs.valueFor(entry);
   3849     if (c == NULL) {
   3850         if (overlay && !autoAddOverlay && mCanAddEntries.indexOf(entry) < 0) {
   3851             sourcePos.error("Resource at %s appears in overlay but not"
   3852                             " in the base package; use <add-resource> to add.\n",
   3853                             String8(entry).string());
   3854             return NULL;
   3855         }
   3856         c = new ConfigList(entry, sourcePos);
   3857         mConfigs.add(entry, c);
   3858         pos = (int)mOrderedConfigs.size();
   3859         mOrderedConfigs.add(c);
   3860         if (doSetIndex) {
   3861             c->setEntryIndex(pos);
   3862         }
   3863     }
   3864 
   3865     ConfigDescription cdesc;
   3866     if (config) cdesc = *config;
   3867 
   3868     sp<Entry> e = c->getEntries().valueFor(cdesc);
   3869     if (e == NULL) {
   3870         if (kIsDebug) {
   3871             if (config != NULL) {
   3872                 printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
   3873                     "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
   3874                     "sw%ddp w%ddp h%ddp layout:%d\n",
   3875                       sourcePos.file.string(), sourcePos.line,
   3876                       config->mcc, config->mnc,
   3877                       config->language[0] ? config->language[0] : '-',
   3878                       config->language[1] ? config->language[1] : '-',
   3879                       config->country[0] ? config->country[0] : '-',
   3880                       config->country[1] ? config->country[1] : '-',
   3881                       config->orientation,
   3882                       config->touchscreen,
   3883                       config->density,
   3884                       config->keyboard,
   3885                       config->inputFlags,
   3886                       config->navigation,
   3887                       config->screenWidth,
   3888                       config->screenHeight,
   3889                       config->smallestScreenWidthDp,
   3890                       config->screenWidthDp,
   3891                       config->screenHeightDp,
   3892                       config->screenLayout);
   3893             } else {
   3894                 printf("New entry at %s:%d: NULL config\n",
   3895                         sourcePos.file.string(), sourcePos.line);
   3896             }
   3897         }
   3898         e = new Entry(entry, sourcePos);
   3899         c->addEntry(cdesc, e);
   3900         /*
   3901         if (doSetIndex) {
   3902             if (pos < 0) {
   3903                 for (pos=0; pos<(int)mOrderedConfigs.size(); pos++) {
   3904                     if (mOrderedConfigs[pos] == c) {
   3905                         break;
   3906                     }
   3907                 }
   3908                 if (pos >= (int)mOrderedConfigs.size()) {
   3909                     sourcePos.error("Internal error: config not found in mOrderedConfigs when adding entry");
   3910                     return NULL;
   3911                 }
   3912             }
   3913             e->setEntryIndex(pos);
   3914         }
   3915         */
   3916     }
   3917 
   3918     return e;
   3919 }
   3920 
   3921 sp<ResourceTable::ConfigList> ResourceTable::Type::removeEntry(const String16& entry) {
   3922     ssize_t idx = mConfigs.indexOfKey(entry);
   3923     if (idx < 0) {
   3924         return NULL;
   3925     }
   3926 
   3927     sp<ConfigList> removed = mConfigs.valueAt(idx);
   3928     mConfigs.removeItemsAt(idx);
   3929 
   3930     Vector<sp<ConfigList> >::iterator iter = std::find(
   3931             mOrderedConfigs.begin(), mOrderedConfigs.end(), removed);
   3932     if (iter != mOrderedConfigs.end()) {
   3933         mOrderedConfigs.erase(iter);
   3934     }
   3935 
   3936     mPublic.removeItem(entry);
   3937     return removed;
   3938 }
   3939 
   3940 SortedVector<ConfigDescription> ResourceTable::Type::getUniqueConfigs() const {
   3941     SortedVector<ConfigDescription> unique;
   3942     const size_t entryCount = mOrderedConfigs.size();
   3943     for (size_t i = 0; i < entryCount; i++) {
   3944         if (mOrderedConfigs[i] == NULL) {
   3945             continue;
   3946         }
   3947         const DefaultKeyedVector<ConfigDescription, sp<Entry> >& configs =
   3948                 mOrderedConfigs[i]->getEntries();
   3949         const size_t configCount = configs.size();
   3950         for (size_t j = 0; j < configCount; j++) {
   3951             unique.add(configs.keyAt(j));
   3952         }
   3953     }
   3954     return unique;
   3955 }
   3956 
   3957 status_t ResourceTable::Type::applyPublicEntryOrder()
   3958 {
   3959     size_t N = mOrderedConfigs.size();
   3960     Vector<sp<ConfigList> > origOrder(mOrderedConfigs);
   3961     bool hasError = false;
   3962 
   3963     size_t i;
   3964     for (i=0; i<N; i++) {
   3965         mOrderedConfigs.replaceAt(NULL, i);
   3966     }
   3967 
   3968     const size_t NP = mPublic.size();
   3969     //printf("Ordering %d configs from %d public defs\n", N, NP);
   3970     size_t j;
   3971     for (j=0; j<NP; j++) {
   3972         const String16& name = mPublic.keyAt(j);
   3973         const Public& p = mPublic.valueAt(j);
   3974         int32_t idx = Res_GETENTRY(p.ident);
   3975         //printf("Looking for entry \"%s\"/\"%s\" (0x%08x) in %d...\n",
   3976         //       String8(mName).string(), String8(name).string(), p.ident, N);
   3977         bool found = false;
   3978         for (i=0; i<N; i++) {
   3979             sp<ConfigList> e = origOrder.itemAt(i);
   3980             //printf("#%d: \"%s\"\n", i, String8(e->getName()).string());
   3981             if (e->getName() == name) {
   3982                 if (idx >= (int32_t)mOrderedConfigs.size()) {
   3983                     mOrderedConfigs.resize(idx + 1);
   3984                 }
   3985 
   3986                 if (mOrderedConfigs.itemAt(idx) == NULL) {
   3987                     e->setPublic(true);
   3988                     e->setPublicSourcePos(p.sourcePos);
   3989                     mOrderedConfigs.replaceAt(e, idx);
   3990                     origOrder.removeAt(i);
   3991                     N--;
   3992                     found = true;
   3993                     break;
   3994                 } else {
   3995                     sp<ConfigList> oe = mOrderedConfigs.itemAt(idx);
   3996 
   3997                     p.sourcePos.error("Multiple entry names declared for public entry"
   3998                             " identifier 0x%x in type %s (%s vs %s).\n"
   3999                             "%s:%d: Originally defined here.",
   4000                             idx+1, String8(mName).string(),
   4001                             String8(oe->getName()).string(),
   4002                             String8(name).string(),
   4003                             oe->getPublicSourcePos().file.string(),
   4004                             oe->getPublicSourcePos().line);
   4005                     hasError = true;
   4006                 }
   4007             }
   4008         }
   4009 
   4010         if (!found) {
   4011             p.sourcePos.error("Public symbol %s/%s declared here is not defined.",
   4012                     String8(mName).string(), String8(name).string());
   4013             hasError = true;
   4014         }
   4015     }
   4016 
   4017     //printf("Copying back in %d non-public configs, have %d\n", N, origOrder.size());
   4018 
   4019     if (N != origOrder.size()) {
   4020         printf("Internal error: remaining private symbol count mismatch\n");
   4021         N = origOrder.size();
   4022     }
   4023 
   4024     j = 0;
   4025     for (i=0; i<N; i++) {
   4026         sp<ConfigList> e = origOrder.itemAt(i);
   4027         // There will always be enough room for the remaining entries.
   4028         while (mOrderedConfigs.itemAt(j) != NULL) {
   4029             j++;
   4030         }
   4031         mOrderedConfigs.replaceAt(e, j);
   4032         j++;
   4033     }
   4034 
   4035     return hasError ? STATUST(UNKNOWN_ERROR) : NO_ERROR;
   4036 }
   4037 
   4038 ResourceTable::Package::Package(const String16& name, size_t packageId)
   4039     : mName(name), mPackageId(packageId),
   4040       mTypeStringsMapping(0xffffffff),
   4041       mKeyStringsMapping(0xffffffff)
   4042 {
   4043 }
   4044 
   4045 sp<ResourceTable::Type> ResourceTable::Package::getType(const String16& type,
   4046                                                         const SourcePos& sourcePos,
   4047                                                         bool doSetIndex)
   4048 {
   4049     sp<Type> t = mTypes.valueFor(type);
   4050     if (t == NULL) {
   4051         t = new Type(type, sourcePos);
   4052         mTypes.add(type, t);
   4053         mOrderedTypes.add(t);
   4054         if (doSetIndex) {
   4055             // For some reason the type's index is set to one plus the index
   4056             // in the mOrderedTypes list, rather than just the index.
   4057             t->setIndex(mOrderedTypes.size());
   4058         }
   4059     }
   4060     return t;
   4061 }
   4062 
   4063 status_t ResourceTable::Package::setTypeStrings(const sp<AaptFile>& data)
   4064 {
   4065     status_t err = setStrings(data, &mTypeStrings, &mTypeStringsMapping);
   4066     if (err != NO_ERROR) {
   4067         fprintf(stderr, "ERROR: Type string data is corrupt!\n");
   4068         return err;
   4069     }
   4070 
   4071     // Retain a reference to the new data after we've successfully replaced
   4072     // all uses of the old reference (in setStrings() ).
   4073     mTypeStringsData = data;
   4074     return NO_ERROR;
   4075 }
   4076 
   4077 status_t ResourceTable::Package::setKeyStrings(const sp<AaptFile>& data)
   4078 {
   4079     status_t err = setStrings(data, &mKeyStrings, &mKeyStringsMapping);
   4080     if (err != NO_ERROR) {
   4081         fprintf(stderr, "ERROR: Key string data is corrupt!\n");
   4082         return err;
   4083     }
   4084 
   4085     // Retain a reference to the new data after we've successfully replaced
   4086     // all uses of the old reference (in setStrings() ).
   4087     mKeyStringsData = data;
   4088     return NO_ERROR;
   4089 }
   4090 
   4091 status_t ResourceTable::Package::setStrings(const sp<AaptFile>& data,
   4092                                             ResStringPool* strings,
   4093                                             DefaultKeyedVector<String16, uint32_t>* mappings)
   4094 {
   4095     if (data->getData() == NULL) {
   4096         return UNKNOWN_ERROR;
   4097     }
   4098 
   4099     status_t err = strings->setTo(data->getData(), data->getSize());
   4100     if (err == NO_ERROR) {
   4101         const size_t N = strings->size();
   4102         for (size_t i=0; i<N; i++) {
   4103             size_t len;
   4104             mappings->add(String16(strings->stringAt(i, &len)), i);
   4105         }
   4106     }
   4107     return err;
   4108 }
   4109 
   4110 status_t ResourceTable::Package::applyPublicTypeOrder()
   4111 {
   4112     size_t N = mOrderedTypes.size();
   4113     Vector<sp<Type> > origOrder(mOrderedTypes);
   4114 
   4115     size_t i;
   4116     for (i=0; i<N; i++) {
   4117         mOrderedTypes.replaceAt(NULL, i);
   4118     }
   4119 
   4120     for (i=0; i<N; i++) {
   4121         sp<Type> t = origOrder.itemAt(i);
   4122         int32_t idx = t->getPublicIndex();
   4123         if (idx > 0) {
   4124             idx--;
   4125             while (idx >= (int32_t)mOrderedTypes.size()) {
   4126                 mOrderedTypes.add();
   4127             }
   4128             if (mOrderedTypes.itemAt(idx) != NULL) {
   4129                 sp<Type> ot = mOrderedTypes.itemAt(idx);
   4130                 t->getFirstPublicSourcePos().error("Multiple type names declared for public type"
   4131                         " identifier 0x%x (%s vs %s).\n"
   4132                         "%s:%d: Originally defined here.",
   4133                         idx, String8(ot->getName()).string(),
   4134                         String8(t->getName()).string(),
   4135                         ot->getFirstPublicSourcePos().file.string(),
   4136                         ot->getFirstPublicSourcePos().line);
   4137                 return UNKNOWN_ERROR;
   4138             }
   4139             mOrderedTypes.replaceAt(t, idx);
   4140             origOrder.removeAt(i);
   4141             i--;
   4142             N--;
   4143         }
   4144     }
   4145 
   4146     size_t j=0;
   4147     for (i=0; i<N; i++) {
   4148         sp<Type> t = origOrder.itemAt(i);
   4149         // There will always be enough room for the remaining types.
   4150         while (mOrderedTypes.itemAt(j) != NULL) {
   4151             j++;
   4152         }
   4153         mOrderedTypes.replaceAt(t, j);
   4154     }
   4155 
   4156     return NO_ERROR;
   4157 }
   4158 
   4159 void ResourceTable::Package::movePrivateAttrs() {
   4160     sp<Type> attr = mTypes.valueFor(String16("attr"));
   4161     if (attr == NULL) {
   4162         // Nothing to do.
   4163         return;
   4164     }
   4165 
   4166     Vector<sp<ConfigList> > privateAttrs;
   4167 
   4168     bool hasPublic = false;
   4169     const Vector<sp<ConfigList> >& configs = attr->getOrderedConfigs();
   4170     const size_t configCount = configs.size();
   4171     for (size_t i = 0; i < configCount; i++) {
   4172         if (configs[i] == NULL) {
   4173             continue;
   4174         }
   4175 
   4176         if (attr->isPublic(configs[i]->getName())) {
   4177             hasPublic = true;
   4178         } else {
   4179             privateAttrs.add(configs[i]);
   4180         }
   4181     }
   4182 
   4183     // Only if we have public attributes do we create a separate type for
   4184     // private attributes.
   4185     if (!hasPublic) {
   4186         return;
   4187     }
   4188 
   4189     // Create a new type for private attributes.
   4190     sp<Type> privateAttrType = getType(String16(kAttrPrivateType), SourcePos());
   4191 
   4192     const size_t privateAttrCount = privateAttrs.size();
   4193     for (size_t i = 0; i < privateAttrCount; i++) {
   4194         const sp<ConfigList>& cl = privateAttrs[i];
   4195 
   4196         // Remove the private attributes from their current type.
   4197         attr->removeEntry(cl->getName());
   4198 
   4199         // Add it to the new type.
   4200         const DefaultKeyedVector<ConfigDescription, sp<Entry> >& entries = cl->getEntries();
   4201         const size_t entryCount = entries.size();
   4202         for (size_t j = 0; j < entryCount; j++) {
   4203             const sp<Entry>& oldEntry = entries[j];
   4204             sp<Entry> entry = privateAttrType->getEntry(
   4205                     cl->getName(), oldEntry->getPos(), &entries.keyAt(j));
   4206             *entry = *oldEntry;
   4207         }
   4208 
   4209         // Move the symbols to the new type.
   4210 
   4211     }
   4212 }
   4213 
   4214 sp<ResourceTable::Package> ResourceTable::getPackage(const String16& package)
   4215 {
   4216     if (package != mAssetsPackage) {
   4217         return NULL;
   4218     }
   4219     return mPackages.valueFor(package);
   4220 }
   4221 
   4222 sp<ResourceTable::Type> ResourceTable::getType(const String16& package,
   4223                                                const String16& type,
   4224                                                const SourcePos& sourcePos,
   4225                                                bool doSetIndex)
   4226 {
   4227     sp<Package> p = getPackage(package);
   4228     if (p == NULL) {
   4229         return NULL;
   4230     }
   4231     return p->getType(type, sourcePos, doSetIndex);
   4232 }
   4233 
   4234 sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package,
   4235                                                  const String16& type,
   4236                                                  const String16& name,
   4237                                                  const SourcePos& sourcePos,
   4238                                                  bool overlay,
   4239                                                  const ResTable_config* config,
   4240                                                  bool doSetIndex)
   4241 {
   4242     sp<Type> t = getType(package, type, sourcePos, doSetIndex);
   4243     if (t == NULL) {
   4244         return NULL;
   4245     }
   4246     return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay());
   4247 }
   4248 
   4249 sp<ResourceTable::ConfigList> ResourceTable::getConfigList(const String16& package,
   4250         const String16& type, const String16& name) const
   4251 {
   4252     const size_t packageCount = mOrderedPackages.size();
   4253     for (size_t pi = 0; pi < packageCount; pi++) {
   4254         const sp<Package>& p = mOrderedPackages[pi];
   4255         if (p == NULL || p->getName() != package) {
   4256             continue;
   4257         }
   4258 
   4259         const Vector<sp<Type> >& types = p->getOrderedTypes();
   4260         const size_t typeCount = types.size();
   4261         for (size_t ti = 0; ti < typeCount; ti++) {
   4262             const sp<Type>& t = types[ti];
   4263             if (t == NULL || t->getName() != type) {
   4264                 continue;
   4265             }
   4266 
   4267             const Vector<sp<ConfigList> >& configs = t->getOrderedConfigs();
   4268             const size_t configCount = configs.size();
   4269             for (size_t ci = 0; ci < configCount; ci++) {
   4270                 const sp<ConfigList>& cl = configs[ci];
   4271                 if (cl == NULL || cl->getName() != name) {
   4272                     continue;
   4273                 }
   4274 
   4275                 return cl;
   4276             }
   4277         }
   4278     }
   4279     return NULL;
   4280 }
   4281 
   4282 sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
   4283                                                        const ResTable_config* config) const
   4284 {
   4285     size_t pid = Res_GETPACKAGE(resID)+1;
   4286     const size_t N = mOrderedPackages.size();
   4287     sp<Package> p;
   4288     for (size_t i = 0; i < N; i++) {
   4289         sp<Package> check = mOrderedPackages[i];
   4290         if (check->getAssignedId() == pid) {
   4291             p = check;
   4292             break;
   4293         }
   4294 
   4295     }
   4296     if (p == NULL) {
   4297         fprintf(stderr, "warning: Package not found for resource #%08x\n", resID);
   4298         return NULL;
   4299     }
   4300 
   4301     int tid = Res_GETTYPE(resID);
   4302     if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) {
   4303         fprintf(stderr, "warning: Type not found for resource #%08x\n", resID);
   4304         return NULL;
   4305     }
   4306     sp<Type> t = p->getOrderedTypes()[tid];
   4307 
   4308     int eid = Res_GETENTRY(resID);
   4309     if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) {
   4310         fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
   4311         return NULL;
   4312     }
   4313 
   4314     sp<ConfigList> c = t->getOrderedConfigs()[eid];
   4315     if (c == NULL) {
   4316         fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
   4317         return NULL;
   4318     }
   4319 
   4320     ConfigDescription cdesc;
   4321     if (config) cdesc = *config;
   4322     sp<Entry> e = c->getEntries().valueFor(cdesc);
   4323     if (c == NULL) {
   4324         fprintf(stderr, "warning: Entry configuration not found for resource #%08x\n", resID);
   4325         return NULL;
   4326     }
   4327 
   4328     return e;
   4329 }
   4330 
   4331 const ResourceTable::Item* ResourceTable::getItem(uint32_t resID, uint32_t attrID) const
   4332 {
   4333     sp<const Entry> e = getEntry(resID);
   4334     if (e == NULL) {
   4335         return NULL;
   4336     }
   4337 
   4338     const size_t N = e->getBag().size();
   4339     for (size_t i=0; i<N; i++) {
   4340         const Item& it = e->getBag().valueAt(i);
   4341         if (it.bagKeyId == 0) {
   4342             fprintf(stderr, "warning: ID not yet assigned to '%s' in bag '%s'\n",
   4343                     String8(e->getName()).string(),
   4344                     String8(e->getBag().keyAt(i)).string());
   4345         }
   4346         if (it.bagKeyId == attrID) {
   4347             return &it;
   4348         }
   4349     }
   4350 
   4351     return NULL;
   4352 }
   4353 
   4354 bool ResourceTable::getItemValue(
   4355     uint32_t resID, uint32_t attrID, Res_value* outValue)
   4356 {
   4357     const Item* item = getItem(resID, attrID);
   4358 
   4359     bool res = false;
   4360     if (item != NULL) {
   4361         if (item->evaluating) {
   4362             sp<const Entry> e = getEntry(resID);
   4363             const size_t N = e->getBag().size();
   4364             size_t i;
   4365             for (i=0; i<N; i++) {
   4366                 if (&e->getBag().valueAt(i) == item) {
   4367                     break;
   4368                 }
   4369             }
   4370             fprintf(stderr, "warning: Circular reference detected in key '%s' of bag '%s'\n",
   4371                     String8(e->getName()).string(),
   4372                     String8(e->getBag().keyAt(i)).string());
   4373             return false;
   4374         }
   4375         item->evaluating = true;
   4376         res = stringToValue(outValue, NULL, item->value, false, false, item->bagKeyId);
   4377         if (kIsDebug) {
   4378             if (res) {
   4379                 printf("getItemValue of #%08x[#%08x] (%s): type=#%08x, data=#%08x\n",
   4380                        resID, attrID, String8(getEntry(resID)->getName()).string(),
   4381                        outValue->dataType, outValue->data);
   4382             } else {
   4383                 printf("getItemValue of #%08x[#%08x]: failed\n",
   4384                        resID, attrID);
   4385             }
   4386         }
   4387         item->evaluating = false;
   4388     }
   4389     return res;
   4390 }
   4391 
   4392 /**
   4393  * Returns the SDK version at which the attribute was
   4394  * made public, or -1 if the resource ID is not an attribute
   4395  * or is not public.
   4396  */
   4397 int ResourceTable::getPublicAttributeSdkLevel(uint32_t attrId) const {
   4398     if (Res_GETPACKAGE(attrId) + 1 != 0x01 || Res_GETTYPE(attrId) + 1 != 0x01) {
   4399         return -1;
   4400     }
   4401 
   4402     uint32_t specFlags;
   4403     if (!mAssets->getIncludedResources().getResourceFlags(attrId, &specFlags)) {
   4404         return -1;
   4405     }
   4406 
   4407     if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) {
   4408         return -1;
   4409     }
   4410 
   4411     const size_t entryId = Res_GETENTRY(attrId);
   4412     if (entryId <= 0x021c) {
   4413         return 1;
   4414     } else if (entryId <= 0x021d) {
   4415         return 2;
   4416     } else if (entryId <= 0x0269) {
   4417         return SDK_CUPCAKE;
   4418     } else if (entryId <= 0x028d) {
   4419         return SDK_DONUT;
   4420     } else if (entryId <= 0x02ad) {
   4421         return SDK_ECLAIR;
   4422     } else if (entryId <= 0x02b3) {
   4423         return SDK_ECLAIR_0_1;
   4424     } else if (entryId <= 0x02b5) {
   4425         return SDK_ECLAIR_MR1;
   4426     } else if (entryId <= 0x02bd) {
   4427         return SDK_FROYO;
   4428     } else if (entryId <= 0x02cb) {
   4429         return SDK_GINGERBREAD;
   4430     } else if (entryId <= 0x0361) {
   4431         return SDK_HONEYCOMB;
   4432     } else if (entryId <= 0x0366) {
   4433         return SDK_HONEYCOMB_MR1;
   4434     } else if (entryId <= 0x03a6) {
   4435         return SDK_HONEYCOMB_MR2;
   4436     } else if (entryId <= 0x03ae) {
   4437         return SDK_JELLY_BEAN;
   4438     } else if (entryId <= 0x03cc) {
   4439         return SDK_JELLY_BEAN_MR1;
   4440     } else if (entryId <= 0x03da) {
   4441         return SDK_JELLY_BEAN_MR2;
   4442     } else if (entryId <= 0x03f1) {
   4443         return SDK_KITKAT;
   4444     } else if (entryId <= 0x03f6) {
   4445         return SDK_KITKAT_WATCH;
   4446     } else if (entryId <= 0x04ce) {
   4447         return SDK_LOLLIPOP;
   4448     } else {
   4449         // Anything else is marked as defined in
   4450         // SDK_LOLLIPOP_MR1 since after this
   4451         // version no attribute compat work
   4452         // needs to be done.
   4453         return SDK_LOLLIPOP_MR1;
   4454     }
   4455 }
   4456 
   4457 /**
   4458  * First check the Manifest, then check the command line flag.
   4459  */
   4460 static int getMinSdkVersion(const Bundle* bundle) {
   4461     if (bundle->getManifestMinSdkVersion() != NULL && strlen(bundle->getManifestMinSdkVersion()) > 0) {
   4462         return atoi(bundle->getManifestMinSdkVersion());
   4463     } else if (bundle->getMinSdkVersion() != NULL && strlen(bundle->getMinSdkVersion()) > 0) {
   4464         return atoi(bundle->getMinSdkVersion());
   4465     }
   4466     return 0;
   4467 }
   4468 
   4469 bool ResourceTable::shouldGenerateVersionedResource(
   4470         const sp<ResourceTable::ConfigList>& configList,
   4471         const ConfigDescription& sourceConfig,
   4472         const int sdkVersionToGenerate) {
   4473     assert(sdkVersionToGenerate > sourceConfig.sdkVersion);
   4474     const DefaultKeyedVector<ConfigDescription, sp<ResourceTable::Entry>>& entries
   4475             = configList->getEntries();
   4476     ssize_t idx = entries.indexOfKey(sourceConfig);
   4477 
   4478     // The source config came from this list, so it should be here.
   4479     assert(idx >= 0);
   4480 
   4481     // The next configuration either only varies in sdkVersion, or it is completely different
   4482     // and therefore incompatible. If it is incompatible, we must generate the versioned resource.
   4483 
   4484     // NOTE: The ordering of configurations takes sdkVersion as higher precedence than other
   4485     // qualifiers, so we need to iterate through the entire list to be sure there
   4486     // are no higher sdk level versions of this resource.
   4487     ConfigDescription tempConfig(sourceConfig);
   4488     for (size_t i = static_cast<size_t>(idx) + 1; i < entries.size(); i++) {
   4489         const ConfigDescription& nextConfig = entries.keyAt(i);
   4490         tempConfig.sdkVersion = nextConfig.sdkVersion;
   4491         if (tempConfig == nextConfig) {
   4492             // The two configs are the same, check the sdk version.
   4493             return sdkVersionToGenerate < nextConfig.sdkVersion;
   4494         }
   4495     }
   4496 
   4497     // No match was found, so we should generate the versioned resource.
   4498     return true;
   4499 }
   4500 
   4501 /**
   4502  * Modifies the entries in the resource table to account for compatibility
   4503  * issues with older versions of Android.
   4504  *
   4505  * This primarily handles the issue of private/public attribute clashes
   4506  * in framework resources.
   4507  *
   4508  * AAPT has traditionally assigned resource IDs to public attributes,
   4509  * and then followed those public definitions with private attributes.
   4510  *
   4511  * --- PUBLIC ---
   4512  * | 0x01010234 | attr/color
   4513  * | 0x01010235 | attr/background
   4514  *
   4515  * --- PRIVATE ---
   4516  * | 0x01010236 | attr/secret
   4517  * | 0x01010237 | attr/shhh
   4518  *
   4519  * Each release, when attributes are added, they take the place of the private
   4520  * attributes and the private attributes are shifted down again.
   4521  *
   4522  * --- PUBLIC ---
   4523  * | 0x01010234 | attr/color
   4524  * | 0x01010235 | attr/background
   4525  * | 0x01010236 | attr/shinyNewAttr
   4526  * | 0x01010237 | attr/highlyValuedFeature
   4527  *
   4528  * --- PRIVATE ---
   4529  * | 0x01010238 | attr/secret
   4530  * | 0x01010239 | attr/shhh
   4531  *
   4532  * Platform code may look for private attributes set in a theme. If an app
   4533  * compiled against a newer version of the platform uses a new public
   4534  * attribute that happens to have the same ID as the private attribute
   4535  * the older platform is expecting, then the behavior is undefined.
   4536  *
   4537  * We get around this by detecting any newly defined attributes (in L),
   4538  * copy the resource into a -v21 qualified resource, and delete the
   4539  * attribute from the original resource. This ensures that older platforms
   4540  * don't see the new attribute, but when running on L+ platforms, the
   4541  * attribute will be respected.
   4542  */
   4543 status_t ResourceTable::modifyForCompat(const Bundle* bundle) {
   4544     const int minSdk = getMinSdkVersion(bundle);
   4545     if (minSdk >= SDK_LOLLIPOP_MR1) {
   4546         // Lollipop MR1 and up handles public attributes differently, no
   4547         // need to do any compat modifications.
   4548         return NO_ERROR;
   4549     }
   4550 
   4551     const String16 attr16("attr");
   4552 
   4553     const size_t packageCount = mOrderedPackages.size();
   4554     for (size_t pi = 0; pi < packageCount; pi++) {
   4555         sp<Package> p = mOrderedPackages.itemAt(pi);
   4556         if (p == NULL || p->getTypes().size() == 0) {
   4557             // Empty, skip!
   4558             continue;
   4559         }
   4560 
   4561         const size_t typeCount = p->getOrderedTypes().size();
   4562         for (size_t ti = 0; ti < typeCount; ti++) {
   4563             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   4564             if (t == NULL) {
   4565                 continue;
   4566             }
   4567 
   4568             const size_t configCount = t->getOrderedConfigs().size();
   4569             for (size_t ci = 0; ci < configCount; ci++) {
   4570                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
   4571                 if (c == NULL) {
   4572                     continue;
   4573                 }
   4574 
   4575                 Vector<key_value_pair_t<ConfigDescription, sp<Entry> > > entriesToAdd;
   4576                 const DefaultKeyedVector<ConfigDescription, sp<Entry> >& entries =
   4577                         c->getEntries();
   4578                 const size_t entryCount = entries.size();
   4579                 for (size_t ei = 0; ei < entryCount; ei++) {
   4580                     sp<Entry> e = entries.valueAt(ei);
   4581                     if (e == NULL || e->getType() != Entry::TYPE_BAG) {
   4582                         continue;
   4583                     }
   4584 
   4585                     const ConfigDescription& config = entries.keyAt(ei);
   4586                     if (config.sdkVersion >= SDK_LOLLIPOP_MR1) {
   4587                         continue;
   4588                     }
   4589 
   4590                     KeyedVector<int, Vector<String16> > attributesToRemove;
   4591                     const KeyedVector<String16, Item>& bag = e->getBag();
   4592                     const size_t bagCount = bag.size();
   4593                     for (size_t bi = 0; bi < bagCount; bi++) {
   4594                         const uint32_t attrId = getResId(bag.keyAt(bi), &attr16);
   4595                         const int sdkLevel = getPublicAttributeSdkLevel(attrId);
   4596                         if (sdkLevel > 1 && sdkLevel > config.sdkVersion && sdkLevel > minSdk) {
   4597                             AaptUtil::appendValue(attributesToRemove, sdkLevel, bag.keyAt(bi));
   4598                         }
   4599                     }
   4600 
   4601                     if (attributesToRemove.isEmpty()) {
   4602                         continue;
   4603                     }
   4604 
   4605                     const size_t sdkCount = attributesToRemove.size();
   4606                     for (size_t i = 0; i < sdkCount; i++) {
   4607                         const int sdkLevel = attributesToRemove.keyAt(i);
   4608 
   4609                         if (!shouldGenerateVersionedResource(c, config, sdkLevel)) {
   4610                             // There is a style that will override this generated one.
   4611                             continue;
   4612                         }
   4613 
   4614                         // Duplicate the entry under the same configuration
   4615                         // but with sdkVersion == sdkLevel.
   4616                         ConfigDescription newConfig(config);
   4617                         newConfig.sdkVersion = sdkLevel;
   4618 
   4619                         sp<Entry> newEntry = new Entry(*e);
   4620 
   4621                         // Remove all items that have a higher SDK level than
   4622                         // the one we are synthesizing.
   4623                         for (size_t j = 0; j < sdkCount; j++) {
   4624                             if (j == i) {
   4625                                 continue;
   4626                             }
   4627 
   4628                             if (attributesToRemove.keyAt(j) > sdkLevel) {
   4629                                 const size_t attrCount = attributesToRemove[j].size();
   4630                                 for (size_t k = 0; k < attrCount; k++) {
   4631                                     newEntry->removeFromBag(attributesToRemove[j][k]);
   4632                                 }
   4633                             }
   4634                         }
   4635 
   4636                         entriesToAdd.add(key_value_pair_t<ConfigDescription, sp<Entry> >(
   4637                                 newConfig, newEntry));
   4638                     }
   4639 
   4640                     // Remove the attribute from the original.
   4641                     for (size_t i = 0; i < attributesToRemove.size(); i++) {
   4642                         for (size_t j = 0; j < attributesToRemove[i].size(); j++) {
   4643                             e->removeFromBag(attributesToRemove[i][j]);
   4644                         }
   4645                     }
   4646                 }
   4647 
   4648                 const size_t entriesToAddCount = entriesToAdd.size();
   4649                 for (size_t i = 0; i < entriesToAddCount; i++) {
   4650                     assert(entries.indexOfKey(entriesToAdd[i].key) < 0);
   4651 
   4652                     if (bundle->getVerbose()) {
   4653                         entriesToAdd[i].value->getPos()
   4654                                 .printf("using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.",
   4655                                         entriesToAdd[i].key.sdkVersion,
   4656                                         String8(p->getName()).string(),
   4657                                         String8(t->getName()).string(),
   4658                                         String8(entriesToAdd[i].value->getName()).string(),
   4659                                         entriesToAdd[i].key.toString().string());
   4660                     }
   4661 
   4662                     sp<Entry> newEntry = t->getEntry(c->getName(),
   4663                             entriesToAdd[i].value->getPos(),
   4664                             &entriesToAdd[i].key);
   4665 
   4666                     *newEntry = *entriesToAdd[i].value;
   4667                 }
   4668             }
   4669         }
   4670     }
   4671     return NO_ERROR;
   4672 }
   4673 
   4674 status_t ResourceTable::modifyForCompat(const Bundle* bundle,
   4675                                         const String16& resourceName,
   4676                                         const sp<AaptFile>& target,
   4677                                         const sp<XMLNode>& root) {
   4678     const String16 vector16("vector");
   4679     const String16 animatedVector16("animated-vector");
   4680 
   4681     const int minSdk = getMinSdkVersion(bundle);
   4682     if (minSdk >= SDK_LOLLIPOP_MR1) {
   4683         // Lollipop MR1 and up handles public attributes differently, no
   4684         // need to do any compat modifications.
   4685         return NO_ERROR;
   4686     }
   4687 
   4688     const ConfigDescription config(target->getGroupEntry().toParams());
   4689     if (target->getResourceType() == "" || config.sdkVersion >= SDK_LOLLIPOP_MR1) {
   4690         // Skip resources that have no type (AndroidManifest.xml) or are already version qualified
   4691         // with v21 or higher.
   4692         return NO_ERROR;
   4693     }
   4694 
   4695     sp<XMLNode> newRoot = NULL;
   4696     int sdkVersionToGenerate = SDK_LOLLIPOP_MR1;
   4697 
   4698     Vector<sp<XMLNode> > nodesToVisit;
   4699     nodesToVisit.push(root);
   4700     while (!nodesToVisit.isEmpty()) {
   4701         sp<XMLNode> node = nodesToVisit.top();
   4702         nodesToVisit.pop();
   4703 
   4704         if (bundle->getNoVersionVectors() && (node->getElementName() == vector16 ||
   4705                     node->getElementName() == animatedVector16)) {
   4706             // We were told not to version vector tags, so skip the children here.
   4707             continue;
   4708         }
   4709 
   4710         const Vector<XMLNode::attribute_entry>& attrs = node->getAttributes();
   4711         for (size_t i = 0; i < attrs.size(); i++) {
   4712             const XMLNode::attribute_entry& attr = attrs[i];
   4713             const int sdkLevel = getPublicAttributeSdkLevel(attr.nameResId);
   4714             if (sdkLevel > 1 && sdkLevel > config.sdkVersion && sdkLevel > minSdk) {
   4715                 if (newRoot == NULL) {
   4716                     newRoot = root->clone();
   4717                 }
   4718 
   4719                 // Find the smallest sdk version that we need to synthesize for
   4720                 // and do that one. Subsequent versions will be processed on
   4721                 // the next pass.
   4722                 sdkVersionToGenerate = std::min(sdkLevel, sdkVersionToGenerate);
   4723 
   4724                 if (bundle->getVerbose()) {
   4725                     SourcePos(node->getFilename(), node->getStartLineNumber()).printf(
   4726                             "removing attribute %s%s%s from <%s>",
   4727                             String8(attr.ns).string(),
   4728                             (attr.ns.size() == 0 ? "" : ":"),
   4729                             String8(attr.name).string(),
   4730                             String8(node->getElementName()).string());
   4731                 }
   4732                 node->removeAttribute(i);
   4733                 i--;
   4734             }
   4735         }
   4736 
   4737         // Schedule a visit to the children.
   4738         const Vector<sp<XMLNode> >& children = node->getChildren();
   4739         const size_t childCount = children.size();
   4740         for (size_t i = 0; i < childCount; i++) {
   4741             nodesToVisit.push(children[i]);
   4742         }
   4743     }
   4744 
   4745     if (newRoot == NULL) {
   4746         return NO_ERROR;
   4747     }
   4748 
   4749     // Look to see if we already have an overriding v21 configuration.
   4750     sp<ConfigList> cl = getConfigList(String16(mAssets->getPackage()),
   4751             String16(target->getResourceType()), resourceName);
   4752     if (shouldGenerateVersionedResource(cl, config, sdkVersionToGenerate)) {
   4753         // We don't have an overriding entry for v21, so we must duplicate this one.
   4754         ConfigDescription newConfig(config);
   4755         newConfig.sdkVersion = sdkVersionToGenerate;
   4756         sp<AaptFile> newFile = new AaptFile(target->getSourceFile(),
   4757                 AaptGroupEntry(newConfig), target->getResourceType());
   4758         String8 resPath = String8::format("res/%s/%s",
   4759                 newFile->getGroupEntry().toDirName(target->getResourceType()).string(),
   4760                 target->getSourceFile().getPathLeaf().string());
   4761         resPath.convertToResPath();
   4762 
   4763         // Add a resource table entry.
   4764         if (bundle->getVerbose()) {
   4765             SourcePos(target->getSourceFile(), -1).printf(
   4766                     "using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.",
   4767                     newConfig.sdkVersion,
   4768                     mAssets->getPackage().string(),
   4769                     newFile->getResourceType().string(),
   4770                     String8(resourceName).string(),
   4771                     newConfig.toString().string());
   4772         }
   4773 
   4774         addEntry(SourcePos(),
   4775                 String16(mAssets->getPackage()),
   4776                 String16(target->getResourceType()),
   4777                 resourceName,
   4778                 String16(resPath),
   4779                 NULL,
   4780                 &newConfig);
   4781 
   4782         // Schedule this to be compiled.
   4783         CompileResourceWorkItem item;
   4784         item.resourceName = resourceName;
   4785         item.resPath = resPath;
   4786         item.file = newFile;
   4787         mWorkQueue.push(item);
   4788     }
   4789 
   4790     return NO_ERROR;
   4791 }
   4792 
   4793 void ResourceTable::getDensityVaryingResources(
   4794         KeyedVector<Symbol, Vector<SymbolDefinition> >& resources) {
   4795     const ConfigDescription nullConfig;
   4796 
   4797     const size_t packageCount = mOrderedPackages.size();
   4798     for (size_t p = 0; p < packageCount; p++) {
   4799         const Vector<sp<Type> >& types = mOrderedPackages[p]->getOrderedTypes();
   4800         const size_t typeCount = types.size();
   4801         for (size_t t = 0; t < typeCount; t++) {
   4802             const Vector<sp<ConfigList> >& configs = types[t]->getOrderedConfigs();
   4803             const size_t configCount = configs.size();
   4804             for (size_t c = 0; c < configCount; c++) {
   4805                 const DefaultKeyedVector<ConfigDescription, sp<Entry> >& configEntries
   4806                         = configs[c]->getEntries();
   4807                 const size_t configEntryCount = configEntries.size();
   4808                 for (size_t ce = 0; ce < configEntryCount; ce++) {
   4809                     const ConfigDescription& config = configEntries.keyAt(ce);
   4810                     if (AaptConfig::isDensityOnly(config)) {
   4811                         // This configuration only varies with regards to density.
   4812                         const Symbol symbol(
   4813                                 mOrderedPackages[p]->getName(),
   4814                                 types[t]->getName(),
   4815                                 configs[c]->getName(),
   4816                                 getResId(mOrderedPackages[p], types[t],
   4817                                          configs[c]->getEntryIndex()));
   4818 
   4819                         const sp<Entry>& entry = configEntries.valueAt(ce);
   4820                         AaptUtil::appendValue(resources, symbol,
   4821                                               SymbolDefinition(symbol, config, entry->getPos()));
   4822                     }
   4823                 }
   4824             }
   4825         }
   4826     }
   4827 }
   4828