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