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         // Assign indices...
   2627         const size_t typeCount = p->getOrderedTypes().size();
   2628         for (size_t ti = 0; ti < typeCount; ti++) {
   2629             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2630             if (t == NULL) {
   2631                 continue;
   2632             }
   2633 
   2634             err = t->applyPublicEntryOrder();
   2635             if (err != NO_ERROR && firstError == NO_ERROR) {
   2636                 firstError = err;
   2637             }
   2638 
   2639             const size_t N = t->getOrderedConfigs().size();
   2640             t->setIndex(ti + 1 + typeIdOffset);
   2641 
   2642             LOG_ALWAYS_FATAL_IF(ti == 0 && attr != t,
   2643                                 "First type is not attr!");
   2644 
   2645             for (size_t ei=0; ei<N; ei++) {
   2646                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ei);
   2647                 if (c == NULL) {
   2648                     continue;
   2649                 }
   2650                 c->setEntryIndex(ei);
   2651             }
   2652         }
   2653 
   2654 
   2655         // Assign resource IDs to keys in bags...
   2656         for (size_t ti = 0; ti < typeCount; ti++) {
   2657             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2658             if (t == NULL) {
   2659                 continue;
   2660             }
   2661 
   2662             const size_t N = t->getOrderedConfigs().size();
   2663             for (size_t ci=0; ci<N; ci++) {
   2664                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
   2665                 if (c == NULL) {
   2666                     continue;
   2667                 }
   2668                 //printf("Ordered config #%d: %p\n", ci, c.get());
   2669                 const size_t N = c->getEntries().size();
   2670                 for (size_t ei=0; ei<N; ei++) {
   2671                     sp<Entry> e = c->getEntries().valueAt(ei);
   2672                     if (e == NULL) {
   2673                         continue;
   2674                     }
   2675                     status_t err = e->assignResourceIds(this, p->getName());
   2676                     if (err != NO_ERROR && firstError == NO_ERROR) {
   2677                         firstError = err;
   2678                     }
   2679                 }
   2680             }
   2681         }
   2682     }
   2683     return firstError;
   2684 }
   2685 
   2686 status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols,
   2687         bool skipSymbolsWithoutDefaultLocalization) {
   2688     const size_t N = mOrderedPackages.size();
   2689     const String8 defaultLocale;
   2690     const String16 stringType("string");
   2691     size_t pi;
   2692 
   2693     for (pi=0; pi<N; pi++) {
   2694         sp<Package> p = mOrderedPackages.itemAt(pi);
   2695         if (p->getTypes().size() == 0) {
   2696             // Empty, skip!
   2697             continue;
   2698         }
   2699 
   2700         const size_t N = p->getOrderedTypes().size();
   2701         size_t ti;
   2702 
   2703         for (ti=0; ti<N; ti++) {
   2704             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2705             if (t == NULL) {
   2706                 continue;
   2707             }
   2708 
   2709             const size_t N = t->getOrderedConfigs().size();
   2710             sp<AaptSymbols> typeSymbols;
   2711             if (t->getName() == String16(kAttrPrivateType)) {
   2712                 typeSymbols = outSymbols->addNestedSymbol(String8("attr"), t->getPos());
   2713             } else {
   2714                 typeSymbols = outSymbols->addNestedSymbol(String8(t->getName()), t->getPos());
   2715             }
   2716 
   2717             if (typeSymbols == NULL) {
   2718                 return UNKNOWN_ERROR;
   2719             }
   2720 
   2721             for (size_t ci=0; ci<N; ci++) {
   2722                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
   2723                 if (c == NULL) {
   2724                     continue;
   2725                 }
   2726                 uint32_t rid = getResId(p, t, ci);
   2727                 if (rid == 0) {
   2728                     return UNKNOWN_ERROR;
   2729                 }
   2730                 if (Res_GETPACKAGE(rid) + 1 == p->getAssignedId()) {
   2731 
   2732                     if (skipSymbolsWithoutDefaultLocalization &&
   2733                             t->getName() == stringType) {
   2734 
   2735                         // Don't generate symbols for strings without a default localization.
   2736                         if (mHasDefaultLocalization.find(c->getName())
   2737                                 == mHasDefaultLocalization.end()) {
   2738                             // printf("Skip symbol [%08x] %s\n", rid,
   2739                             //          String8(c->getName()).string());
   2740                             continue;
   2741                         }
   2742                     }
   2743 
   2744                     typeSymbols->addSymbol(String8(c->getName()), rid, c->getPos());
   2745 
   2746                     String16 comment(c->getComment());
   2747                     typeSymbols->appendComment(String8(c->getName()), comment, c->getPos());
   2748                     //printf("Type symbol [%08x] %s comment: %s\n", rid,
   2749                     //        String8(c->getName()).string(), String8(comment).string());
   2750                     comment = c->getTypeComment();
   2751                     typeSymbols->appendTypeComment(String8(c->getName()), comment);
   2752                 }
   2753             }
   2754         }
   2755     }
   2756     return NO_ERROR;
   2757 }
   2758 
   2759 
   2760 void
   2761 ResourceTable::addLocalization(const String16& name, const String8& locale, const SourcePos& src)
   2762 {
   2763     mLocalizations[name][locale] = src;
   2764 }
   2765 
   2766 void
   2767 ResourceTable::addDefaultLocalization(const String16& name)
   2768 {
   2769     mHasDefaultLocalization.insert(name);
   2770 }
   2771 
   2772 
   2773 /*!
   2774  * Flag various sorts of localization problems.  '+' indicates checks already implemented;
   2775  * '-' indicates checks that will be implemented in the future.
   2776  *
   2777  * + A localized string for which no default-locale version exists => warning
   2778  * + A string for which no version in an explicitly-requested locale exists => warning
   2779  * + A localized translation of an translateable="false" string => warning
   2780  * - A localized string not provided in every locale used by the table
   2781  */
   2782 status_t
   2783 ResourceTable::validateLocalizations(void)
   2784 {
   2785     status_t err = NO_ERROR;
   2786     const String8 defaultLocale;
   2787 
   2788     // For all strings...
   2789     for (const auto& nameIter : mLocalizations) {
   2790         const std::map<String8, SourcePos>& configSrcMap = nameIter.second;
   2791 
   2792         // Look for strings with no default localization
   2793         if (configSrcMap.count(defaultLocale) == 0) {
   2794             SourcePos().warning("string '%s' has no default translation.",
   2795                     String8(nameIter.first).string());
   2796             if (mBundle->getVerbose()) {
   2797                 for (const auto& locale : configSrcMap) {
   2798                     locale.second.printf("locale %s found", locale.first.string());
   2799                 }
   2800             }
   2801             // !!! TODO: throw an error here in some circumstances
   2802         }
   2803 
   2804         // Check that all requested localizations are present for this string
   2805         if (mBundle->getConfigurations().size() > 0 && mBundle->getRequireLocalization()) {
   2806             const char* allConfigs = mBundle->getConfigurations().string();
   2807             const char* start = allConfigs;
   2808             const char* comma;
   2809 
   2810             std::set<String8> missingConfigs;
   2811             AaptLocaleValue locale;
   2812             do {
   2813                 String8 config;
   2814                 comma = strchr(start, ',');
   2815                 if (comma != NULL) {
   2816                     config.setTo(start, comma - start);
   2817                     start = comma + 1;
   2818                 } else {
   2819                     config.setTo(start);
   2820                 }
   2821 
   2822                 if (!locale.initFromFilterString(config)) {
   2823                     continue;
   2824                 }
   2825 
   2826                 // don't bother with the pseudolocale "en_XA" or "ar_XB"
   2827                 if (config != "en_XA" && config != "ar_XB") {
   2828                     if (configSrcMap.find(config) == configSrcMap.end()) {
   2829                         // okay, no specific localization found.  it's possible that we are
   2830                         // requiring a specific regional localization [e.g. de_DE] but there is an
   2831                         // available string in the generic language localization [e.g. de];
   2832                         // consider that string to have fulfilled the localization requirement.
   2833                         String8 region(config.string(), 2);
   2834                         if (configSrcMap.find(region) == configSrcMap.end() &&
   2835                                 configSrcMap.count(defaultLocale) == 0) {
   2836                             missingConfigs.insert(config);
   2837                         }
   2838                     }
   2839                 }
   2840             } while (comma != NULL);
   2841 
   2842             if (!missingConfigs.empty()) {
   2843                 String8 configStr;
   2844                 for (const auto& iter : missingConfigs) {
   2845                     configStr.appendFormat(" %s", iter.string());
   2846                 }
   2847                 SourcePos().warning("string '%s' is missing %u required localizations:%s",
   2848                         String8(nameIter.first).string(),
   2849                         (unsigned int)missingConfigs.size(),
   2850                         configStr.string());
   2851             }
   2852         }
   2853     }
   2854 
   2855     return err;
   2856 }
   2857 
   2858 status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter,
   2859         const sp<AaptFile>& dest,
   2860         const bool isBase)
   2861 {
   2862     const ConfigDescription nullConfig;
   2863 
   2864     const size_t N = mOrderedPackages.size();
   2865     size_t pi;
   2866 
   2867     const static String16 mipmap16("mipmap");
   2868 
   2869     bool useUTF8 = !bundle->getUTF16StringsOption();
   2870 
   2871     // The libraries this table references.
   2872     Vector<sp<Package> > libraryPackages;
   2873     const ResTable& table = mAssets->getIncludedResources();
   2874     const size_t basePackageCount = table.getBasePackageCount();
   2875     for (size_t i = 0; i < basePackageCount; i++) {
   2876         size_t packageId = table.getBasePackageId(i);
   2877         String16 packageName(table.getBasePackageName(i));
   2878         if (packageId > 0x01 && packageId != 0x7f &&
   2879                 packageName != String16("android")) {
   2880             libraryPackages.add(sp<Package>(new Package(packageName, packageId)));
   2881         }
   2882     }
   2883 
   2884     // Iterate through all data, collecting all values (strings,
   2885     // references, etc).
   2886     StringPool valueStrings(useUTF8);
   2887     Vector<sp<Entry> > allEntries;
   2888     for (pi=0; pi<N; pi++) {
   2889         sp<Package> p = mOrderedPackages.itemAt(pi);
   2890         if (p->getTypes().size() == 0) {
   2891             continue;
   2892         }
   2893 
   2894         StringPool typeStrings(useUTF8);
   2895         StringPool keyStrings(useUTF8);
   2896 
   2897         ssize_t stringsAdded = 0;
   2898         const size_t N = p->getOrderedTypes().size();
   2899         for (size_t ti=0; ti<N; ti++) {
   2900             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2901             if (t == NULL) {
   2902                 typeStrings.add(String16("<empty>"), false);
   2903                 stringsAdded++;
   2904                 continue;
   2905             }
   2906 
   2907             while (stringsAdded < t->getIndex() - 1) {
   2908                 typeStrings.add(String16("<empty>"), false);
   2909                 stringsAdded++;
   2910             }
   2911 
   2912             const String16 typeName(t->getName());
   2913             typeStrings.add(typeName, false);
   2914             stringsAdded++;
   2915 
   2916             // This is a hack to tweak the sorting order of the final strings,
   2917             // to put stuff that is generally not language-specific first.
   2918             String8 configTypeName(typeName);
   2919             if (configTypeName == "drawable" || configTypeName == "layout"
   2920                     || configTypeName == "color" || configTypeName == "anim"
   2921                     || configTypeName == "interpolator" || configTypeName == "animator"
   2922                     || configTypeName == "xml" || configTypeName == "menu"
   2923                     || configTypeName == "mipmap" || configTypeName == "raw") {
   2924                 configTypeName = "1complex";
   2925             } else {
   2926                 configTypeName = "2value";
   2927             }
   2928 
   2929             // mipmaps don't get filtered, so they will
   2930             // allways end up in the base. Make sure they
   2931             // don't end up in a split.
   2932             if (typeName == mipmap16 && !isBase) {
   2933                 continue;
   2934             }
   2935 
   2936             const bool filterable = (typeName != mipmap16);
   2937 
   2938             const size_t N = t->getOrderedConfigs().size();
   2939             for (size_t ci=0; ci<N; ci++) {
   2940                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
   2941                 if (c == NULL) {
   2942                     continue;
   2943                 }
   2944                 const size_t N = c->getEntries().size();
   2945                 for (size_t ei=0; ei<N; ei++) {
   2946                     ConfigDescription config = c->getEntries().keyAt(ei);
   2947                     if (filterable && !filter->match(config)) {
   2948                         continue;
   2949                     }
   2950                     sp<Entry> e = c->getEntries().valueAt(ei);
   2951                     if (e == NULL) {
   2952                         continue;
   2953                     }
   2954                     e->setNameIndex(keyStrings.add(e->getName(), true));
   2955 
   2956                     // If this entry has no values for other configs,
   2957                     // and is the default config, then it is special.  Otherwise
   2958                     // we want to add it with the config info.
   2959                     ConfigDescription* valueConfig = NULL;
   2960                     if (N != 1 || config == nullConfig) {
   2961                         valueConfig = &config;
   2962                     }
   2963 
   2964                     status_t err = e->prepareFlatten(&valueStrings, this,
   2965                             &configTypeName, &config);
   2966                     if (err != NO_ERROR) {
   2967                         return err;
   2968                     }
   2969                     allEntries.add(e);
   2970                 }
   2971             }
   2972         }
   2973 
   2974         p->setTypeStrings(typeStrings.createStringBlock());
   2975         p->setKeyStrings(keyStrings.createStringBlock());
   2976     }
   2977 
   2978     if (bundle->getOutputAPKFile() != NULL) {
   2979         // Now we want to sort the value strings for better locality.  This will
   2980         // cause the positions of the strings to change, so we need to go back
   2981         // through out resource entries and update them accordingly.  Only need
   2982         // to do this if actually writing the output file.
   2983         valueStrings.sortByConfig();
   2984         for (pi=0; pi<allEntries.size(); pi++) {
   2985             allEntries[pi]->remapStringValue(&valueStrings);
   2986         }
   2987     }
   2988 
   2989     ssize_t strAmt = 0;
   2990 
   2991     // Now build the array of package chunks.
   2992     Vector<sp<AaptFile> > flatPackages;
   2993     for (pi=0; pi<N; pi++) {
   2994         sp<Package> p = mOrderedPackages.itemAt(pi);
   2995         if (p->getTypes().size() == 0) {
   2996             // Empty, skip!
   2997             continue;
   2998         }
   2999 
   3000         const size_t N = p->getTypeStrings().size();
   3001 
   3002         const size_t baseSize = sizeof(ResTable_package);
   3003 
   3004         // Start the package data.
   3005         sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
   3006         ResTable_package* header = (ResTable_package*)data->editData(baseSize);
   3007         if (header == NULL) {
   3008             fprintf(stderr, "ERROR: out of memory creating ResTable_package\n");
   3009             return NO_MEMORY;
   3010         }
   3011         memset(header, 0, sizeof(*header));
   3012         header->header.type = htods(RES_TABLE_PACKAGE_TYPE);
   3013         header->header.headerSize = htods(sizeof(*header));
   3014         header->id = htodl(static_cast<uint32_t>(p->getAssignedId()));
   3015         strcpy16_htod(header->name, p->getName().string());
   3016 
   3017         // Write the string blocks.
   3018         const size_t typeStringsStart = data->getSize();
   3019         sp<AaptFile> strFile = p->getTypeStringsData();
   3020         ssize_t amt = data->writeData(strFile->getData(), strFile->getSize());
   3021         if (kPrintStringMetrics) {
   3022             fprintf(stderr, "**** type strings: %zd\n", SSIZE(amt));
   3023         }
   3024         strAmt += amt;
   3025         if (amt < 0) {
   3026             return amt;
   3027         }
   3028         const size_t keyStringsStart = data->getSize();
   3029         strFile = p->getKeyStringsData();
   3030         amt = data->writeData(strFile->getData(), strFile->getSize());
   3031         if (kPrintStringMetrics) {
   3032             fprintf(stderr, "**** key strings: %zd\n", SSIZE(amt));
   3033         }
   3034         strAmt += amt;
   3035         if (amt < 0) {
   3036             return amt;
   3037         }
   3038 
   3039         if (isBase) {
   3040             status_t err = flattenLibraryTable(data, libraryPackages);
   3041             if (err != NO_ERROR) {
   3042                 fprintf(stderr, "ERROR: failed to write library table\n");
   3043                 return err;
   3044             }
   3045         }
   3046 
   3047         // Build the type chunks inside of this package.
   3048         for (size_t ti=0; ti<N; ti++) {
   3049             // Retrieve them in the same order as the type string block.
   3050             size_t len;
   3051             String16 typeName(p->getTypeStrings().stringAt(ti, &len));
   3052             sp<Type> t = p->getTypes().valueFor(typeName);
   3053             LOG_ALWAYS_FATAL_IF(t == NULL && typeName != String16("<empty>"),
   3054                                 "Type name %s not found",
   3055                                 String8(typeName).string());
   3056             if (t == NULL) {
   3057                 continue;
   3058             }
   3059             const bool filterable = (typeName != mipmap16);
   3060             const bool skipEntireType = (typeName == mipmap16 && !isBase);
   3061 
   3062             const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
   3063 
   3064             // Until a non-NO_ENTRY value has been written for a resource,
   3065             // that resource is invalid; validResources[i] represents
   3066             // the item at t->getOrderedConfigs().itemAt(i).
   3067             Vector<bool> validResources;
   3068             validResources.insertAt(false, 0, N);
   3069 
   3070             // First write the typeSpec chunk, containing information about
   3071             // each resource entry in this type.
   3072             {
   3073                 const size_t typeSpecSize = sizeof(ResTable_typeSpec) + sizeof(uint32_t)*N;
   3074                 const size_t typeSpecStart = data->getSize();
   3075                 ResTable_typeSpec* tsHeader = (ResTable_typeSpec*)
   3076                     (((uint8_t*)data->editData(typeSpecStart+typeSpecSize)) + typeSpecStart);
   3077                 if (tsHeader == NULL) {
   3078                     fprintf(stderr, "ERROR: out of memory creating ResTable_typeSpec\n");
   3079                     return NO_MEMORY;
   3080                 }
   3081                 memset(tsHeader, 0, sizeof(*tsHeader));
   3082                 tsHeader->header.type = htods(RES_TABLE_TYPE_SPEC_TYPE);
   3083                 tsHeader->header.headerSize = htods(sizeof(*tsHeader));
   3084                 tsHeader->header.size = htodl(typeSpecSize);
   3085                 tsHeader->id = ti+1;
   3086                 tsHeader->entryCount = htodl(N);
   3087 
   3088                 uint32_t* typeSpecFlags = (uint32_t*)
   3089                     (((uint8_t*)data->editData())
   3090                         + typeSpecStart + sizeof(ResTable_typeSpec));
   3091                 memset(typeSpecFlags, 0, sizeof(uint32_t)*N);
   3092 
   3093                 for (size_t ei=0; ei<N; ei++) {
   3094                     sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
   3095                     if (cl == NULL) {
   3096                         continue;
   3097                     }
   3098 
   3099                     if (cl->getPublic()) {
   3100                         typeSpecFlags[ei] |= htodl(ResTable_typeSpec::SPEC_PUBLIC);
   3101                     }
   3102 
   3103                     if (skipEntireType) {
   3104                         continue;
   3105                     }
   3106 
   3107                     const size_t CN = cl->getEntries().size();
   3108                     for (size_t ci=0; ci<CN; ci++) {
   3109                         if (filterable && !filter->match(cl->getEntries().keyAt(ci))) {
   3110                             continue;
   3111                         }
   3112                         for (size_t cj=ci+1; cj<CN; cj++) {
   3113                             if (filterable && !filter->match(cl->getEntries().keyAt(cj))) {
   3114                                 continue;
   3115                             }
   3116                             typeSpecFlags[ei] |= htodl(
   3117                                 cl->getEntries().keyAt(ci).diff(cl->getEntries().keyAt(cj)));
   3118                         }
   3119                     }
   3120                 }
   3121             }
   3122 
   3123             if (skipEntireType) {
   3124                 continue;
   3125             }
   3126 
   3127             // We need to write one type chunk for each configuration for
   3128             // which we have entries in this type.
   3129             SortedVector<ConfigDescription> uniqueConfigs;
   3130             if (t != NULL) {
   3131                 uniqueConfigs = t->getUniqueConfigs();
   3132             }
   3133 
   3134             const size_t typeSize = sizeof(ResTable_type) + sizeof(uint32_t)*N;
   3135 
   3136             const size_t NC = uniqueConfigs.size();
   3137             for (size_t ci=0; ci<NC; ci++) {
   3138                 const ConfigDescription& config = uniqueConfigs[ci];
   3139 
   3140                 if (kIsDebug) {
   3141                     printf("Writing config %zu config: imsi:%d/%d lang:%c%c cnt:%c%c "
   3142                         "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
   3143                         "sw%ddp w%ddp h%ddp layout:%d\n",
   3144                         ti + 1,
   3145                         config.mcc, config.mnc,
   3146                         config.language[0] ? config.language[0] : '-',
   3147                         config.language[1] ? config.language[1] : '-',
   3148                         config.country[0] ? config.country[0] : '-',
   3149                         config.country[1] ? config.country[1] : '-',
   3150                         config.orientation,
   3151                         config.uiMode,
   3152                         config.touchscreen,
   3153                         config.density,
   3154                         config.keyboard,
   3155                         config.inputFlags,
   3156                         config.navigation,
   3157                         config.screenWidth,
   3158                         config.screenHeight,
   3159                         config.smallestScreenWidthDp,
   3160                         config.screenWidthDp,
   3161                         config.screenHeightDp,
   3162                         config.screenLayout);
   3163                 }
   3164 
   3165                 if (filterable && !filter->match(config)) {
   3166                     continue;
   3167                 }
   3168 
   3169                 const size_t typeStart = data->getSize();
   3170 
   3171                 ResTable_type* tHeader = (ResTable_type*)
   3172                     (((uint8_t*)data->editData(typeStart+typeSize)) + typeStart);
   3173                 if (tHeader == NULL) {
   3174                     fprintf(stderr, "ERROR: out of memory creating ResTable_type\n");
   3175                     return NO_MEMORY;
   3176                 }
   3177 
   3178                 memset(tHeader, 0, sizeof(*tHeader));
   3179                 tHeader->header.type = htods(RES_TABLE_TYPE_TYPE);
   3180                 tHeader->header.headerSize = htods(sizeof(*tHeader));
   3181                 tHeader->id = ti+1;
   3182                 tHeader->entryCount = htodl(N);
   3183                 tHeader->entriesStart = htodl(typeSize);
   3184                 tHeader->config = config;
   3185                 if (kIsDebug) {
   3186                     printf("Writing type %zu config: imsi:%d/%d lang:%c%c cnt:%c%c "
   3187                         "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
   3188                         "sw%ddp w%ddp h%ddp layout:%d\n",
   3189                         ti + 1,
   3190                         tHeader->config.mcc, tHeader->config.mnc,
   3191                         tHeader->config.language[0] ? tHeader->config.language[0] : '-',
   3192                         tHeader->config.language[1] ? tHeader->config.language[1] : '-',
   3193                         tHeader->config.country[0] ? tHeader->config.country[0] : '-',
   3194                         tHeader->config.country[1] ? tHeader->config.country[1] : '-',
   3195                         tHeader->config.orientation,
   3196                         tHeader->config.uiMode,
   3197                         tHeader->config.touchscreen,
   3198                         tHeader->config.density,
   3199                         tHeader->config.keyboard,
   3200                         tHeader->config.inputFlags,
   3201                         tHeader->config.navigation,
   3202                         tHeader->config.screenWidth,
   3203                         tHeader->config.screenHeight,
   3204                         tHeader->config.smallestScreenWidthDp,
   3205                         tHeader->config.screenWidthDp,
   3206                         tHeader->config.screenHeightDp,
   3207                         tHeader->config.screenLayout);
   3208                 }
   3209                 tHeader->config.swapHtoD();
   3210 
   3211                 // Build the entries inside of this type.
   3212                 for (size_t ei=0; ei<N; ei++) {
   3213                     sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
   3214                     sp<Entry> e = NULL;
   3215                     if (cl != NULL) {
   3216                         e = cl->getEntries().valueFor(config);
   3217                     }
   3218 
   3219                     // Set the offset for this entry in its type.
   3220                     uint32_t* index = (uint32_t*)
   3221                         (((uint8_t*)data->editData())
   3222                             + typeStart + sizeof(ResTable_type));
   3223                     if (e != NULL) {
   3224                         index[ei] = htodl(data->getSize()-typeStart-typeSize);
   3225 
   3226                         // Create the entry.
   3227                         ssize_t amt = e->flatten(bundle, data, cl->getPublic());
   3228                         if (amt < 0) {
   3229                             return amt;
   3230                         }
   3231                         validResources.editItemAt(ei) = true;
   3232                     } else {
   3233                         index[ei] = htodl(ResTable_type::NO_ENTRY);
   3234                     }
   3235                 }
   3236 
   3237                 // Fill in the rest of the type information.
   3238                 tHeader = (ResTable_type*)
   3239                     (((uint8_t*)data->editData()) + typeStart);
   3240                 tHeader->header.size = htodl(data->getSize()-typeStart);
   3241             }
   3242 
   3243             // If we're building splits, then each invocation of the flattening
   3244             // step will have 'missing' entries. Don't warn/error for this case.
   3245             if (bundle->getSplitConfigurations().isEmpty()) {
   3246                 bool missing_entry = false;
   3247                 const char* log_prefix = bundle->getErrorOnMissingConfigEntry() ?
   3248                         "error" : "warning";
   3249                 for (size_t i = 0; i < N; ++i) {
   3250                     if (!validResources[i]) {
   3251                         sp<ConfigList> c = t->getOrderedConfigs().itemAt(i);
   3252                         if (c != NULL) {
   3253                             fprintf(stderr, "%s: no entries written for %s/%s (0x%08zx)\n", log_prefix,
   3254                                     String8(typeName).string(), String8(c->getName()).string(),
   3255                                     Res_MAKEID(p->getAssignedId() - 1, ti, i));
   3256                         }
   3257                         missing_entry = true;
   3258                     }
   3259                 }
   3260                 if (bundle->getErrorOnMissingConfigEntry() && missing_entry) {
   3261                     fprintf(stderr, "Error: Missing entries, quit!\n");
   3262                     return NOT_ENOUGH_DATA;
   3263                 }
   3264             }
   3265         }
   3266 
   3267         // Fill in the rest of the package information.
   3268         header = (ResTable_package*)data->editData();
   3269         header->header.size = htodl(data->getSize());
   3270         header->typeStrings = htodl(typeStringsStart);
   3271         header->lastPublicType = htodl(p->getTypeStrings().size());
   3272         header->keyStrings = htodl(keyStringsStart);
   3273         header->lastPublicKey = htodl(p->getKeyStrings().size());
   3274 
   3275         flatPackages.add(data);
   3276     }
   3277 
   3278     // And now write out the final chunks.
   3279     const size_t dataStart = dest->getSize();
   3280 
   3281     {
   3282         // blah
   3283         ResTable_header header;
   3284         memset(&header, 0, sizeof(header));
   3285         header.header.type = htods(RES_TABLE_TYPE);
   3286         header.header.headerSize = htods(sizeof(header));
   3287         header.packageCount = htodl(flatPackages.size());
   3288         status_t err = dest->writeData(&header, sizeof(header));
   3289         if (err != NO_ERROR) {
   3290             fprintf(stderr, "ERROR: out of memory creating ResTable_header\n");
   3291             return err;
   3292         }
   3293     }
   3294 
   3295     ssize_t strStart = dest->getSize();
   3296     status_t err = valueStrings.writeStringBlock(dest);
   3297     if (err != NO_ERROR) {
   3298         return err;
   3299     }
   3300 
   3301     ssize_t amt = (dest->getSize()-strStart);
   3302     strAmt += amt;
   3303     if (kPrintStringMetrics) {
   3304         fprintf(stderr, "**** value strings: %zd\n", SSIZE(amt));
   3305         fprintf(stderr, "**** total strings: %zd\n", SSIZE(strAmt));
   3306     }
   3307 
   3308     for (pi=0; pi<flatPackages.size(); pi++) {
   3309         err = dest->writeData(flatPackages[pi]->getData(),
   3310                               flatPackages[pi]->getSize());
   3311         if (err != NO_ERROR) {
   3312             fprintf(stderr, "ERROR: out of memory creating package chunk for ResTable_header\n");
   3313             return err;
   3314         }
   3315     }
   3316 
   3317     ResTable_header* header = (ResTable_header*)
   3318         (((uint8_t*)dest->getData()) + dataStart);
   3319     header->header.size = htodl(dest->getSize() - dataStart);
   3320 
   3321     if (kPrintStringMetrics) {
   3322         fprintf(stderr, "**** total resource table size: %zu / %zu%% strings\n",
   3323                 dest->getSize(), (size_t)(strAmt*100)/dest->getSize());
   3324     }
   3325 
   3326     return NO_ERROR;
   3327 }
   3328 
   3329 status_t ResourceTable::flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs) {
   3330     // Write out the library table if necessary
   3331     if (libs.size() > 0) {
   3332         if (kIsDebug) {
   3333             fprintf(stderr, "Writing library reference table\n");
   3334         }
   3335 
   3336         const size_t libStart = dest->getSize();
   3337         const size_t count = libs.size();
   3338         ResTable_lib_header* libHeader = (ResTable_lib_header*) dest->editDataInRange(
   3339                 libStart, sizeof(ResTable_lib_header));
   3340 
   3341         memset(libHeader, 0, sizeof(*libHeader));
   3342         libHeader->header.type = htods(RES_TABLE_LIBRARY_TYPE);
   3343         libHeader->header.headerSize = htods(sizeof(*libHeader));
   3344         libHeader->header.size = htodl(sizeof(*libHeader) + (sizeof(ResTable_lib_entry) * count));
   3345         libHeader->count = htodl(count);
   3346 
   3347         // Write the library entries
   3348         for (size_t i = 0; i < count; i++) {
   3349             const size_t entryStart = dest->getSize();
   3350             sp<Package> libPackage = libs[i];
   3351             if (kIsDebug) {
   3352                 fprintf(stderr, "  Entry %s -> 0x%02x\n",
   3353                         String8(libPackage->getName()).string(),
   3354                         (uint8_t)libPackage->getAssignedId());
   3355             }
   3356 
   3357             ResTable_lib_entry* entry = (ResTable_lib_entry*) dest->editDataInRange(
   3358                     entryStart, sizeof(ResTable_lib_entry));
   3359             memset(entry, 0, sizeof(*entry));
   3360             entry->packageId = htodl(libPackage->getAssignedId());
   3361             strcpy16_htod(entry->packageName, libPackage->getName().string());
   3362         }
   3363     }
   3364     return NO_ERROR;
   3365 }
   3366 
   3367 void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp)
   3368 {
   3369     fprintf(fp,
   3370     "<!-- This file contains <public> resource definitions for all\n"
   3371     "     resources that were generated from the source data. -->\n"
   3372     "\n"
   3373     "<resources>\n");
   3374 
   3375     writePublicDefinitions(package, fp, true);
   3376     writePublicDefinitions(package, fp, false);
   3377 
   3378     fprintf(fp,
   3379     "\n"
   3380     "</resources>\n");
   3381 }
   3382 
   3383 void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp, bool pub)
   3384 {
   3385     bool didHeader = false;
   3386 
   3387     sp<Package> pkg = mPackages.valueFor(package);
   3388     if (pkg != NULL) {
   3389         const size_t NT = pkg->getOrderedTypes().size();
   3390         for (size_t i=0; i<NT; i++) {
   3391             sp<Type> t = pkg->getOrderedTypes().itemAt(i);
   3392             if (t == NULL) {
   3393                 continue;
   3394             }
   3395 
   3396             bool didType = false;
   3397 
   3398             const size_t NC = t->getOrderedConfigs().size();
   3399             for (size_t j=0; j<NC; j++) {
   3400                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(j);
   3401                 if (c == NULL) {
   3402                     continue;
   3403                 }
   3404 
   3405                 if (c->getPublic() != pub) {
   3406                     continue;
   3407                 }
   3408 
   3409                 if (!didType) {
   3410                     fprintf(fp, "\n");
   3411                     didType = true;
   3412                 }
   3413                 if (!didHeader) {
   3414                     if (pub) {
   3415                         fprintf(fp,"  <!-- PUBLIC SECTION.  These resources have been declared public.\n");
   3416                         fprintf(fp,"       Changes to these definitions will break binary compatibility. -->\n\n");
   3417                     } else {
   3418                         fprintf(fp,"  <!-- PRIVATE SECTION.  These resources have not been declared public.\n");
   3419                         fprintf(fp,"       You can make them public my moving these lines into a file in res/values. -->\n\n");
   3420                     }
   3421                     didHeader = true;
   3422                 }
   3423                 if (!pub) {
   3424                     const size_t NE = c->getEntries().size();
   3425                     for (size_t k=0; k<NE; k++) {
   3426                         const SourcePos& pos = c->getEntries().valueAt(k)->getPos();
   3427                         if (pos.file != "") {
   3428                             fprintf(fp,"  <!-- Declared at %s:%d -->\n",
   3429                                     pos.file.string(), pos.line);
   3430                         }
   3431                     }
   3432                 }
   3433                 fprintf(fp, "  <public type=\"%s\" name=\"%s\" id=\"0x%08x\" />\n",
   3434                         String8(t->getName()).string(),
   3435                         String8(c->getName()).string(),
   3436                         getResId(pkg, t, c->getEntryIndex()));
   3437             }
   3438         }
   3439     }
   3440 }
   3441 
   3442 ResourceTable::Item::Item(const SourcePos& _sourcePos,
   3443                           bool _isId,
   3444                           const String16& _value,
   3445                           const Vector<StringPool::entry_style_span>* _style,
   3446                           int32_t _format)
   3447     : sourcePos(_sourcePos)
   3448     , isId(_isId)
   3449     , value(_value)
   3450     , format(_format)
   3451     , bagKeyId(0)
   3452     , evaluating(false)
   3453 {
   3454     if (_style) {
   3455         style = *_style;
   3456     }
   3457 }
   3458 
   3459 ResourceTable::Entry::Entry(const Entry& entry)
   3460     : RefBase()
   3461     , mName(entry.mName)
   3462     , mParent(entry.mParent)
   3463     , mType(entry.mType)
   3464     , mItem(entry.mItem)
   3465     , mItemFormat(entry.mItemFormat)
   3466     , mBag(entry.mBag)
   3467     , mNameIndex(entry.mNameIndex)
   3468     , mParentId(entry.mParentId)
   3469     , mPos(entry.mPos) {}
   3470 
   3471 ResourceTable::Entry& ResourceTable::Entry::operator=(const Entry& entry) {
   3472     mName = entry.mName;
   3473     mParent = entry.mParent;
   3474     mType = entry.mType;
   3475     mItem = entry.mItem;
   3476     mItemFormat = entry.mItemFormat;
   3477     mBag = entry.mBag;
   3478     mNameIndex = entry.mNameIndex;
   3479     mParentId = entry.mParentId;
   3480     mPos = entry.mPos;
   3481     return *this;
   3482 }
   3483 
   3484 status_t ResourceTable::Entry::makeItABag(const SourcePos& sourcePos)
   3485 {
   3486     if (mType == TYPE_BAG) {
   3487         return NO_ERROR;
   3488     }
   3489     if (mType == TYPE_UNKNOWN) {
   3490         mType = TYPE_BAG;
   3491         return NO_ERROR;
   3492     }
   3493     sourcePos.error("Resource entry %s is already defined as a single item.\n"
   3494                     "%s:%d: Originally defined here.\n",
   3495                     String8(mName).string(),
   3496                     mItem.sourcePos.file.string(), mItem.sourcePos.line);
   3497     return UNKNOWN_ERROR;
   3498 }
   3499 
   3500 status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos,
   3501                                        const String16& value,
   3502                                        const Vector<StringPool::entry_style_span>* style,
   3503                                        int32_t format,
   3504                                        const bool overwrite)
   3505 {
   3506     Item item(sourcePos, false, value, style);
   3507 
   3508     if (mType == TYPE_BAG) {
   3509         if (mBag.size() == 0) {
   3510             sourcePos.error("Resource entry %s is already defined as a bag.",
   3511                     String8(mName).string());
   3512         } else {
   3513             const Item& item(mBag.valueAt(0));
   3514             sourcePos.error("Resource entry %s is already defined as a bag.\n"
   3515                             "%s:%d: Originally defined here.\n",
   3516                             String8(mName).string(),
   3517                             item.sourcePos.file.string(), item.sourcePos.line);
   3518         }
   3519         return UNKNOWN_ERROR;
   3520     }
   3521     if ( (mType != TYPE_UNKNOWN) && (overwrite == false) ) {
   3522         sourcePos.error("Resource entry %s is already defined.\n"
   3523                         "%s:%d: Originally defined here.\n",
   3524                         String8(mName).string(),
   3525                         mItem.sourcePos.file.string(), mItem.sourcePos.line);
   3526         return UNKNOWN_ERROR;
   3527     }
   3528 
   3529     mType = TYPE_ITEM;
   3530     mItem = item;
   3531     mItemFormat = format;
   3532     return NO_ERROR;
   3533 }
   3534 
   3535 status_t ResourceTable::Entry::addToBag(const SourcePos& sourcePos,
   3536                                         const String16& key, const String16& value,
   3537                                         const Vector<StringPool::entry_style_span>* style,
   3538                                         bool replace, bool isId, int32_t format)
   3539 {
   3540     status_t err = makeItABag(sourcePos);
   3541     if (err != NO_ERROR) {
   3542         return err;
   3543     }
   3544 
   3545     Item item(sourcePos, isId, value, style, format);
   3546 
   3547     // XXX NOTE: there is an error if you try to have a bag with two keys,
   3548     // one an attr and one an id, with the same name.  Not something we
   3549     // currently ever have to worry about.
   3550     ssize_t origKey = mBag.indexOfKey(key);
   3551     if (origKey >= 0) {
   3552         if (!replace) {
   3553             const Item& item(mBag.valueAt(origKey));
   3554             sourcePos.error("Resource entry %s already has bag item %s.\n"
   3555                     "%s:%d: Originally defined here.\n",
   3556                     String8(mName).string(), String8(key).string(),
   3557                     item.sourcePos.file.string(), item.sourcePos.line);
   3558             return UNKNOWN_ERROR;
   3559         }
   3560         //printf("Replacing %s with %s\n",
   3561         //       String8(mBag.valueFor(key).value).string(), String8(value).string());
   3562         mBag.replaceValueFor(key, item);
   3563     }
   3564 
   3565     mBag.add(key, item);
   3566     return NO_ERROR;
   3567 }
   3568 
   3569 status_t ResourceTable::Entry::removeFromBag(const String16& key) {
   3570     if (mType != Entry::TYPE_BAG) {
   3571         return NO_ERROR;
   3572     }
   3573 
   3574     if (mBag.removeItem(key) >= 0) {
   3575         return NO_ERROR;
   3576     }
   3577     return UNKNOWN_ERROR;
   3578 }
   3579 
   3580 status_t ResourceTable::Entry::emptyBag(const SourcePos& sourcePos)
   3581 {
   3582     status_t err = makeItABag(sourcePos);
   3583     if (err != NO_ERROR) {
   3584         return err;
   3585     }
   3586 
   3587     mBag.clear();
   3588     return NO_ERROR;
   3589 }
   3590 
   3591 status_t ResourceTable::Entry::generateAttributes(ResourceTable* table,
   3592                                                   const String16& package)
   3593 {
   3594     const String16 attr16("attr");
   3595     const String16 id16("id");
   3596     const size_t N = mBag.size();
   3597     for (size_t i=0; i<N; i++) {
   3598         const String16& key = mBag.keyAt(i);
   3599         const Item& it = mBag.valueAt(i);
   3600         if (it.isId) {
   3601             if (!table->hasBagOrEntry(key, &id16, &package)) {
   3602                 String16 value("false");
   3603                 if (kIsDebug) {
   3604                     fprintf(stderr, "Generating %s:id/%s\n",
   3605                             String8(package).string(),
   3606                             String8(key).string());
   3607                 }
   3608                 status_t err = table->addEntry(SourcePos(String8("<generated>"), 0), package,
   3609                                                id16, key, value);
   3610                 if (err != NO_ERROR) {
   3611                     return err;
   3612                 }
   3613             }
   3614         } else if (!table->hasBagOrEntry(key, &attr16, &package)) {
   3615 
   3616 #if 1
   3617 //             fprintf(stderr, "ERROR: Bag attribute '%s' has not been defined.\n",
   3618 //                     String8(key).string());
   3619 //             const Item& item(mBag.valueAt(i));
   3620 //             fprintf(stderr, "Referenced from file %s line %d\n",
   3621 //                     item.sourcePos.file.string(), item.sourcePos.line);
   3622 //             return UNKNOWN_ERROR;
   3623 #else
   3624             char numberStr[16];
   3625             sprintf(numberStr, "%d", ResTable_map::TYPE_ANY);
   3626             status_t err = table->addBag(SourcePos("<generated>", 0), package,
   3627                                          attr16, key, String16(""),
   3628                                          String16("^type"),
   3629                                          String16(numberStr), NULL, NULL);
   3630             if (err != NO_ERROR) {
   3631                 return err;
   3632             }
   3633 #endif
   3634         }
   3635     }
   3636     return NO_ERROR;
   3637 }
   3638 
   3639 status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table,
   3640                                                  const String16& /* package */)
   3641 {
   3642     bool hasErrors = false;
   3643 
   3644     if (mType == TYPE_BAG) {
   3645         const char* errorMsg;
   3646         const String16 style16("style");
   3647         const String16 attr16("attr");
   3648         const String16 id16("id");
   3649         mParentId = 0;
   3650         if (mParent.size() > 0) {
   3651             mParentId = table->getResId(mParent, &style16, NULL, &errorMsg);
   3652             if (mParentId == 0) {
   3653                 mPos.error("Error retrieving parent for item: %s '%s'.\n",
   3654                         errorMsg, String8(mParent).string());
   3655                 hasErrors = true;
   3656             }
   3657         }
   3658         const size_t N = mBag.size();
   3659         for (size_t i=0; i<N; i++) {
   3660             const String16& key = mBag.keyAt(i);
   3661             Item& it = mBag.editValueAt(i);
   3662             it.bagKeyId = table->getResId(key,
   3663                     it.isId ? &id16 : &attr16, NULL, &errorMsg);
   3664             //printf("Bag key of %s: #%08x\n", String8(key).string(), it.bagKeyId);
   3665             if (it.bagKeyId == 0) {
   3666                 it.sourcePos.error("Error: %s: %s '%s'.\n", errorMsg,
   3667                         String8(it.isId ? id16 : attr16).string(),
   3668                         String8(key).string());
   3669                 hasErrors = true;
   3670             }
   3671         }
   3672     }
   3673     return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR;
   3674 }
   3675 
   3676 status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table,
   3677         const String8* configTypeName, const ConfigDescription* config)
   3678 {
   3679     if (mType == TYPE_ITEM) {
   3680         Item& it = mItem;
   3681         AccessorCookie ac(it.sourcePos, String8(mName), String8(it.value));
   3682         if (!table->stringToValue(&it.parsedValue, strings,
   3683                                   it.value, false, true, 0,
   3684                                   &it.style, NULL, &ac, mItemFormat,
   3685                                   configTypeName, config)) {
   3686             return UNKNOWN_ERROR;
   3687         }
   3688     } else if (mType == TYPE_BAG) {
   3689         const size_t N = mBag.size();
   3690         for (size_t i=0; i<N; i++) {
   3691             const String16& key = mBag.keyAt(i);
   3692             Item& it = mBag.editValueAt(i);
   3693             AccessorCookie ac(it.sourcePos, String8(key), String8(it.value));
   3694             if (!table->stringToValue(&it.parsedValue, strings,
   3695                                       it.value, false, true, it.bagKeyId,
   3696                                       &it.style, NULL, &ac, it.format,
   3697                                       configTypeName, config)) {
   3698                 return UNKNOWN_ERROR;
   3699             }
   3700         }
   3701     } else {
   3702         mPos.error("Error: entry %s is not a single item or a bag.\n",
   3703                    String8(mName).string());
   3704         return UNKNOWN_ERROR;
   3705     }
   3706     return NO_ERROR;
   3707 }
   3708 
   3709 status_t ResourceTable::Entry::remapStringValue(StringPool* strings)
   3710 {
   3711     if (mType == TYPE_ITEM) {
   3712         Item& it = mItem;
   3713         if (it.parsedValue.dataType == Res_value::TYPE_STRING) {
   3714             it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data);
   3715         }
   3716     } else if (mType == TYPE_BAG) {
   3717         const size_t N = mBag.size();
   3718         for (size_t i=0; i<N; i++) {
   3719             Item& it = mBag.editValueAt(i);
   3720             if (it.parsedValue.dataType == Res_value::TYPE_STRING) {
   3721                 it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data);
   3722             }
   3723         }
   3724     } else {
   3725         mPos.error("Error: entry %s is not a single item or a bag.\n",
   3726                    String8(mName).string());
   3727         return UNKNOWN_ERROR;
   3728     }
   3729     return NO_ERROR;
   3730 }
   3731 
   3732 ssize_t ResourceTable::Entry::flatten(Bundle* /* bundle */, const sp<AaptFile>& data, bool isPublic)
   3733 {
   3734     size_t amt = 0;
   3735     ResTable_entry header;
   3736     memset(&header, 0, sizeof(header));
   3737     header.size = htods(sizeof(header));
   3738     const type ty = mType;
   3739     if (ty == TYPE_BAG) {
   3740         header.flags |= htods(header.FLAG_COMPLEX);
   3741     }
   3742     if (isPublic) {
   3743         header.flags |= htods(header.FLAG_PUBLIC);
   3744     }
   3745     header.key.index = htodl(mNameIndex);
   3746     if (ty != TYPE_BAG) {
   3747         status_t err = data->writeData(&header, sizeof(header));
   3748         if (err != NO_ERROR) {
   3749             fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
   3750             return err;
   3751         }
   3752 
   3753         const Item& it = mItem;
   3754         Res_value par;
   3755         memset(&par, 0, sizeof(par));
   3756         par.size = htods(it.parsedValue.size);
   3757         par.dataType = it.parsedValue.dataType;
   3758         par.res0 = it.parsedValue.res0;
   3759         par.data = htodl(it.parsedValue.data);
   3760         #if 0
   3761         printf("Writing item (%s): type=%d, data=0x%x, res0=0x%x\n",
   3762                String8(mName).string(), it.parsedValue.dataType,
   3763                it.parsedValue.data, par.res0);
   3764         #endif
   3765         err = data->writeData(&par, it.parsedValue.size);
   3766         if (err != NO_ERROR) {
   3767             fprintf(stderr, "ERROR: out of memory creating Res_value\n");
   3768             return err;
   3769         }
   3770         amt += it.parsedValue.size;
   3771     } else {
   3772         size_t N = mBag.size();
   3773         size_t i;
   3774         // Create correct ordering of items.
   3775         KeyedVector<uint32_t, const Item*> items;
   3776         for (i=0; i<N; i++) {
   3777             const Item& it = mBag.valueAt(i);
   3778             items.add(it.bagKeyId, &it);
   3779         }
   3780         N = items.size();
   3781 
   3782         ResTable_map_entry mapHeader;
   3783         memcpy(&mapHeader, &header, sizeof(header));
   3784         mapHeader.size = htods(sizeof(mapHeader));
   3785         mapHeader.parent.ident = htodl(mParentId);
   3786         mapHeader.count = htodl(N);
   3787         status_t err = data->writeData(&mapHeader, sizeof(mapHeader));
   3788         if (err != NO_ERROR) {
   3789             fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
   3790             return err;
   3791         }
   3792 
   3793         for (i=0; i<N; i++) {
   3794             const Item& it = *items.valueAt(i);
   3795             ResTable_map map;
   3796             map.name.ident = htodl(it.bagKeyId);
   3797             map.value.size = htods(it.parsedValue.size);
   3798             map.value.dataType = it.parsedValue.dataType;
   3799             map.value.res0 = it.parsedValue.res0;
   3800             map.value.data = htodl(it.parsedValue.data);
   3801             err = data->writeData(&map, sizeof(map));
   3802             if (err != NO_ERROR) {
   3803                 fprintf(stderr, "ERROR: out of memory creating Res_value\n");
   3804                 return err;
   3805             }
   3806             amt += sizeof(map);
   3807         }
   3808     }
   3809     return amt;
   3810 }
   3811 
   3812 void ResourceTable::ConfigList::appendComment(const String16& comment,
   3813                                               bool onlyIfEmpty)
   3814 {
   3815     if (comment.size() <= 0) {
   3816         return;
   3817     }
   3818     if (onlyIfEmpty && mComment.size() > 0) {
   3819         return;
   3820     }
   3821     if (mComment.size() > 0) {
   3822         mComment.append(String16("\n"));
   3823     }
   3824     mComment.append(comment);
   3825 }
   3826 
   3827 void ResourceTable::ConfigList::appendTypeComment(const String16& comment)
   3828 {
   3829     if (comment.size() <= 0) {
   3830         return;
   3831     }
   3832     if (mTypeComment.size() > 0) {
   3833         mTypeComment.append(String16("\n"));
   3834     }
   3835     mTypeComment.append(comment);
   3836 }
   3837 
   3838 status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos,
   3839                                         const String16& name,
   3840                                         const uint32_t ident)
   3841 {
   3842     #if 0
   3843     int32_t entryIdx = Res_GETENTRY(ident);
   3844     if (entryIdx < 0) {
   3845         sourcePos.error("Public resource %s/%s has an invalid 0 identifier (0x%08x).\n",
   3846                 String8(mName).string(), String8(name).string(), ident);
   3847         return UNKNOWN_ERROR;
   3848     }
   3849     #endif
   3850 
   3851     int32_t typeIdx = Res_GETTYPE(ident);
   3852     if (typeIdx >= 0) {
   3853         typeIdx++;
   3854         if (mPublicIndex > 0 && mPublicIndex != typeIdx) {
   3855             sourcePos.error("Public resource %s/%s has conflicting type codes for its"
   3856                     " public identifiers (0x%x vs 0x%x).\n",
   3857                     String8(mName).string(), String8(name).string(),
   3858                     mPublicIndex, typeIdx);
   3859             return UNKNOWN_ERROR;
   3860         }
   3861         mPublicIndex = typeIdx;
   3862     }
   3863 
   3864     if (mFirstPublicSourcePos == NULL) {
   3865         mFirstPublicSourcePos = new SourcePos(sourcePos);
   3866     }
   3867 
   3868     if (mPublic.indexOfKey(name) < 0) {
   3869         mPublic.add(name, Public(sourcePos, String16(), ident));
   3870     } else {
   3871         Public& p = mPublic.editValueFor(name);
   3872         if (p.ident != ident) {
   3873             sourcePos.error("Public resource %s/%s has conflicting public identifiers"
   3874                     " (0x%08x vs 0x%08x).\n"
   3875                     "%s:%d: Originally defined here.\n",
   3876                     String8(mName).string(), String8(name).string(), p.ident, ident,
   3877                     p.sourcePos.file.string(), p.sourcePos.line);
   3878             return UNKNOWN_ERROR;
   3879         }
   3880     }
   3881 
   3882     return NO_ERROR;
   3883 }
   3884 
   3885 void ResourceTable::Type::canAddEntry(const String16& name)
   3886 {
   3887     mCanAddEntries.add(name);
   3888 }
   3889 
   3890 sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
   3891                                                        const SourcePos& sourcePos,
   3892                                                        const ResTable_config* config,
   3893                                                        bool doSetIndex,
   3894                                                        bool overlay,
   3895                                                        bool autoAddOverlay)
   3896 {
   3897     int pos = -1;
   3898     sp<ConfigList> c = mConfigs.valueFor(entry);
   3899     if (c == NULL) {
   3900         if (overlay && !autoAddOverlay && mCanAddEntries.indexOf(entry) < 0) {
   3901             sourcePos.error("Resource at %s appears in overlay but not"
   3902                             " in the base package; use <add-resource> to add.\n",
   3903                             String8(entry).string());
   3904             return NULL;
   3905         }
   3906         c = new ConfigList(entry, sourcePos);
   3907         mConfigs.add(entry, c);
   3908         pos = (int)mOrderedConfigs.size();
   3909         mOrderedConfigs.add(c);
   3910         if (doSetIndex) {
   3911             c->setEntryIndex(pos);
   3912         }
   3913     }
   3914 
   3915     ConfigDescription cdesc;
   3916     if (config) cdesc = *config;
   3917 
   3918     sp<Entry> e = c->getEntries().valueFor(cdesc);
   3919     if (e == NULL) {
   3920         if (kIsDebug) {
   3921             if (config != NULL) {
   3922                 printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
   3923                     "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
   3924                     "sw%ddp w%ddp h%ddp layout:%d\n",
   3925                       sourcePos.file.string(), sourcePos.line,
   3926                       config->mcc, config->mnc,
   3927                       config->language[0] ? config->language[0] : '-',
   3928                       config->language[1] ? config->language[1] : '-',
   3929                       config->country[0] ? config->country[0] : '-',
   3930                       config->country[1] ? config->country[1] : '-',
   3931                       config->orientation,
   3932                       config->touchscreen,
   3933                       config->density,
   3934                       config->keyboard,
   3935                       config->inputFlags,
   3936                       config->navigation,
   3937                       config->screenWidth,
   3938                       config->screenHeight,
   3939                       config->smallestScreenWidthDp,
   3940                       config->screenWidthDp,
   3941                       config->screenHeightDp,
   3942                       config->screenLayout);
   3943             } else {
   3944                 printf("New entry at %s:%d: NULL config\n",
   3945                         sourcePos.file.string(), sourcePos.line);
   3946             }
   3947         }
   3948         e = new Entry(entry, sourcePos);
   3949         c->addEntry(cdesc, e);
   3950         /*
   3951         if (doSetIndex) {
   3952             if (pos < 0) {
   3953                 for (pos=0; pos<(int)mOrderedConfigs.size(); pos++) {
   3954                     if (mOrderedConfigs[pos] == c) {
   3955                         break;
   3956                     }
   3957                 }
   3958                 if (pos >= (int)mOrderedConfigs.size()) {
   3959                     sourcePos.error("Internal error: config not found in mOrderedConfigs when adding entry");
   3960                     return NULL;
   3961                 }
   3962             }
   3963             e->setEntryIndex(pos);
   3964         }
   3965         */
   3966     }
   3967 
   3968     return e;
   3969 }
   3970 
   3971 sp<ResourceTable::ConfigList> ResourceTable::Type::removeEntry(const String16& entry) {
   3972     ssize_t idx = mConfigs.indexOfKey(entry);
   3973     if (idx < 0) {
   3974         return NULL;
   3975     }
   3976 
   3977     sp<ConfigList> removed = mConfigs.valueAt(idx);
   3978     mConfigs.removeItemsAt(idx);
   3979 
   3980     Vector<sp<ConfigList> >::iterator iter = std::find(
   3981             mOrderedConfigs.begin(), mOrderedConfigs.end(), removed);
   3982     if (iter != mOrderedConfigs.end()) {
   3983         mOrderedConfigs.erase(iter);
   3984     }
   3985 
   3986     mPublic.removeItem(entry);
   3987     return removed;
   3988 }
   3989 
   3990 SortedVector<ConfigDescription> ResourceTable::Type::getUniqueConfigs() const {
   3991     SortedVector<ConfigDescription> unique;
   3992     const size_t entryCount = mOrderedConfigs.size();
   3993     for (size_t i = 0; i < entryCount; i++) {
   3994         if (mOrderedConfigs[i] == NULL) {
   3995             continue;
   3996         }
   3997         const DefaultKeyedVector<ConfigDescription, sp<Entry> >& configs =
   3998                 mOrderedConfigs[i]->getEntries();
   3999         const size_t configCount = configs.size();
   4000         for (size_t j = 0; j < configCount; j++) {
   4001             unique.add(configs.keyAt(j));
   4002         }
   4003     }
   4004     return unique;
   4005 }
   4006 
   4007 status_t ResourceTable::Type::applyPublicEntryOrder()
   4008 {
   4009     size_t N = mOrderedConfigs.size();
   4010     Vector<sp<ConfigList> > origOrder(mOrderedConfigs);
   4011     bool hasError = false;
   4012 
   4013     size_t i;
   4014     for (i=0; i<N; i++) {
   4015         mOrderedConfigs.replaceAt(NULL, i);
   4016     }
   4017 
   4018     const size_t NP = mPublic.size();
   4019     //printf("Ordering %d configs from %d public defs\n", N, NP);
   4020     size_t j;
   4021     for (j=0; j<NP; j++) {
   4022         const String16& name = mPublic.keyAt(j);
   4023         const Public& p = mPublic.valueAt(j);
   4024         int32_t idx = Res_GETENTRY(p.ident);
   4025         //printf("Looking for entry \"%s\"/\"%s\" (0x%08x) in %d...\n",
   4026         //       String8(mName).string(), String8(name).string(), p.ident, N);
   4027         bool found = false;
   4028         for (i=0; i<N; i++) {
   4029             sp<ConfigList> e = origOrder.itemAt(i);
   4030             //printf("#%d: \"%s\"\n", i, String8(e->getName()).string());
   4031             if (e->getName() == name) {
   4032                 if (idx >= (int32_t)mOrderedConfigs.size()) {
   4033                     mOrderedConfigs.resize(idx + 1);
   4034                 }
   4035 
   4036                 if (mOrderedConfigs.itemAt(idx) == NULL) {
   4037                     e->setPublic(true);
   4038                     e->setPublicSourcePos(p.sourcePos);
   4039                     mOrderedConfigs.replaceAt(e, idx);
   4040                     origOrder.removeAt(i);
   4041                     N--;
   4042                     found = true;
   4043                     break;
   4044                 } else {
   4045                     sp<ConfigList> oe = mOrderedConfigs.itemAt(idx);
   4046 
   4047                     p.sourcePos.error("Multiple entry names declared for public entry"
   4048                             " identifier 0x%x in type %s (%s vs %s).\n"
   4049                             "%s:%d: Originally defined here.",
   4050                             idx+1, String8(mName).string(),
   4051                             String8(oe->getName()).string(),
   4052                             String8(name).string(),
   4053                             oe->getPublicSourcePos().file.string(),
   4054                             oe->getPublicSourcePos().line);
   4055                     hasError = true;
   4056                 }
   4057             }
   4058         }
   4059 
   4060         if (!found) {
   4061             p.sourcePos.error("Public symbol %s/%s declared here is not defined.",
   4062                     String8(mName).string(), String8(name).string());
   4063             hasError = true;
   4064         }
   4065     }
   4066 
   4067     //printf("Copying back in %d non-public configs, have %d\n", N, origOrder.size());
   4068 
   4069     if (N != origOrder.size()) {
   4070         printf("Internal error: remaining private symbol count mismatch\n");
   4071         N = origOrder.size();
   4072     }
   4073 
   4074     j = 0;
   4075     for (i=0; i<N; i++) {
   4076         sp<ConfigList> e = origOrder.itemAt(i);
   4077         // There will always be enough room for the remaining entries.
   4078         while (mOrderedConfigs.itemAt(j) != NULL) {
   4079             j++;
   4080         }
   4081         mOrderedConfigs.replaceAt(e, j);
   4082         j++;
   4083     }
   4084 
   4085     return hasError ? STATUST(UNKNOWN_ERROR) : NO_ERROR;
   4086 }
   4087 
   4088 ResourceTable::Package::Package(const String16& name, size_t packageId)
   4089     : mName(name), mPackageId(packageId),
   4090       mTypeStringsMapping(0xffffffff),
   4091       mKeyStringsMapping(0xffffffff)
   4092 {
   4093 }
   4094 
   4095 sp<ResourceTable::Type> ResourceTable::Package::getType(const String16& type,
   4096                                                         const SourcePos& sourcePos,
   4097                                                         bool doSetIndex)
   4098 {
   4099     sp<Type> t = mTypes.valueFor(type);
   4100     if (t == NULL) {
   4101         t = new Type(type, sourcePos);
   4102         mTypes.add(type, t);
   4103         mOrderedTypes.add(t);
   4104         if (doSetIndex) {
   4105             // For some reason the type's index is set to one plus the index
   4106             // in the mOrderedTypes list, rather than just the index.
   4107             t->setIndex(mOrderedTypes.size());
   4108         }
   4109     }
   4110     return t;
   4111 }
   4112 
   4113 status_t ResourceTable::Package::setTypeStrings(const sp<AaptFile>& data)
   4114 {
   4115     status_t err = setStrings(data, &mTypeStrings, &mTypeStringsMapping);
   4116     if (err != NO_ERROR) {
   4117         fprintf(stderr, "ERROR: Type string data is corrupt!\n");
   4118         return err;
   4119     }
   4120 
   4121     // Retain a reference to the new data after we've successfully replaced
   4122     // all uses of the old reference (in setStrings() ).
   4123     mTypeStringsData = data;
   4124     return NO_ERROR;
   4125 }
   4126 
   4127 status_t ResourceTable::Package::setKeyStrings(const sp<AaptFile>& data)
   4128 {
   4129     status_t err = setStrings(data, &mKeyStrings, &mKeyStringsMapping);
   4130     if (err != NO_ERROR) {
   4131         fprintf(stderr, "ERROR: Key string data is corrupt!\n");
   4132         return err;
   4133     }
   4134 
   4135     // Retain a reference to the new data after we've successfully replaced
   4136     // all uses of the old reference (in setStrings() ).
   4137     mKeyStringsData = data;
   4138     return NO_ERROR;
   4139 }
   4140 
   4141 status_t ResourceTable::Package::setStrings(const sp<AaptFile>& data,
   4142                                             ResStringPool* strings,
   4143                                             DefaultKeyedVector<String16, uint32_t>* mappings)
   4144 {
   4145     if (data->getData() == NULL) {
   4146         return UNKNOWN_ERROR;
   4147     }
   4148 
   4149     status_t err = strings->setTo(data->getData(), data->getSize());
   4150     if (err == NO_ERROR) {
   4151         const size_t N = strings->size();
   4152         for (size_t i=0; i<N; i++) {
   4153             size_t len;
   4154             mappings->add(String16(strings->stringAt(i, &len)), i);
   4155         }
   4156     }
   4157     return err;
   4158 }
   4159 
   4160 status_t ResourceTable::Package::applyPublicTypeOrder()
   4161 {
   4162     size_t N = mOrderedTypes.size();
   4163     Vector<sp<Type> > origOrder(mOrderedTypes);
   4164 
   4165     size_t i;
   4166     for (i=0; i<N; i++) {
   4167         mOrderedTypes.replaceAt(NULL, i);
   4168     }
   4169 
   4170     for (i=0; i<N; i++) {
   4171         sp<Type> t = origOrder.itemAt(i);
   4172         int32_t idx = t->getPublicIndex();
   4173         if (idx > 0) {
   4174             idx--;
   4175             while (idx >= (int32_t)mOrderedTypes.size()) {
   4176                 mOrderedTypes.add();
   4177             }
   4178             if (mOrderedTypes.itemAt(idx) != NULL) {
   4179                 sp<Type> ot = mOrderedTypes.itemAt(idx);
   4180                 t->getFirstPublicSourcePos().error("Multiple type names declared for public type"
   4181                         " identifier 0x%x (%s vs %s).\n"
   4182                         "%s:%d: Originally defined here.",
   4183                         idx, String8(ot->getName()).string(),
   4184                         String8(t->getName()).string(),
   4185                         ot->getFirstPublicSourcePos().file.string(),
   4186                         ot->getFirstPublicSourcePos().line);
   4187                 return UNKNOWN_ERROR;
   4188             }
   4189             mOrderedTypes.replaceAt(t, idx);
   4190             origOrder.removeAt(i);
   4191             i--;
   4192             N--;
   4193         }
   4194     }
   4195 
   4196     size_t j=0;
   4197     for (i=0; i<N; i++) {
   4198         sp<Type> t = origOrder.itemAt(i);
   4199         // There will always be enough room for the remaining types.
   4200         while (mOrderedTypes.itemAt(j) != NULL) {
   4201             j++;
   4202         }
   4203         mOrderedTypes.replaceAt(t, j);
   4204     }
   4205 
   4206     return NO_ERROR;
   4207 }
   4208 
   4209 void ResourceTable::Package::movePrivateAttrs() {
   4210     sp<Type> attr = mTypes.valueFor(String16("attr"));
   4211     if (attr == NULL) {
   4212         // Nothing to do.
   4213         return;
   4214     }
   4215 
   4216     Vector<sp<ConfigList> > privateAttrs;
   4217 
   4218     bool hasPublic = false;
   4219     const Vector<sp<ConfigList> >& configs = attr->getOrderedConfigs();
   4220     const size_t configCount = configs.size();
   4221     for (size_t i = 0; i < configCount; i++) {
   4222         if (configs[i] == NULL) {
   4223             continue;
   4224         }
   4225 
   4226         if (attr->isPublic(configs[i]->getName())) {
   4227             hasPublic = true;
   4228         } else {
   4229             privateAttrs.add(configs[i]);
   4230         }
   4231     }
   4232 
   4233     // Only if we have public attributes do we create a separate type for
   4234     // private attributes.
   4235     if (!hasPublic) {
   4236         return;
   4237     }
   4238 
   4239     // Create a new type for private attributes.
   4240     sp<Type> privateAttrType = getType(String16(kAttrPrivateType), SourcePos());
   4241 
   4242     const size_t privateAttrCount = privateAttrs.size();
   4243     for (size_t i = 0; i < privateAttrCount; i++) {
   4244         const sp<ConfigList>& cl = privateAttrs[i];
   4245 
   4246         // Remove the private attributes from their current type.
   4247         attr->removeEntry(cl->getName());
   4248 
   4249         // Add it to the new type.
   4250         const DefaultKeyedVector<ConfigDescription, sp<Entry> >& entries = cl->getEntries();
   4251         const size_t entryCount = entries.size();
   4252         for (size_t j = 0; j < entryCount; j++) {
   4253             const sp<Entry>& oldEntry = entries[j];
   4254             sp<Entry> entry = privateAttrType->getEntry(
   4255                     cl->getName(), oldEntry->getPos(), &entries.keyAt(j));
   4256             *entry = *oldEntry;
   4257         }
   4258 
   4259         // Move the symbols to the new type.
   4260 
   4261     }
   4262 }
   4263 
   4264 sp<ResourceTable::Package> ResourceTable::getPackage(const String16& package)
   4265 {
   4266     if (package != mAssetsPackage) {
   4267         return NULL;
   4268     }
   4269     return mPackages.valueFor(package);
   4270 }
   4271 
   4272 sp<ResourceTable::Type> ResourceTable::getType(const String16& package,
   4273                                                const String16& type,
   4274                                                const SourcePos& sourcePos,
   4275                                                bool doSetIndex)
   4276 {
   4277     sp<Package> p = getPackage(package);
   4278     if (p == NULL) {
   4279         return NULL;
   4280     }
   4281     return p->getType(type, sourcePos, doSetIndex);
   4282 }
   4283 
   4284 sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package,
   4285                                                  const String16& type,
   4286                                                  const String16& name,
   4287                                                  const SourcePos& sourcePos,
   4288                                                  bool overlay,
   4289                                                  const ResTable_config* config,
   4290                                                  bool doSetIndex)
   4291 {
   4292     sp<Type> t = getType(package, type, sourcePos, doSetIndex);
   4293     if (t == NULL) {
   4294         return NULL;
   4295     }
   4296     return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay());
   4297 }
   4298 
   4299 sp<ResourceTable::ConfigList> ResourceTable::getConfigList(const String16& package,
   4300         const String16& type, const String16& name) const
   4301 {
   4302     const size_t packageCount = mOrderedPackages.size();
   4303     for (size_t pi = 0; pi < packageCount; pi++) {
   4304         const sp<Package>& p = mOrderedPackages[pi];
   4305         if (p == NULL || p->getName() != package) {
   4306             continue;
   4307         }
   4308 
   4309         const Vector<sp<Type> >& types = p->getOrderedTypes();
   4310         const size_t typeCount = types.size();
   4311         for (size_t ti = 0; ti < typeCount; ti++) {
   4312             const sp<Type>& t = types[ti];
   4313             if (t == NULL || t->getName() != type) {
   4314                 continue;
   4315             }
   4316 
   4317             const Vector<sp<ConfigList> >& configs = t->getOrderedConfigs();
   4318             const size_t configCount = configs.size();
   4319             for (size_t ci = 0; ci < configCount; ci++) {
   4320                 const sp<ConfigList>& cl = configs[ci];
   4321                 if (cl == NULL || cl->getName() != name) {
   4322                     continue;
   4323                 }
   4324 
   4325                 return cl;
   4326             }
   4327         }
   4328     }
   4329     return NULL;
   4330 }
   4331 
   4332 sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
   4333                                                        const ResTable_config* config) const
   4334 {
   4335     size_t pid = Res_GETPACKAGE(resID)+1;
   4336     const size_t N = mOrderedPackages.size();
   4337     sp<Package> p;
   4338     for (size_t i = 0; i < N; i++) {
   4339         sp<Package> check = mOrderedPackages[i];
   4340         if (check->getAssignedId() == pid) {
   4341             p = check;
   4342             break;
   4343         }
   4344 
   4345     }
   4346     if (p == NULL) {
   4347         fprintf(stderr, "warning: Package not found for resource #%08x\n", resID);
   4348         return NULL;
   4349     }
   4350 
   4351     int tid = Res_GETTYPE(resID);
   4352     if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) {
   4353         fprintf(stderr, "warning: Type not found for resource #%08x\n", resID);
   4354         return NULL;
   4355     }
   4356     sp<Type> t = p->getOrderedTypes()[tid];
   4357 
   4358     int eid = Res_GETENTRY(resID);
   4359     if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) {
   4360         fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
   4361         return NULL;
   4362     }
   4363 
   4364     sp<ConfigList> c = t->getOrderedConfigs()[eid];
   4365     if (c == NULL) {
   4366         fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
   4367         return NULL;
   4368     }
   4369 
   4370     ConfigDescription cdesc;
   4371     if (config) cdesc = *config;
   4372     sp<Entry> e = c->getEntries().valueFor(cdesc);
   4373     if (c == NULL) {
   4374         fprintf(stderr, "warning: Entry configuration not found for resource #%08x\n", resID);
   4375         return NULL;
   4376     }
   4377 
   4378     return e;
   4379 }
   4380 
   4381 const ResourceTable::Item* ResourceTable::getItem(uint32_t resID, uint32_t attrID) const
   4382 {
   4383     sp<const Entry> e = getEntry(resID);
   4384     if (e == NULL) {
   4385         return NULL;
   4386     }
   4387 
   4388     const size_t N = e->getBag().size();
   4389     for (size_t i=0; i<N; i++) {
   4390         const Item& it = e->getBag().valueAt(i);
   4391         if (it.bagKeyId == 0) {
   4392             fprintf(stderr, "warning: ID not yet assigned to '%s' in bag '%s'\n",
   4393                     String8(e->getName()).string(),
   4394                     String8(e->getBag().keyAt(i)).string());
   4395         }
   4396         if (it.bagKeyId == attrID) {
   4397             return &it;
   4398         }
   4399     }
   4400 
   4401     return NULL;
   4402 }
   4403 
   4404 bool ResourceTable::getItemValue(
   4405     uint32_t resID, uint32_t attrID, Res_value* outValue)
   4406 {
   4407     const Item* item = getItem(resID, attrID);
   4408 
   4409     bool res = false;
   4410     if (item != NULL) {
   4411         if (item->evaluating) {
   4412             sp<const Entry> e = getEntry(resID);
   4413             const size_t N = e->getBag().size();
   4414             size_t i;
   4415             for (i=0; i<N; i++) {
   4416                 if (&e->getBag().valueAt(i) == item) {
   4417                     break;
   4418                 }
   4419             }
   4420             fprintf(stderr, "warning: Circular reference detected in key '%s' of bag '%s'\n",
   4421                     String8(e->getName()).string(),
   4422                     String8(e->getBag().keyAt(i)).string());
   4423             return false;
   4424         }
   4425         item->evaluating = true;
   4426         res = stringToValue(outValue, NULL, item->value, false, false, item->bagKeyId);
   4427         if (kIsDebug) {
   4428             if (res) {
   4429                 printf("getItemValue of #%08x[#%08x] (%s): type=#%08x, data=#%08x\n",
   4430                        resID, attrID, String8(getEntry(resID)->getName()).string(),
   4431                        outValue->dataType, outValue->data);
   4432             } else {
   4433                 printf("getItemValue of #%08x[#%08x]: failed\n",
   4434                        resID, attrID);
   4435             }
   4436         }
   4437         item->evaluating = false;
   4438     }
   4439     return res;
   4440 }
   4441 
   4442 /**
   4443  * Returns the SDK version at which the attribute was
   4444  * made public, or -1 if the resource ID is not an attribute
   4445  * or is not public.
   4446  */
   4447 int ResourceTable::getPublicAttributeSdkLevel(uint32_t attrId) const {
   4448     if (Res_GETPACKAGE(attrId) + 1 != 0x01 || Res_GETTYPE(attrId) + 1 != 0x01) {
   4449         return -1;
   4450     }
   4451 
   4452     uint32_t specFlags;
   4453     if (!mAssets->getIncludedResources().getResourceFlags(attrId, &specFlags)) {
   4454         return -1;
   4455     }
   4456 
   4457     if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) {
   4458         return -1;
   4459     }
   4460 
   4461     const size_t entryId = Res_GETENTRY(attrId);
   4462     if (entryId <= 0x021c) {
   4463         return 1;
   4464     } else if (entryId <= 0x021d) {
   4465         return 2;
   4466     } else if (entryId <= 0x0269) {
   4467         return SDK_CUPCAKE;
   4468     } else if (entryId <= 0x028d) {
   4469         return SDK_DONUT;
   4470     } else if (entryId <= 0x02ad) {
   4471         return SDK_ECLAIR;
   4472     } else if (entryId <= 0x02b3) {
   4473         return SDK_ECLAIR_0_1;
   4474     } else if (entryId <= 0x02b5) {
   4475         return SDK_ECLAIR_MR1;
   4476     } else if (entryId <= 0x02bd) {
   4477         return SDK_FROYO;
   4478     } else if (entryId <= 0x02cb) {
   4479         return SDK_GINGERBREAD;
   4480     } else if (entryId <= 0x0361) {
   4481         return SDK_HONEYCOMB;
   4482     } else if (entryId <= 0x0366) {
   4483         return SDK_HONEYCOMB_MR1;
   4484     } else if (entryId <= 0x03a6) {
   4485         return SDK_HONEYCOMB_MR2;
   4486     } else if (entryId <= 0x03ae) {
   4487         return SDK_JELLY_BEAN;
   4488     } else if (entryId <= 0x03cc) {
   4489         return SDK_JELLY_BEAN_MR1;
   4490     } else if (entryId <= 0x03da) {
   4491         return SDK_JELLY_BEAN_MR2;
   4492     } else if (entryId <= 0x03f1) {
   4493         return SDK_KITKAT;
   4494     } else if (entryId <= 0x03f6) {
   4495         return SDK_KITKAT_WATCH;
   4496     } else if (entryId <= 0x04ce) {
   4497         return SDK_LOLLIPOP;
   4498     } else {
   4499         // Anything else is marked as defined in
   4500         // SDK_LOLLIPOP_MR1 since after this
   4501         // version no attribute compat work
   4502         // needs to be done.
   4503         return SDK_LOLLIPOP_MR1;
   4504     }
   4505 }
   4506 
   4507 /**
   4508  * First check the Manifest, then check the command line flag.
   4509  */
   4510 static int getMinSdkVersion(const Bundle* bundle) {
   4511     if (bundle->getManifestMinSdkVersion() != NULL && strlen(bundle->getManifestMinSdkVersion()) > 0) {
   4512         return atoi(bundle->getManifestMinSdkVersion());
   4513     } else if (bundle->getMinSdkVersion() != NULL && strlen(bundle->getMinSdkVersion()) > 0) {
   4514         return atoi(bundle->getMinSdkVersion());
   4515     }
   4516     return 0;
   4517 }
   4518 
   4519 bool ResourceTable::shouldGenerateVersionedResource(
   4520         const sp<ResourceTable::ConfigList>& configList,
   4521         const ConfigDescription& sourceConfig,
   4522         const int sdkVersionToGenerate) {
   4523     assert(sdkVersionToGenerate > sourceConfig.sdkVersion);
   4524     const DefaultKeyedVector<ConfigDescription, sp<ResourceTable::Entry>>& entries
   4525             = configList->getEntries();
   4526     ssize_t idx = entries.indexOfKey(sourceConfig);
   4527 
   4528     // The source config came from this list, so it should be here.
   4529     assert(idx >= 0);
   4530 
   4531     // The next configuration either only varies in sdkVersion, or it is completely different
   4532     // and therefore incompatible. If it is incompatible, we must generate the versioned resource.
   4533 
   4534     // NOTE: The ordering of configurations takes sdkVersion as higher precedence than other
   4535     // qualifiers, so we need to iterate through the entire list to be sure there
   4536     // are no higher sdk level versions of this resource.
   4537     ConfigDescription tempConfig(sourceConfig);
   4538     for (size_t i = static_cast<size_t>(idx) + 1; i < entries.size(); i++) {
   4539         const ConfigDescription& nextConfig = entries.keyAt(i);
   4540         tempConfig.sdkVersion = nextConfig.sdkVersion;
   4541         if (tempConfig == nextConfig) {
   4542             // The two configs are the same, check the sdk version.
   4543             return sdkVersionToGenerate < nextConfig.sdkVersion;
   4544         }
   4545     }
   4546 
   4547     // No match was found, so we should generate the versioned resource.
   4548     return true;
   4549 }
   4550 
   4551 /**
   4552  * Modifies the entries in the resource table to account for compatibility
   4553  * issues with older versions of Android.
   4554  *
   4555  * This primarily handles the issue of private/public attribute clashes
   4556  * in framework resources.
   4557  *
   4558  * AAPT has traditionally assigned resource IDs to public attributes,
   4559  * and then followed those public definitions with private attributes.
   4560  *
   4561  * --- PUBLIC ---
   4562  * | 0x01010234 | attr/color
   4563  * | 0x01010235 | attr/background
   4564  *
   4565  * --- PRIVATE ---
   4566  * | 0x01010236 | attr/secret
   4567  * | 0x01010237 | attr/shhh
   4568  *
   4569  * Each release, when attributes are added, they take the place of the private
   4570  * attributes and the private attributes are shifted down again.
   4571  *
   4572  * --- PUBLIC ---
   4573  * | 0x01010234 | attr/color
   4574  * | 0x01010235 | attr/background
   4575  * | 0x01010236 | attr/shinyNewAttr
   4576  * | 0x01010237 | attr/highlyValuedFeature
   4577  *
   4578  * --- PRIVATE ---
   4579  * | 0x01010238 | attr/secret
   4580  * | 0x01010239 | attr/shhh
   4581  *
   4582  * Platform code may look for private attributes set in a theme. If an app
   4583  * compiled against a newer version of the platform uses a new public
   4584  * attribute that happens to have the same ID as the private attribute
   4585  * the older platform is expecting, then the behavior is undefined.
   4586  *
   4587  * We get around this by detecting any newly defined attributes (in L),
   4588  * copy the resource into a -v21 qualified resource, and delete the
   4589  * attribute from the original resource. This ensures that older platforms
   4590  * don't see the new attribute, but when running on L+ platforms, the
   4591  * attribute will be respected.
   4592  */
   4593 status_t ResourceTable::modifyForCompat(const Bundle* bundle) {
   4594     const int minSdk = getMinSdkVersion(bundle);
   4595     if (minSdk >= SDK_LOLLIPOP_MR1) {
   4596         // Lollipop MR1 and up handles public attributes differently, no
   4597         // need to do any compat modifications.
   4598         return NO_ERROR;
   4599     }
   4600 
   4601     const String16 attr16("attr");
   4602 
   4603     const size_t packageCount = mOrderedPackages.size();
   4604     for (size_t pi = 0; pi < packageCount; pi++) {
   4605         sp<Package> p = mOrderedPackages.itemAt(pi);
   4606         if (p == NULL || p->getTypes().size() == 0) {
   4607             // Empty, skip!
   4608             continue;
   4609         }
   4610 
   4611         const size_t typeCount = p->getOrderedTypes().size();
   4612         for (size_t ti = 0; ti < typeCount; ti++) {
   4613             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   4614             if (t == NULL) {
   4615                 continue;
   4616             }
   4617 
   4618             const size_t configCount = t->getOrderedConfigs().size();
   4619             for (size_t ci = 0; ci < configCount; ci++) {
   4620                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
   4621                 if (c == NULL) {
   4622                     continue;
   4623                 }
   4624 
   4625                 Vector<key_value_pair_t<ConfigDescription, sp<Entry> > > entriesToAdd;
   4626                 const DefaultKeyedVector<ConfigDescription, sp<Entry> >& entries =
   4627                         c->getEntries();
   4628                 const size_t entryCount = entries.size();
   4629                 for (size_t ei = 0; ei < entryCount; ei++) {
   4630                     sp<Entry> e = entries.valueAt(ei);
   4631                     if (e == NULL || e->getType() != Entry::TYPE_BAG) {
   4632                         continue;
   4633                     }
   4634 
   4635                     const ConfigDescription& config = entries.keyAt(ei);
   4636                     if (config.sdkVersion >= SDK_LOLLIPOP_MR1) {
   4637                         continue;
   4638                     }
   4639 
   4640                     KeyedVector<int, Vector<String16> > attributesToRemove;
   4641                     const KeyedVector<String16, Item>& bag = e->getBag();
   4642                     const size_t bagCount = bag.size();
   4643                     for (size_t bi = 0; bi < bagCount; bi++) {
   4644                         const uint32_t attrId = getResId(bag.keyAt(bi), &attr16);
   4645                         const int sdkLevel = getPublicAttributeSdkLevel(attrId);
   4646                         if (sdkLevel > 1 && sdkLevel > config.sdkVersion && sdkLevel > minSdk) {
   4647                             AaptUtil::appendValue(attributesToRemove, sdkLevel, bag.keyAt(bi));
   4648                         }
   4649                     }
   4650 
   4651                     if (attributesToRemove.isEmpty()) {
   4652                         continue;
   4653                     }
   4654 
   4655                     const size_t sdkCount = attributesToRemove.size();
   4656                     for (size_t i = 0; i < sdkCount; i++) {
   4657                         const int sdkLevel = attributesToRemove.keyAt(i);
   4658 
   4659                         if (!shouldGenerateVersionedResource(c, config, sdkLevel)) {
   4660                             // There is a style that will override this generated one.
   4661                             continue;
   4662                         }
   4663 
   4664                         // Duplicate the entry under the same configuration
   4665                         // but with sdkVersion == sdkLevel.
   4666                         ConfigDescription newConfig(config);
   4667                         newConfig.sdkVersion = sdkLevel;
   4668 
   4669                         sp<Entry> newEntry = new Entry(*e);
   4670 
   4671                         // Remove all items that have a higher SDK level than
   4672                         // the one we are synthesizing.
   4673                         for (size_t j = 0; j < sdkCount; j++) {
   4674                             if (j == i) {
   4675                                 continue;
   4676                             }
   4677 
   4678                             if (attributesToRemove.keyAt(j) > sdkLevel) {
   4679                                 const size_t attrCount = attributesToRemove[j].size();
   4680                                 for (size_t k = 0; k < attrCount; k++) {
   4681                                     newEntry->removeFromBag(attributesToRemove[j][k]);
   4682                                 }
   4683                             }
   4684                         }
   4685 
   4686                         entriesToAdd.add(key_value_pair_t<ConfigDescription, sp<Entry> >(
   4687                                 newConfig, newEntry));
   4688                     }
   4689 
   4690                     // Remove the attribute from the original.
   4691                     for (size_t i = 0; i < attributesToRemove.size(); i++) {
   4692                         for (size_t j = 0; j < attributesToRemove[i].size(); j++) {
   4693                             e->removeFromBag(attributesToRemove[i][j]);
   4694                         }
   4695                     }
   4696                 }
   4697 
   4698                 const size_t entriesToAddCount = entriesToAdd.size();
   4699                 for (size_t i = 0; i < entriesToAddCount; i++) {
   4700                     assert(entries.indexOfKey(entriesToAdd[i].key) < 0);
   4701 
   4702                     if (bundle->getVerbose()) {
   4703                         entriesToAdd[i].value->getPos()
   4704                                 .printf("using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.",
   4705                                         entriesToAdd[i].key.sdkVersion,
   4706                                         String8(p->getName()).string(),
   4707                                         String8(t->getName()).string(),
   4708                                         String8(entriesToAdd[i].value->getName()).string(),
   4709                                         entriesToAdd[i].key.toString().string());
   4710                     }
   4711 
   4712                     sp<Entry> newEntry = t->getEntry(c->getName(),
   4713                             entriesToAdd[i].value->getPos(),
   4714                             &entriesToAdd[i].key);
   4715 
   4716                     *newEntry = *entriesToAdd[i].value;
   4717                 }
   4718             }
   4719         }
   4720     }
   4721     return NO_ERROR;
   4722 }
   4723 
   4724 status_t ResourceTable::modifyForCompat(const Bundle* bundle,
   4725                                         const String16& resourceName,
   4726                                         const sp<AaptFile>& target,
   4727                                         const sp<XMLNode>& root) {
   4728     const String16 vector16("vector");
   4729     const String16 animatedVector16("animated-vector");
   4730 
   4731     const int minSdk = getMinSdkVersion(bundle);
   4732     if (minSdk >= SDK_LOLLIPOP_MR1) {
   4733         // Lollipop MR1 and up handles public attributes differently, no
   4734         // need to do any compat modifications.
   4735         return NO_ERROR;
   4736     }
   4737 
   4738     const ConfigDescription config(target->getGroupEntry().toParams());
   4739     if (target->getResourceType() == "" || config.sdkVersion >= SDK_LOLLIPOP_MR1) {
   4740         // Skip resources that have no type (AndroidManifest.xml) or are already version qualified
   4741         // with v21 or higher.
   4742         return NO_ERROR;
   4743     }
   4744 
   4745     sp<XMLNode> newRoot = NULL;
   4746     int sdkVersionToGenerate = SDK_LOLLIPOP_MR1;
   4747 
   4748     Vector<sp<XMLNode> > nodesToVisit;
   4749     nodesToVisit.push(root);
   4750     while (!nodesToVisit.isEmpty()) {
   4751         sp<XMLNode> node = nodesToVisit.top();
   4752         nodesToVisit.pop();
   4753 
   4754         if (bundle->getNoVersionVectors() && (node->getElementName() == vector16 ||
   4755                     node->getElementName() == animatedVector16)) {
   4756             // We were told not to version vector tags, so skip the children here.
   4757             continue;
   4758         }
   4759 
   4760         const Vector<XMLNode::attribute_entry>& attrs = node->getAttributes();
   4761         for (size_t i = 0; i < attrs.size(); i++) {
   4762             const XMLNode::attribute_entry& attr = attrs[i];
   4763             const int sdkLevel = getPublicAttributeSdkLevel(attr.nameResId);
   4764             if (sdkLevel > 1 && sdkLevel > config.sdkVersion && sdkLevel > minSdk) {
   4765                 if (newRoot == NULL) {
   4766                     newRoot = root->clone();
   4767                 }
   4768 
   4769                 // Find the smallest sdk version that we need to synthesize for
   4770                 // and do that one. Subsequent versions will be processed on
   4771                 // the next pass.
   4772                 sdkVersionToGenerate = std::min(sdkLevel, sdkVersionToGenerate);
   4773 
   4774                 if (bundle->getVerbose()) {
   4775                     SourcePos(node->getFilename(), node->getStartLineNumber()).printf(
   4776                             "removing attribute %s%s%s from <%s>",
   4777                             String8(attr.ns).string(),
   4778                             (attr.ns.size() == 0 ? "" : ":"),
   4779                             String8(attr.name).string(),
   4780                             String8(node->getElementName()).string());
   4781                 }
   4782                 node->removeAttribute(i);
   4783                 i--;
   4784             }
   4785         }
   4786 
   4787         // Schedule a visit to the children.
   4788         const Vector<sp<XMLNode> >& children = node->getChildren();
   4789         const size_t childCount = children.size();
   4790         for (size_t i = 0; i < childCount; i++) {
   4791             nodesToVisit.push(children[i]);
   4792         }
   4793     }
   4794 
   4795     if (newRoot == NULL) {
   4796         return NO_ERROR;
   4797     }
   4798 
   4799     // Look to see if we already have an overriding v21 configuration.
   4800     sp<ConfigList> cl = getConfigList(String16(mAssets->getPackage()),
   4801             String16(target->getResourceType()), resourceName);
   4802     if (shouldGenerateVersionedResource(cl, config, sdkVersionToGenerate)) {
   4803         // We don't have an overriding entry for v21, so we must duplicate this one.
   4804         ConfigDescription newConfig(config);
   4805         newConfig.sdkVersion = sdkVersionToGenerate;
   4806         sp<AaptFile> newFile = new AaptFile(target->getSourceFile(),
   4807                 AaptGroupEntry(newConfig), target->getResourceType());
   4808         String8 resPath = String8::format("res/%s/%s.xml",
   4809                 newFile->getGroupEntry().toDirName(target->getResourceType()).string(),
   4810                 String8(resourceName).string());
   4811         resPath.convertToResPath();
   4812 
   4813         // Add a resource table entry.
   4814         if (bundle->getVerbose()) {
   4815             SourcePos(target->getSourceFile(), -1).printf(
   4816                     "using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.",
   4817                     newConfig.sdkVersion,
   4818                     mAssets->getPackage().string(),
   4819                     newFile->getResourceType().string(),
   4820                     String8(resourceName).string(),
   4821                     newConfig.toString().string());
   4822         }
   4823 
   4824         addEntry(SourcePos(),
   4825                 String16(mAssets->getPackage()),
   4826                 String16(target->getResourceType()),
   4827                 resourceName,
   4828                 String16(resPath),
   4829                 NULL,
   4830                 &newConfig);
   4831 
   4832         // Schedule this to be compiled.
   4833         CompileResourceWorkItem item;
   4834         item.resourceName = resourceName;
   4835         item.resPath = resPath;
   4836         item.file = newFile;
   4837         item.xmlRoot = newRoot;
   4838         item.needsCompiling = false;    // This step occurs after we parse/assign, so we don't need
   4839                                         // to do it again.
   4840         mWorkQueue.push(item);
   4841     }
   4842     return NO_ERROR;
   4843 }
   4844 
   4845 void ResourceTable::getDensityVaryingResources(
   4846         KeyedVector<Symbol, Vector<SymbolDefinition> >& resources) {
   4847     const ConfigDescription nullConfig;
   4848 
   4849     const size_t packageCount = mOrderedPackages.size();
   4850     for (size_t p = 0; p < packageCount; p++) {
   4851         const Vector<sp<Type> >& types = mOrderedPackages[p]->getOrderedTypes();
   4852         const size_t typeCount = types.size();
   4853         for (size_t t = 0; t < typeCount; t++) {
   4854             const Vector<sp<ConfigList> >& configs = types[t]->getOrderedConfigs();
   4855             const size_t configCount = configs.size();
   4856             for (size_t c = 0; c < configCount; c++) {
   4857                 const DefaultKeyedVector<ConfigDescription, sp<Entry> >& configEntries
   4858                         = configs[c]->getEntries();
   4859                 const size_t configEntryCount = configEntries.size();
   4860                 for (size_t ce = 0; ce < configEntryCount; ce++) {
   4861                     const ConfigDescription& config = configEntries.keyAt(ce);
   4862                     if (AaptConfig::isDensityOnly(config)) {
   4863                         // This configuration only varies with regards to density.
   4864                         const Symbol symbol(
   4865                                 mOrderedPackages[p]->getName(),
   4866                                 types[t]->getName(),
   4867                                 configs[c]->getName(),
   4868                                 getResId(mOrderedPackages[p], types[t],
   4869                                          configs[c]->getEntryIndex()));
   4870 
   4871                         const sp<Entry>& entry = configEntries.valueAt(ce);
   4872                         AaptUtil::appendValue(resources, symbol,
   4873                                               SymbolDefinition(symbol, config, entry->getPos()));
   4874                     }
   4875                 }
   4876             }
   4877         }
   4878     }
   4879 }
   4880 
   4881 static String16 buildNamespace(const String16& package) {
   4882     return String16("http://schemas.android.com/apk/res/") + package;
   4883 }
   4884 
   4885 static sp<XMLNode> findOnlyChildElement(const sp<XMLNode>& parent) {
   4886     const Vector<sp<XMLNode> >& children = parent->getChildren();
   4887     sp<XMLNode> onlyChild;
   4888     for (size_t i = 0; i < children.size(); i++) {
   4889         if (children[i]->getType() != XMLNode::TYPE_CDATA) {
   4890             if (onlyChild != NULL) {
   4891                 return NULL;
   4892             }
   4893             onlyChild = children[i];
   4894         }
   4895     }
   4896     return onlyChild;
   4897 }
   4898 
   4899 /**
   4900  * Detects use of the `bundle' format and extracts nested resources into their own top level
   4901  * resources. The bundle format looks like this:
   4902  *
   4903  * <!-- res/drawable/bundle.xml -->
   4904  * <animated-vector xmlns:aapt="http://schemas.android.com/aapt">
   4905  *   <aapt:attr name="android:drawable">
   4906  *     <vector android:width="60dp"
   4907  *             android:height="60dp">
   4908  *       <path android:name="v"
   4909  *             android:fillColor="#000000"
   4910  *             android:pathData="M300,70 l 0,-70 70,..." />
   4911  *     </vector>
   4912  *   </aapt:attr>
   4913  * </animated-vector>
   4914  *
   4915  * When AAPT sees the <aapt:attr> tag, it will extract its single element and its children
   4916  * into a new high-level resource, assigning it a name and ID. Then value of the `name`
   4917  * attribute must be a resource attribute. That resource attribute is inserted into the parent
   4918  * with the reference to the extracted resource as the value.
   4919  *
   4920  * <!-- res/drawable/bundle.xml -->
   4921  * <animated-vector android:drawable="@drawable/bundle_1.xml">
   4922  * </animated-vector>
   4923  *
   4924  * <!-- res/drawable/bundle_1.xml -->
   4925  * <vector android:width="60dp"
   4926  *         android:height="60dp">
   4927  *   <path android:name="v"
   4928  *         android:fillColor="#000000"
   4929  *         android:pathData="M300,70 l 0,-70 70,..." />
   4930  * </vector>
   4931  */
   4932 status_t ResourceTable::processBundleFormat(const Bundle* bundle,
   4933                                             const String16& resourceName,
   4934                                             const sp<AaptFile>& target,
   4935                                             const sp<XMLNode>& root) {
   4936     Vector<sp<XMLNode> > namespaces;
   4937     if (root->getType() == XMLNode::TYPE_NAMESPACE) {
   4938         namespaces.push(root);
   4939     }
   4940     return processBundleFormatImpl(bundle, resourceName, target, root, &namespaces);
   4941 }
   4942 
   4943 status_t ResourceTable::processBundleFormatImpl(const Bundle* bundle,
   4944                                                 const String16& resourceName,
   4945                                                 const sp<AaptFile>& target,
   4946                                                 const sp<XMLNode>& parent,
   4947                                                 Vector<sp<XMLNode> >* namespaces) {
   4948     const String16 kAaptNamespaceUri16("http://schemas.android.com/aapt");
   4949     const String16 kName16("name");
   4950     const String16 kAttr16("attr");
   4951     const String16 kAssetPackage16(mAssets->getPackage());
   4952 
   4953     Vector<sp<XMLNode> >& children = parent->getChildren();
   4954     for (size_t i = 0; i < children.size(); i++) {
   4955         const sp<XMLNode>& child = children[i];
   4956 
   4957         if (child->getType() == XMLNode::TYPE_CDATA) {
   4958             continue;
   4959         } else if (child->getType() == XMLNode::TYPE_NAMESPACE) {
   4960             namespaces->push(child);
   4961         }
   4962 
   4963         if (child->getElementNamespace() != kAaptNamespaceUri16 ||
   4964                 child->getElementName() != kAttr16) {
   4965             status_t result = processBundleFormatImpl(bundle, resourceName, target, child,
   4966                                                       namespaces);
   4967             if (result != NO_ERROR) {
   4968                 return result;
   4969             }
   4970 
   4971             if (child->getType() == XMLNode::TYPE_NAMESPACE) {
   4972                 namespaces->pop();
   4973             }
   4974             continue;
   4975         }
   4976 
   4977         // This is the <aapt:attr> tag. Look for the 'name' attribute.
   4978         SourcePos source(child->getFilename(), child->getStartLineNumber());
   4979 
   4980         sp<XMLNode> nestedRoot = findOnlyChildElement(child);
   4981         if (nestedRoot == NULL) {
   4982             source.error("<%s:%s> must have exactly one child element",
   4983                          String8(child->getElementNamespace()).string(),
   4984                          String8(child->getElementName()).string());
   4985             return UNKNOWN_ERROR;
   4986         }
   4987 
   4988         // Find the special attribute 'parent-attr'. This attribute's value contains
   4989         // the resource attribute for which this element should be assigned in the parent.
   4990         const XMLNode::attribute_entry* attr = child->getAttribute(String16(), kName16);
   4991         if (attr == NULL) {
   4992             source.error("inline resource definition must specify an attribute via 'name'");
   4993             return UNKNOWN_ERROR;
   4994         }
   4995 
   4996         // Parse the attribute name.
   4997         const char* errorMsg = NULL;
   4998         String16 attrPackage, attrType, attrName;
   4999         bool result = ResTable::expandResourceRef(attr->string.string(),
   5000                                                   attr->string.size(),
   5001                                                   &attrPackage, &attrType, &attrName,
   5002                                                   &kAttr16, &kAssetPackage16,
   5003                                                   &errorMsg, NULL);
   5004         if (!result) {
   5005             source.error("invalid attribute name for 'name': %s", errorMsg);
   5006             return UNKNOWN_ERROR;
   5007         }
   5008 
   5009         if (attrType != kAttr16) {
   5010             // The value of the 'name' attribute must be an attribute reference.
   5011             source.error("value of 'name' must be an attribute reference.");
   5012             return UNKNOWN_ERROR;
   5013         }
   5014 
   5015         // Generate a name for this nested resource and try to add it to the table.
   5016         // We do this in a loop because the name may be taken, in which case we will
   5017         // increment a suffix until we succeed.
   5018         String8 nestedResourceName;
   5019         String8 nestedResourcePath;
   5020         int suffix = 1;
   5021         while (true) {
   5022             // This child element will be extracted into its own resource file.
   5023             // Generate a name and path for it from its parent.
   5024             nestedResourceName = String8::format("%s_%d",
   5025                         String8(resourceName).string(), suffix++);
   5026             nestedResourcePath = String8::format("res/%s/%s.xml",
   5027                         target->getGroupEntry().toDirName(target->getResourceType())
   5028                                                .string(),
   5029                         nestedResourceName.string());
   5030 
   5031             // Lookup or create the entry for this name.
   5032             sp<Entry> entry = getEntry(kAssetPackage16,
   5033                                        String16(target->getResourceType()),
   5034                                        String16(nestedResourceName),
   5035                                        source,
   5036                                        false,
   5037                                        &target->getGroupEntry().toParams(),
   5038                                        true);
   5039             if (entry == NULL) {
   5040                 return UNKNOWN_ERROR;
   5041             }
   5042 
   5043             if (entry->getType() == Entry::TYPE_UNKNOWN) {
   5044                 // The value for this resource has never been set,
   5045                 // meaning we're good!
   5046                 entry->setItem(source, String16(nestedResourcePath));
   5047                 break;
   5048             }
   5049 
   5050             // We failed (name already exists), so try with a different name
   5051             // (increment the suffix).
   5052         }
   5053 
   5054         if (bundle->getVerbose()) {
   5055             source.printf("generating nested resource %s:%s/%s",
   5056                     mAssets->getPackage().string(), target->getResourceType().string(),
   5057                     nestedResourceName.string());
   5058         }
   5059 
   5060         // Build the attribute reference and assign it to the parent.
   5061         String16 nestedResourceRef = String16(String8::format("@%s:%s/%s",
   5062                     mAssets->getPackage().string(), target->getResourceType().string(),
   5063                     nestedResourceName.string()));
   5064 
   5065         String16 attrNs = buildNamespace(attrPackage);
   5066         if (parent->getAttribute(attrNs, attrName) != NULL) {
   5067             SourcePos(parent->getFilename(), parent->getStartLineNumber())
   5068                     .error("parent of nested resource already defines attribute '%s:%s'",
   5069                            String8(attrPackage).string(), String8(attrName).string());
   5070             return UNKNOWN_ERROR;
   5071         }
   5072 
   5073         // Add the reference to the inline resource.
   5074         parent->addAttribute(attrNs, attrName, nestedResourceRef);
   5075 
   5076         // Remove the <aapt:attr> child element from here.
   5077         children.removeAt(i);
   5078         i--;
   5079 
   5080         // Append all namespace declarations that we've seen on this branch in the XML tree
   5081         // to this resource.
   5082         // We do this because the order of namespace declarations and prefix usage is determined
   5083         // by the developer and we do not want to override any decisions. Be conservative.
   5084         for (size_t nsIndex = namespaces->size(); nsIndex > 0; nsIndex--) {
   5085             const sp<XMLNode>& ns = namespaces->itemAt(nsIndex - 1);
   5086             sp<XMLNode> newNs = XMLNode::newNamespace(ns->getFilename(), ns->getNamespacePrefix(),
   5087                                                       ns->getNamespaceUri());
   5088             newNs->addChild(nestedRoot);
   5089             nestedRoot = newNs;
   5090         }
   5091 
   5092         // Schedule compilation of the nested resource.
   5093         CompileResourceWorkItem workItem;
   5094         workItem.resPath = nestedResourcePath;
   5095         workItem.resourceName = String16(nestedResourceName);
   5096         workItem.xmlRoot = nestedRoot;
   5097         workItem.file = new AaptFile(target->getSourceFile(), target->getGroupEntry(),
   5098                                      target->getResourceType());
   5099         mWorkQueue.push(workItem);
   5100     }
   5101     return NO_ERROR;
   5102 }
   5103