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 "XMLNode.h"
     10 #include "ResourceFilter.h"
     11 #include "ResourceIdCache.h"
     12 
     13 #include <androidfw/ResourceTypes.h>
     14 #include <utils/ByteOrder.h>
     15 #include <stdarg.h>
     16 
     17 #define NOISY(x) //x
     18 
     19 status_t compileXmlFile(const sp<AaptAssets>& assets,
     20                         const sp<AaptFile>& target,
     21                         ResourceTable* table,
     22                         int options)
     23 {
     24     sp<XMLNode> root = XMLNode::parse(target);
     25     if (root == NULL) {
     26         return UNKNOWN_ERROR;
     27     }
     28 
     29     return compileXmlFile(assets, root, target, table, options);
     30 }
     31 
     32 status_t compileXmlFile(const sp<AaptAssets>& assets,
     33                         const sp<AaptFile>& target,
     34                         const sp<AaptFile>& outTarget,
     35                         ResourceTable* table,
     36                         int options)
     37 {
     38     sp<XMLNode> root = XMLNode::parse(target);
     39     if (root == NULL) {
     40         return UNKNOWN_ERROR;
     41     }
     42 
     43     return compileXmlFile(assets, root, outTarget, table, options);
     44 }
     45 
     46 status_t compileXmlFile(const sp<AaptAssets>& assets,
     47                         const sp<XMLNode>& root,
     48                         const sp<AaptFile>& target,
     49                         ResourceTable* table,
     50                         int options)
     51 {
     52     if ((options&XML_COMPILE_STRIP_WHITESPACE) != 0) {
     53         root->removeWhitespace(true, NULL);
     54     } else  if ((options&XML_COMPILE_COMPACT_WHITESPACE) != 0) {
     55         root->removeWhitespace(false, NULL);
     56     }
     57 
     58     if ((options&XML_COMPILE_UTF8) != 0) {
     59         root->setUTF8(true);
     60     }
     61 
     62     bool hasErrors = false;
     63 
     64     if ((options&XML_COMPILE_ASSIGN_ATTRIBUTE_IDS) != 0) {
     65         status_t err = root->assignResourceIds(assets, table);
     66         if (err != NO_ERROR) {
     67             hasErrors = true;
     68         }
     69     }
     70 
     71     status_t err = root->parseValues(assets, table);
     72     if (err != NO_ERROR) {
     73         hasErrors = true;
     74     }
     75 
     76     if (hasErrors) {
     77         return UNKNOWN_ERROR;
     78     }
     79 
     80     NOISY(printf("Input XML Resource:\n"));
     81     NOISY(root->print());
     82     err = root->flatten(target,
     83             (options&XML_COMPILE_STRIP_COMMENTS) != 0,
     84             (options&XML_COMPILE_STRIP_RAW_VALUES) != 0);
     85     if (err != NO_ERROR) {
     86         return err;
     87     }
     88 
     89     NOISY(printf("Output XML Resource:\n"));
     90     NOISY(ResXMLTree tree;
     91         tree.setTo(target->getData(), target->getSize());
     92         printXMLBlock(&tree));
     93 
     94     target->setCompressionMethod(ZipEntry::kCompressDeflated);
     95 
     96     return err;
     97 }
     98 
     99 #undef NOISY
    100 #define NOISY(x) //x
    101 
    102 struct flag_entry
    103 {
    104     const char16_t* name;
    105     size_t nameLen;
    106     uint32_t value;
    107     const char* description;
    108 };
    109 
    110 static const char16_t referenceArray[] =
    111     { 'r', 'e', 'f', 'e', 'r', 'e', 'n', 'c', 'e' };
    112 static const char16_t stringArray[] =
    113     { 's', 't', 'r', 'i', 'n', 'g' };
    114 static const char16_t integerArray[] =
    115     { 'i', 'n', 't', 'e', 'g', 'e', 'r' };
    116 static const char16_t booleanArray[] =
    117     { 'b', 'o', 'o', 'l', 'e', 'a', 'n' };
    118 static const char16_t colorArray[] =
    119     { 'c', 'o', 'l', 'o', 'r' };
    120 static const char16_t floatArray[] =
    121     { 'f', 'l', 'o', 'a', 't' };
    122 static const char16_t dimensionArray[] =
    123     { 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n' };
    124 static const char16_t fractionArray[] =
    125     { 'f', 'r', 'a', 'c', 't', 'i', 'o', 'n' };
    126 static const char16_t enumArray[] =
    127     { 'e', 'n', 'u', 'm' };
    128 static const char16_t flagsArray[] =
    129     { 'f', 'l', 'a', 'g', 's' };
    130 
    131 static const flag_entry gFormatFlags[] = {
    132     { referenceArray, sizeof(referenceArray)/2, ResTable_map::TYPE_REFERENCE,
    133       "a reference to another resource, in the form \"<code>@[+][<i>package</i>:]<i>type</i>:<i>name</i></code>\"\n"
    134       "or to a theme attribute in the form \"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>\"."},
    135     { stringArray, sizeof(stringArray)/2, ResTable_map::TYPE_STRING,
    136       "a string value, using '\\\\;' to escape characters such as '\\\\n' or '\\\\uxxxx' for a unicode character." },
    137     { integerArray, sizeof(integerArray)/2, ResTable_map::TYPE_INTEGER,
    138       "an integer value, such as \"<code>100</code>\"." },
    139     { booleanArray, sizeof(booleanArray)/2, ResTable_map::TYPE_BOOLEAN,
    140       "a boolean value, either \"<code>true</code>\" or \"<code>false</code>\"." },
    141     { colorArray, sizeof(colorArray)/2, ResTable_map::TYPE_COLOR,
    142       "a color value, in the form of \"<code>#<i>rgb</i></code>\", \"<code>#<i>argb</i></code>\",\n"
    143       "\"<code>#<i>rrggbb</i></code>\", or \"<code>#<i>aarrggbb</i></code>\"." },
    144     { floatArray, sizeof(floatArray)/2, ResTable_map::TYPE_FLOAT,
    145       "a floating point value, such as \"<code>1.2</code>\"."},
    146     { dimensionArray, sizeof(dimensionArray)/2, ResTable_map::TYPE_DIMENSION,
    147       "a dimension value, which is a floating point number appended with a unit such as \"<code>14.5sp</code>\".\n"
    148       "Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),\n"
    149       "in (inches), mm (millimeters)." },
    150     { fractionArray, sizeof(fractionArray)/2, ResTable_map::TYPE_FRACTION,
    151       "a fractional value, which is a floating point number appended with either % or %p, such as \"<code>14.5%</code>\".\n"
    152       "The % suffix always means a percentage of the base size; the optional %p suffix provides a size relative to\n"
    153       "some parent container." },
    154     { enumArray, sizeof(enumArray)/2, ResTable_map::TYPE_ENUM, NULL },
    155     { flagsArray, sizeof(flagsArray)/2, ResTable_map::TYPE_FLAGS, NULL },
    156     { NULL, 0, 0, NULL }
    157 };
    158 
    159 static const char16_t suggestedArray[] = { 's', 'u', 'g', 'g', 'e', 's', 't', 'e', 'd' };
    160 
    161 static const flag_entry l10nRequiredFlags[] = {
    162     { suggestedArray, sizeof(suggestedArray)/2, ResTable_map::L10N_SUGGESTED, NULL },
    163     { NULL, 0, 0, NULL }
    164 };
    165 
    166 static const char16_t nulStr[] = { 0 };
    167 
    168 static uint32_t parse_flags(const char16_t* str, size_t len,
    169                              const flag_entry* flags, bool* outError = NULL)
    170 {
    171     while (len > 0 && isspace(*str)) {
    172         str++;
    173         len--;
    174     }
    175     while (len > 0 && isspace(str[len-1])) {
    176         len--;
    177     }
    178 
    179     const char16_t* const end = str + len;
    180     uint32_t value = 0;
    181 
    182     while (str < end) {
    183         const char16_t* div = str;
    184         while (div < end && *div != '|') {
    185             div++;
    186         }
    187 
    188         const flag_entry* cur = flags;
    189         while (cur->name) {
    190             if (strzcmp16(cur->name, cur->nameLen, str, div-str) == 0) {
    191                 value |= cur->value;
    192                 break;
    193             }
    194             cur++;
    195         }
    196 
    197         if (!cur->name) {
    198             if (outError) *outError = true;
    199             return 0;
    200         }
    201 
    202         str = div < end ? div+1 : div;
    203     }
    204 
    205     if (outError) *outError = false;
    206     return value;
    207 }
    208 
    209 static String16 mayOrMust(int type, int flags)
    210 {
    211     if ((type&(~flags)) == 0) {
    212         return String16("<p>Must");
    213     }
    214 
    215     return String16("<p>May");
    216 }
    217 
    218 static void appendTypeInfo(ResourceTable* outTable, const String16& pkg,
    219         const String16& typeName, const String16& ident, int type,
    220         const flag_entry* flags)
    221 {
    222     bool hadType = false;
    223     while (flags->name) {
    224         if ((type&flags->value) != 0 && flags->description != NULL) {
    225             String16 fullMsg(mayOrMust(type, flags->value));
    226             fullMsg.append(String16(" be "));
    227             fullMsg.append(String16(flags->description));
    228             outTable->appendTypeComment(pkg, typeName, ident, fullMsg);
    229             hadType = true;
    230         }
    231         flags++;
    232     }
    233     if (hadType && (type&ResTable_map::TYPE_REFERENCE) == 0) {
    234         outTable->appendTypeComment(pkg, typeName, ident,
    235                 String16("<p>This may also be a reference to a resource (in the form\n"
    236                          "\"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>\") or\n"
    237                          "theme attribute (in the form\n"
    238                          "\"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>\")\n"
    239                          "containing a value of this type."));
    240     }
    241 }
    242 
    243 struct PendingAttribute
    244 {
    245     const String16 myPackage;
    246     const SourcePos sourcePos;
    247     const bool appendComment;
    248     int32_t type;
    249     String16 ident;
    250     String16 comment;
    251     bool hasErrors;
    252     bool added;
    253 
    254     PendingAttribute(String16 _package, const sp<AaptFile>& in,
    255             ResXMLTree& block, bool _appendComment)
    256         : myPackage(_package)
    257         , sourcePos(in->getPrintableSource(), block.getLineNumber())
    258         , appendComment(_appendComment)
    259         , type(ResTable_map::TYPE_ANY)
    260         , hasErrors(false)
    261         , added(false)
    262     {
    263     }
    264 
    265     status_t createIfNeeded(ResourceTable* outTable)
    266     {
    267         if (added || hasErrors) {
    268             return NO_ERROR;
    269         }
    270         added = true;
    271 
    272         String16 attr16("attr");
    273 
    274         if (outTable->hasBagOrEntry(myPackage, attr16, ident)) {
    275             sourcePos.error("Attribute \"%s\" has already been defined\n",
    276                     String8(ident).string());
    277             hasErrors = true;
    278             return UNKNOWN_ERROR;
    279         }
    280 
    281         char numberStr[16];
    282         sprintf(numberStr, "%d", type);
    283         status_t err = outTable->addBag(sourcePos, myPackage,
    284                 attr16, ident, String16(""),
    285                 String16("^type"),
    286                 String16(numberStr), NULL, NULL);
    287         if (err != NO_ERROR) {
    288             hasErrors = true;
    289             return err;
    290         }
    291         outTable->appendComment(myPackage, attr16, ident, comment, appendComment);
    292         //printf("Attribute %s comment: %s\n", String8(ident).string(),
    293         //     String8(comment).string());
    294         return err;
    295     }
    296 };
    297 
    298 static status_t compileAttribute(const sp<AaptFile>& in,
    299                                  ResXMLTree& block,
    300                                  const String16& myPackage,
    301                                  ResourceTable* outTable,
    302                                  String16* outIdent = NULL,
    303                                  bool inStyleable = false)
    304 {
    305     PendingAttribute attr(myPackage, in, block, inStyleable);
    306 
    307     const String16 attr16("attr");
    308     const String16 id16("id");
    309 
    310     // Attribute type constants.
    311     const String16 enum16("enum");
    312     const String16 flag16("flag");
    313 
    314     ResXMLTree::event_code_t code;
    315     size_t len;
    316     status_t err;
    317 
    318     ssize_t identIdx = block.indexOfAttribute(NULL, "name");
    319     if (identIdx >= 0) {
    320         attr.ident = String16(block.getAttributeStringValue(identIdx, &len));
    321         if (outIdent) {
    322             *outIdent = attr.ident;
    323         }
    324     } else {
    325         attr.sourcePos.error("A 'name' attribute is required for <attr>\n");
    326         attr.hasErrors = true;
    327     }
    328 
    329     attr.comment = String16(
    330             block.getComment(&len) ? block.getComment(&len) : nulStr);
    331 
    332     ssize_t typeIdx = block.indexOfAttribute(NULL, "format");
    333     if (typeIdx >= 0) {
    334         String16 typeStr = String16(block.getAttributeStringValue(typeIdx, &len));
    335         attr.type = parse_flags(typeStr.string(), typeStr.size(), gFormatFlags);
    336         if (attr.type == 0) {
    337             attr.sourcePos.error("Tag <attr> 'format' attribute value \"%s\" not valid\n",
    338                     String8(typeStr).string());
    339             attr.hasErrors = true;
    340         }
    341         attr.createIfNeeded(outTable);
    342     } else if (!inStyleable) {
    343         // Attribute definitions outside of styleables always define the
    344         // attribute as a generic value.
    345         attr.createIfNeeded(outTable);
    346     }
    347 
    348     //printf("Attribute %s: type=0x%08x\n", String8(attr.ident).string(), attr.type);
    349 
    350     ssize_t minIdx = block.indexOfAttribute(NULL, "min");
    351     if (minIdx >= 0) {
    352         String16 val = String16(block.getAttributeStringValue(minIdx, &len));
    353         if (!ResTable::stringToInt(val.string(), val.size(), NULL)) {
    354             attr.sourcePos.error("Tag <attr> 'min' attribute must be a number, not \"%s\"\n",
    355                     String8(val).string());
    356             attr.hasErrors = true;
    357         }
    358         attr.createIfNeeded(outTable);
    359         if (!attr.hasErrors) {
    360             err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident,
    361                     String16(""), String16("^min"), String16(val), NULL, NULL);
    362             if (err != NO_ERROR) {
    363                 attr.hasErrors = true;
    364             }
    365         }
    366     }
    367 
    368     ssize_t maxIdx = block.indexOfAttribute(NULL, "max");
    369     if (maxIdx >= 0) {
    370         String16 val = String16(block.getAttributeStringValue(maxIdx, &len));
    371         if (!ResTable::stringToInt(val.string(), val.size(), NULL)) {
    372             attr.sourcePos.error("Tag <attr> 'max' attribute must be a number, not \"%s\"\n",
    373                     String8(val).string());
    374             attr.hasErrors = true;
    375         }
    376         attr.createIfNeeded(outTable);
    377         if (!attr.hasErrors) {
    378             err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident,
    379                     String16(""), String16("^max"), String16(val), NULL, NULL);
    380             attr.hasErrors = true;
    381         }
    382     }
    383 
    384     if ((minIdx >= 0 || maxIdx >= 0) && (attr.type&ResTable_map::TYPE_INTEGER) == 0) {
    385         attr.sourcePos.error("Tag <attr> must have format=integer attribute if using max or min\n");
    386         attr.hasErrors = true;
    387     }
    388 
    389     ssize_t l10nIdx = block.indexOfAttribute(NULL, "localization");
    390     if (l10nIdx >= 0) {
    391         const uint16_t* str = block.getAttributeStringValue(l10nIdx, &len);
    392         bool error;
    393         uint32_t l10n_required = parse_flags(str, len, l10nRequiredFlags, &error);
    394         if (error) {
    395             attr.sourcePos.error("Tag <attr> 'localization' attribute value \"%s\" not valid\n",
    396                     String8(str).string());
    397             attr.hasErrors = true;
    398         }
    399         attr.createIfNeeded(outTable);
    400         if (!attr.hasErrors) {
    401             char buf[11];
    402             sprintf(buf, "%d", l10n_required);
    403             err = outTable->addBag(attr.sourcePos, myPackage, attr16, attr.ident,
    404                     String16(""), String16("^l10n"), String16(buf), NULL, NULL);
    405             if (err != NO_ERROR) {
    406                 attr.hasErrors = true;
    407             }
    408         }
    409     }
    410 
    411     String16 enumOrFlagsComment;
    412 
    413     while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
    414         if (code == ResXMLTree::START_TAG) {
    415             uint32_t localType = 0;
    416             if (strcmp16(block.getElementName(&len), enum16.string()) == 0) {
    417                 localType = ResTable_map::TYPE_ENUM;
    418             } else if (strcmp16(block.getElementName(&len), flag16.string()) == 0) {
    419                 localType = ResTable_map::TYPE_FLAGS;
    420             } else {
    421                 SourcePos(in->getPrintableSource(), block.getLineNumber())
    422                         .error("Tag <%s> can not appear inside <attr>, only <enum> or <flag>\n",
    423                         String8(block.getElementName(&len)).string());
    424                 return UNKNOWN_ERROR;
    425             }
    426 
    427             attr.createIfNeeded(outTable);
    428 
    429             if (attr.type == ResTable_map::TYPE_ANY) {
    430                 // No type was explicitly stated, so supplying enum tags
    431                 // implicitly creates an enum or flag.
    432                 attr.type = 0;
    433             }
    434 
    435             if ((attr.type&(ResTable_map::TYPE_ENUM|ResTable_map::TYPE_FLAGS)) == 0) {
    436                 // Wasn't originally specified as an enum, so update its type.
    437                 attr.type |= localType;
    438                 if (!attr.hasErrors) {
    439                     char numberStr[16];
    440                     sprintf(numberStr, "%d", attr.type);
    441                     err = outTable->addBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
    442                             myPackage, attr16, attr.ident, String16(""),
    443                             String16("^type"), String16(numberStr), NULL, NULL, true);
    444                     if (err != NO_ERROR) {
    445                         attr.hasErrors = true;
    446                     }
    447                 }
    448             } else if ((uint32_t)(attr.type&(ResTable_map::TYPE_ENUM|ResTable_map::TYPE_FLAGS)) != localType) {
    449                 if (localType == ResTable_map::TYPE_ENUM) {
    450                     SourcePos(in->getPrintableSource(), block.getLineNumber())
    451                             .error("<enum> attribute can not be used inside a flags format\n");
    452                     attr.hasErrors = true;
    453                 } else {
    454                     SourcePos(in->getPrintableSource(), block.getLineNumber())
    455                             .error("<flag> attribute can not be used inside a enum format\n");
    456                     attr.hasErrors = true;
    457                 }
    458             }
    459 
    460             String16 itemIdent;
    461             ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "name");
    462             if (itemIdentIdx >= 0) {
    463                 itemIdent = String16(block.getAttributeStringValue(itemIdentIdx, &len));
    464             } else {
    465                 SourcePos(in->getPrintableSource(), block.getLineNumber())
    466                         .error("A 'name' attribute is required for <enum> or <flag>\n");
    467                 attr.hasErrors = true;
    468             }
    469 
    470             String16 value;
    471             ssize_t valueIdx = block.indexOfAttribute(NULL, "value");
    472             if (valueIdx >= 0) {
    473                 value = String16(block.getAttributeStringValue(valueIdx, &len));
    474             } else {
    475                 SourcePos(in->getPrintableSource(), block.getLineNumber())
    476                         .error("A 'value' attribute is required for <enum> or <flag>\n");
    477                 attr.hasErrors = true;
    478             }
    479             if (!attr.hasErrors && !ResTable::stringToInt(value.string(), value.size(), NULL)) {
    480                 SourcePos(in->getPrintableSource(), block.getLineNumber())
    481                         .error("Tag <enum> or <flag> 'value' attribute must be a number,"
    482                         " not \"%s\"\n",
    483                         String8(value).string());
    484                 attr.hasErrors = true;
    485             }
    486 
    487             // Make sure an id is defined for this enum/flag identifier...
    488             if (!attr.hasErrors && !outTable->hasBagOrEntry(itemIdent, &id16, &myPackage)) {
    489                 err = outTable->startBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
    490                                          myPackage, id16, itemIdent, String16(), NULL);
    491                 if (err != NO_ERROR) {
    492                     attr.hasErrors = true;
    493                 }
    494             }
    495 
    496             if (!attr.hasErrors) {
    497                 if (enumOrFlagsComment.size() == 0) {
    498                     enumOrFlagsComment.append(mayOrMust(attr.type,
    499                             ResTable_map::TYPE_ENUM|ResTable_map::TYPE_FLAGS));
    500                     enumOrFlagsComment.append((attr.type&ResTable_map::TYPE_ENUM)
    501                                        ? String16(" be one of the following constant values.")
    502                                        : String16(" be one or more (separated by '|') of the following constant values."));
    503                     enumOrFlagsComment.append(String16("</p>\n<table>\n"
    504                                                 "<colgroup align=\"left\" />\n"
    505                                                 "<colgroup align=\"left\" />\n"
    506                                                 "<colgroup align=\"left\" />\n"
    507                                                 "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>"));
    508                 }
    509 
    510                 enumOrFlagsComment.append(String16("\n<tr><td><code>"));
    511                 enumOrFlagsComment.append(itemIdent);
    512                 enumOrFlagsComment.append(String16("</code></td><td>"));
    513                 enumOrFlagsComment.append(value);
    514                 enumOrFlagsComment.append(String16("</td><td>"));
    515                 if (block.getComment(&len)) {
    516                     enumOrFlagsComment.append(String16(block.getComment(&len)));
    517                 }
    518                 enumOrFlagsComment.append(String16("</td></tr>"));
    519 
    520                 err = outTable->addBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
    521                                        myPackage,
    522                                        attr16, attr.ident, String16(""),
    523                                        itemIdent, value, NULL, NULL, false, true);
    524                 if (err != NO_ERROR) {
    525                     attr.hasErrors = true;
    526                 }
    527             }
    528         } else if (code == ResXMLTree::END_TAG) {
    529             if (strcmp16(block.getElementName(&len), attr16.string()) == 0) {
    530                 break;
    531             }
    532             if ((attr.type&ResTable_map::TYPE_ENUM) != 0) {
    533                 if (strcmp16(block.getElementName(&len), enum16.string()) != 0) {
    534                     SourcePos(in->getPrintableSource(), block.getLineNumber())
    535                             .error("Found tag </%s> where </enum> is expected\n",
    536                             String8(block.getElementName(&len)).string());
    537                     return UNKNOWN_ERROR;
    538                 }
    539             } else {
    540                 if (strcmp16(block.getElementName(&len), flag16.string()) != 0) {
    541                     SourcePos(in->getPrintableSource(), block.getLineNumber())
    542                             .error("Found tag </%s> where </flag> is expected\n",
    543                             String8(block.getElementName(&len)).string());
    544                     return UNKNOWN_ERROR;
    545                 }
    546             }
    547         }
    548     }
    549 
    550     if (!attr.hasErrors && attr.added) {
    551         appendTypeInfo(outTable, myPackage, attr16, attr.ident, attr.type, gFormatFlags);
    552     }
    553 
    554     if (!attr.hasErrors && enumOrFlagsComment.size() > 0) {
    555         enumOrFlagsComment.append(String16("\n</table>"));
    556         outTable->appendTypeComment(myPackage, attr16, attr.ident, enumOrFlagsComment);
    557     }
    558 
    559 
    560     return NO_ERROR;
    561 }
    562 
    563 bool localeIsDefined(const ResTable_config& config)
    564 {
    565     return config.locale == 0;
    566 }
    567 
    568 status_t parseAndAddBag(Bundle* bundle,
    569                         const sp<AaptFile>& in,
    570                         ResXMLTree* block,
    571                         const ResTable_config& config,
    572                         const String16& myPackage,
    573                         const String16& curType,
    574                         const String16& ident,
    575                         const String16& parentIdent,
    576                         const String16& itemIdent,
    577                         int32_t curFormat,
    578                         bool isFormatted,
    579                         const String16& product,
    580                         bool pseudolocalize,
    581                         const bool overwrite,
    582                         ResourceTable* outTable)
    583 {
    584     status_t err;
    585     const String16 item16("item");
    586 
    587     String16 str;
    588     Vector<StringPool::entry_style_span> spans;
    589     err = parseStyledString(bundle, in->getPrintableSource().string(),
    590                             block, item16, &str, &spans, isFormatted,
    591                             pseudolocalize);
    592     if (err != NO_ERROR) {
    593         return err;
    594     }
    595 
    596     NOISY(printf("Adding resource bag entry l=%c%c c=%c%c orien=%d d=%d "
    597                  " pid=%s, bag=%s, id=%s: %s\n",
    598                  config.language[0], config.language[1],
    599                  config.country[0], config.country[1],
    600                  config.orientation, config.density,
    601                  String8(parentIdent).string(),
    602                  String8(ident).string(),
    603                  String8(itemIdent).string(),
    604                  String8(str).string()));
    605 
    606     err = outTable->addBag(SourcePos(in->getPrintableSource(), block->getLineNumber()),
    607                            myPackage, curType, ident, parentIdent, itemIdent, str,
    608                            &spans, &config, overwrite, false, curFormat);
    609     return err;
    610 }
    611 
    612 /*
    613  * Returns true if needle is one of the elements in the comma-separated list
    614  * haystack, false otherwise.
    615  */
    616 bool isInProductList(const String16& needle, const String16& haystack) {
    617     const char16_t *needle2 = needle.string();
    618     const char16_t *haystack2 = haystack.string();
    619     size_t needlesize = needle.size();
    620 
    621     while (*haystack2 != '\0') {
    622         if (strncmp16(haystack2, needle2, needlesize) == 0) {
    623             if (haystack2[needlesize] == '\0' || haystack2[needlesize] == ',') {
    624                 return true;
    625             }
    626         }
    627 
    628         while (*haystack2 != '\0' && *haystack2 != ',') {
    629             haystack2++;
    630         }
    631         if (*haystack2 == ',') {
    632             haystack2++;
    633         }
    634     }
    635 
    636     return false;
    637 }
    638 
    639 status_t parseAndAddEntry(Bundle* bundle,
    640                         const sp<AaptFile>& in,
    641                         ResXMLTree* block,
    642                         const ResTable_config& config,
    643                         const String16& myPackage,
    644                         const String16& curType,
    645                         const String16& ident,
    646                         const String16& curTag,
    647                         bool curIsStyled,
    648                         int32_t curFormat,
    649                         bool isFormatted,
    650                         const String16& product,
    651                         bool pseudolocalize,
    652                         const bool overwrite,
    653                         ResourceTable* outTable)
    654 {
    655     status_t err;
    656 
    657     String16 str;
    658     Vector<StringPool::entry_style_span> spans;
    659     err = parseStyledString(bundle, in->getPrintableSource().string(), block,
    660                             curTag, &str, curIsStyled ? &spans : NULL,
    661                             isFormatted, pseudolocalize);
    662 
    663     if (err < NO_ERROR) {
    664         return err;
    665     }
    666 
    667     /*
    668      * If a product type was specified on the command line
    669      * and also in the string, and the two are not the same,
    670      * return without adding the string.
    671      */
    672 
    673     const char *bundleProduct = bundle->getProduct();
    674     if (bundleProduct == NULL) {
    675         bundleProduct = "";
    676     }
    677 
    678     if (product.size() != 0) {
    679         /*
    680          * If the command-line-specified product is empty, only "default"
    681          * matches.  Other variants are skipped.  This is so generation
    682          * of the R.java file when the product is not known is predictable.
    683          */
    684 
    685         if (bundleProduct[0] == '\0') {
    686             if (strcmp16(String16("default").string(), product.string()) != 0) {
    687                 return NO_ERROR;
    688             }
    689         } else {
    690             /*
    691              * The command-line product is not empty.
    692              * If the product for this string is on the command-line list,
    693              * it matches.  "default" also matches, but only if nothing
    694              * else has matched already.
    695              */
    696 
    697             if (isInProductList(product, String16(bundleProduct))) {
    698                 ;
    699             } else if (strcmp16(String16("default").string(), product.string()) == 0 &&
    700                        !outTable->hasBagOrEntry(myPackage, curType, ident, config)) {
    701                 ;
    702             } else {
    703                 return NO_ERROR;
    704             }
    705         }
    706     }
    707 
    708     NOISY(printf("Adding resource entry l=%c%c c=%c%c orien=%d d=%d id=%s: %s\n",
    709                  config.language[0], config.language[1],
    710                  config.country[0], config.country[1],
    711                  config.orientation, config.density,
    712                  String8(ident).string(), String8(str).string()));
    713 
    714     err = outTable->addEntry(SourcePos(in->getPrintableSource(), block->getLineNumber()),
    715                              myPackage, curType, ident, str, &spans, &config,
    716                              false, curFormat, overwrite);
    717 
    718     return err;
    719 }
    720 
    721 status_t compileResourceFile(Bundle* bundle,
    722                              const sp<AaptAssets>& assets,
    723                              const sp<AaptFile>& in,
    724                              const ResTable_config& defParams,
    725                              const bool overwrite,
    726                              ResourceTable* outTable)
    727 {
    728     ResXMLTree block;
    729     status_t err = parseXMLResource(in, &block, false, true);
    730     if (err != NO_ERROR) {
    731         return err;
    732     }
    733 
    734     // Top-level tag.
    735     const String16 resources16("resources");
    736 
    737     // Identifier declaration tags.
    738     const String16 declare_styleable16("declare-styleable");
    739     const String16 attr16("attr");
    740 
    741     // Data creation organizational tags.
    742     const String16 string16("string");
    743     const String16 drawable16("drawable");
    744     const String16 color16("color");
    745     const String16 bool16("bool");
    746     const String16 integer16("integer");
    747     const String16 dimen16("dimen");
    748     const String16 fraction16("fraction");
    749     const String16 style16("style");
    750     const String16 plurals16("plurals");
    751     const String16 array16("array");
    752     const String16 string_array16("string-array");
    753     const String16 integer_array16("integer-array");
    754     const String16 public16("public");
    755     const String16 public_padding16("public-padding");
    756     const String16 private_symbols16("private-symbols");
    757     const String16 java_symbol16("java-symbol");
    758     const String16 add_resource16("add-resource");
    759     const String16 skip16("skip");
    760     const String16 eat_comment16("eat-comment");
    761 
    762     // Data creation tags.
    763     const String16 bag16("bag");
    764     const String16 item16("item");
    765 
    766     // Attribute type constants.
    767     const String16 enum16("enum");
    768 
    769     // plural values
    770     const String16 other16("other");
    771     const String16 quantityOther16("^other");
    772     const String16 zero16("zero");
    773     const String16 quantityZero16("^zero");
    774     const String16 one16("one");
    775     const String16 quantityOne16("^one");
    776     const String16 two16("two");
    777     const String16 quantityTwo16("^two");
    778     const String16 few16("few");
    779     const String16 quantityFew16("^few");
    780     const String16 many16("many");
    781     const String16 quantityMany16("^many");
    782 
    783     // useful attribute names and special values
    784     const String16 name16("name");
    785     const String16 translatable16("translatable");
    786     const String16 formatted16("formatted");
    787     const String16 false16("false");
    788 
    789     const String16 myPackage(assets->getPackage());
    790 
    791     bool hasErrors = false;
    792 
    793     bool fileIsTranslatable = true;
    794     if (strstr(in->getPrintableSource().string(), "donottranslate") != NULL) {
    795         fileIsTranslatable = false;
    796     }
    797 
    798     DefaultKeyedVector<String16, uint32_t> nextPublicId(0);
    799 
    800     ResXMLTree::event_code_t code;
    801     do {
    802         code = block.next();
    803     } while (code == ResXMLTree::START_NAMESPACE);
    804 
    805     size_t len;
    806     if (code != ResXMLTree::START_TAG) {
    807         SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
    808                 "No start tag found\n");
    809         return UNKNOWN_ERROR;
    810     }
    811     if (strcmp16(block.getElementName(&len), resources16.string()) != 0) {
    812         SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
    813                 "Invalid start tag %s\n", String8(block.getElementName(&len)).string());
    814         return UNKNOWN_ERROR;
    815     }
    816 
    817     ResTable_config curParams(defParams);
    818 
    819     ResTable_config pseudoParams(curParams);
    820         pseudoParams.language[0] = 'z';
    821         pseudoParams.language[1] = 'z';
    822         pseudoParams.country[0] = 'Z';
    823         pseudoParams.country[1] = 'Z';
    824 
    825     while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
    826         if (code == ResXMLTree::START_TAG) {
    827             const String16* curTag = NULL;
    828             String16 curType;
    829             int32_t curFormat = ResTable_map::TYPE_ANY;
    830             bool curIsBag = false;
    831             bool curIsBagReplaceOnOverwrite = false;
    832             bool curIsStyled = false;
    833             bool curIsPseudolocalizable = false;
    834             bool curIsFormatted = fileIsTranslatable;
    835             bool localHasErrors = false;
    836 
    837             if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
    838                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
    839                         && code != ResXMLTree::BAD_DOCUMENT) {
    840                     if (code == ResXMLTree::END_TAG) {
    841                         if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
    842                             break;
    843                         }
    844                     }
    845                 }
    846                 continue;
    847 
    848             } else if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
    849                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
    850                         && code != ResXMLTree::BAD_DOCUMENT) {
    851                     if (code == ResXMLTree::END_TAG) {
    852                         if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
    853                             break;
    854                         }
    855                     }
    856                 }
    857                 continue;
    858 
    859             } else if (strcmp16(block.getElementName(&len), public16.string()) == 0) {
    860                 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
    861 
    862                 String16 type;
    863                 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
    864                 if (typeIdx < 0) {
    865                     srcPos.error("A 'type' attribute is required for <public>\n");
    866                     hasErrors = localHasErrors = true;
    867                 }
    868                 type = String16(block.getAttributeStringValue(typeIdx, &len));
    869 
    870                 String16 name;
    871                 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
    872                 if (nameIdx < 0) {
    873                     srcPos.error("A 'name' attribute is required for <public>\n");
    874                     hasErrors = localHasErrors = true;
    875                 }
    876                 name = String16(block.getAttributeStringValue(nameIdx, &len));
    877 
    878                 uint32_t ident = 0;
    879                 ssize_t identIdx = block.indexOfAttribute(NULL, "id");
    880                 if (identIdx >= 0) {
    881                     const char16_t* identStr = block.getAttributeStringValue(identIdx, &len);
    882                     Res_value identValue;
    883                     if (!ResTable::stringToInt(identStr, len, &identValue)) {
    884                         srcPos.error("Given 'id' attribute is not an integer: %s\n",
    885                                 String8(block.getAttributeStringValue(identIdx, &len)).string());
    886                         hasErrors = localHasErrors = true;
    887                     } else {
    888                         ident = identValue.data;
    889                         nextPublicId.replaceValueFor(type, ident+1);
    890                     }
    891                 } else if (nextPublicId.indexOfKey(type) < 0) {
    892                     srcPos.error("No 'id' attribute supplied <public>,"
    893                             " and no previous id defined in this file.\n");
    894                     hasErrors = localHasErrors = true;
    895                 } else if (!localHasErrors) {
    896                     ident = nextPublicId.valueFor(type);
    897                     nextPublicId.replaceValueFor(type, ident+1);
    898                 }
    899 
    900                 if (!localHasErrors) {
    901                     err = outTable->addPublic(srcPos, myPackage, type, name, ident);
    902                     if (err < NO_ERROR) {
    903                         hasErrors = localHasErrors = true;
    904                     }
    905                 }
    906                 if (!localHasErrors) {
    907                     sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
    908                     if (symbols != NULL) {
    909                         symbols = symbols->addNestedSymbol(String8(type), srcPos);
    910                     }
    911                     if (symbols != NULL) {
    912                         symbols->makeSymbolPublic(String8(name), srcPos);
    913                         String16 comment(
    914                             block.getComment(&len) ? block.getComment(&len) : nulStr);
    915                         symbols->appendComment(String8(name), comment, srcPos);
    916                     } else {
    917                         srcPos.error("Unable to create symbols!\n");
    918                         hasErrors = localHasErrors = true;
    919                     }
    920                 }
    921 
    922                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
    923                     if (code == ResXMLTree::END_TAG) {
    924                         if (strcmp16(block.getElementName(&len), public16.string()) == 0) {
    925                             break;
    926                         }
    927                     }
    928                 }
    929                 continue;
    930 
    931             } else if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) {
    932                 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
    933 
    934                 String16 type;
    935                 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
    936                 if (typeIdx < 0) {
    937                     srcPos.error("A 'type' attribute is required for <public-padding>\n");
    938                     hasErrors = localHasErrors = true;
    939                 }
    940                 type = String16(block.getAttributeStringValue(typeIdx, &len));
    941 
    942                 String16 name;
    943                 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
    944                 if (nameIdx < 0) {
    945                     srcPos.error("A 'name' attribute is required for <public-padding>\n");
    946                     hasErrors = localHasErrors = true;
    947                 }
    948                 name = String16(block.getAttributeStringValue(nameIdx, &len));
    949 
    950                 uint32_t start = 0;
    951                 ssize_t startIdx = block.indexOfAttribute(NULL, "start");
    952                 if (startIdx >= 0) {
    953                     const char16_t* startStr = block.getAttributeStringValue(startIdx, &len);
    954                     Res_value startValue;
    955                     if (!ResTable::stringToInt(startStr, len, &startValue)) {
    956                         srcPos.error("Given 'start' attribute is not an integer: %s\n",
    957                                 String8(block.getAttributeStringValue(startIdx, &len)).string());
    958                         hasErrors = localHasErrors = true;
    959                     } else {
    960                         start = startValue.data;
    961                     }
    962                 } else if (nextPublicId.indexOfKey(type) < 0) {
    963                     srcPos.error("No 'start' attribute supplied <public-padding>,"
    964                             " and no previous id defined in this file.\n");
    965                     hasErrors = localHasErrors = true;
    966                 } else if (!localHasErrors) {
    967                     start = nextPublicId.valueFor(type);
    968                 }
    969 
    970                 uint32_t end = 0;
    971                 ssize_t endIdx = block.indexOfAttribute(NULL, "end");
    972                 if (endIdx >= 0) {
    973                     const char16_t* endStr = block.getAttributeStringValue(endIdx, &len);
    974                     Res_value endValue;
    975                     if (!ResTable::stringToInt(endStr, len, &endValue)) {
    976                         srcPos.error("Given 'end' attribute is not an integer: %s\n",
    977                                 String8(block.getAttributeStringValue(endIdx, &len)).string());
    978                         hasErrors = localHasErrors = true;
    979                     } else {
    980                         end = endValue.data;
    981                     }
    982                 } else {
    983                     srcPos.error("No 'end' attribute supplied <public-padding>\n");
    984                     hasErrors = localHasErrors = true;
    985                 }
    986 
    987                 if (end >= start) {
    988                     nextPublicId.replaceValueFor(type, end+1);
    989                 } else {
    990                     srcPos.error("Padding start '%ul' is after end '%ul'\n",
    991                             start, end);
    992                     hasErrors = localHasErrors = true;
    993                 }
    994 
    995                 String16 comment(
    996                     block.getComment(&len) ? block.getComment(&len) : nulStr);
    997                 for (uint32_t curIdent=start; curIdent<=end; curIdent++) {
    998                     if (localHasErrors) {
    999                         break;
   1000                     }
   1001                     String16 curName(name);
   1002                     char buf[64];
   1003                     sprintf(buf, "%d", (int)(end-curIdent+1));
   1004                     curName.append(String16(buf));
   1005 
   1006                     err = outTable->addEntry(srcPos, myPackage, type, curName,
   1007                                              String16("padding"), NULL, &curParams, false,
   1008                                              ResTable_map::TYPE_STRING, overwrite);
   1009                     if (err < NO_ERROR) {
   1010                         hasErrors = localHasErrors = true;
   1011                         break;
   1012                     }
   1013                     err = outTable->addPublic(srcPos, myPackage, type,
   1014                             curName, curIdent);
   1015                     if (err < NO_ERROR) {
   1016                         hasErrors = localHasErrors = true;
   1017                         break;
   1018                     }
   1019                     sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
   1020                     if (symbols != NULL) {
   1021                         symbols = symbols->addNestedSymbol(String8(type), srcPos);
   1022                     }
   1023                     if (symbols != NULL) {
   1024                         symbols->makeSymbolPublic(String8(curName), srcPos);
   1025                         symbols->appendComment(String8(curName), comment, srcPos);
   1026                     } else {
   1027                         srcPos.error("Unable to create symbols!\n");
   1028                         hasErrors = localHasErrors = true;
   1029                     }
   1030                 }
   1031 
   1032                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   1033                     if (code == ResXMLTree::END_TAG) {
   1034                         if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) {
   1035                             break;
   1036                         }
   1037                     }
   1038                 }
   1039                 continue;
   1040 
   1041             } else if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
   1042                 String16 pkg;
   1043                 ssize_t pkgIdx = block.indexOfAttribute(NULL, "package");
   1044                 if (pkgIdx < 0) {
   1045                     SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1046                             "A 'package' attribute is required for <private-symbols>\n");
   1047                     hasErrors = localHasErrors = true;
   1048                 }
   1049                 pkg = String16(block.getAttributeStringValue(pkgIdx, &len));
   1050                 if (!localHasErrors) {
   1051                     assets->setSymbolsPrivatePackage(String8(pkg));
   1052                 }
   1053 
   1054                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   1055                     if (code == ResXMLTree::END_TAG) {
   1056                         if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
   1057                             break;
   1058                         }
   1059                     }
   1060                 }
   1061                 continue;
   1062 
   1063             } else if (strcmp16(block.getElementName(&len), java_symbol16.string()) == 0) {
   1064                 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
   1065 
   1066                 String16 type;
   1067                 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
   1068                 if (typeIdx < 0) {
   1069                     srcPos.error("A 'type' attribute is required for <public>\n");
   1070                     hasErrors = localHasErrors = true;
   1071                 }
   1072                 type = String16(block.getAttributeStringValue(typeIdx, &len));
   1073 
   1074                 String16 name;
   1075                 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
   1076                 if (nameIdx < 0) {
   1077                     srcPos.error("A 'name' attribute is required for <public>\n");
   1078                     hasErrors = localHasErrors = true;
   1079                 }
   1080                 name = String16(block.getAttributeStringValue(nameIdx, &len));
   1081 
   1082                 sp<AaptSymbols> symbols = assets->getJavaSymbolsFor(String8("R"));
   1083                 if (symbols != NULL) {
   1084                     symbols = symbols->addNestedSymbol(String8(type), srcPos);
   1085                 }
   1086                 if (symbols != NULL) {
   1087                     symbols->makeSymbolJavaSymbol(String8(name), srcPos);
   1088                     String16 comment(
   1089                         block.getComment(&len) ? block.getComment(&len) : nulStr);
   1090                     symbols->appendComment(String8(name), comment, srcPos);
   1091                 } else {
   1092                     srcPos.error("Unable to create symbols!\n");
   1093                     hasErrors = localHasErrors = true;
   1094                 }
   1095 
   1096                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   1097                     if (code == ResXMLTree::END_TAG) {
   1098                         if (strcmp16(block.getElementName(&len), java_symbol16.string()) == 0) {
   1099                             break;
   1100                         }
   1101                     }
   1102                 }
   1103                 continue;
   1104 
   1105 
   1106             } else if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) {
   1107                 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
   1108 
   1109                 String16 typeName;
   1110                 ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
   1111                 if (typeIdx < 0) {
   1112                     srcPos.error("A 'type' attribute is required for <add-resource>\n");
   1113                     hasErrors = localHasErrors = true;
   1114                 }
   1115                 typeName = String16(block.getAttributeStringValue(typeIdx, &len));
   1116 
   1117                 String16 name;
   1118                 ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
   1119                 if (nameIdx < 0) {
   1120                     srcPos.error("A 'name' attribute is required for <add-resource>\n");
   1121                     hasErrors = localHasErrors = true;
   1122                 }
   1123                 name = String16(block.getAttributeStringValue(nameIdx, &len));
   1124 
   1125                 outTable->canAddEntry(srcPos, myPackage, typeName, name);
   1126 
   1127                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   1128                     if (code == ResXMLTree::END_TAG) {
   1129                         if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) {
   1130                             break;
   1131                         }
   1132                     }
   1133                 }
   1134                 continue;
   1135 
   1136             } else if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
   1137                 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
   1138 
   1139                 String16 ident;
   1140                 ssize_t identIdx = block.indexOfAttribute(NULL, "name");
   1141                 if (identIdx < 0) {
   1142                     srcPos.error("A 'name' attribute is required for <declare-styleable>\n");
   1143                     hasErrors = localHasErrors = true;
   1144                 }
   1145                 ident = String16(block.getAttributeStringValue(identIdx, &len));
   1146 
   1147                 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
   1148                 if (!localHasErrors) {
   1149                     if (symbols != NULL) {
   1150                         symbols = symbols->addNestedSymbol(String8("styleable"), srcPos);
   1151                     }
   1152                     sp<AaptSymbols> styleSymbols = symbols;
   1153                     if (symbols != NULL) {
   1154                         symbols = symbols->addNestedSymbol(String8(ident), srcPos);
   1155                     }
   1156                     if (symbols == NULL) {
   1157                         srcPos.error("Unable to create symbols!\n");
   1158                         return UNKNOWN_ERROR;
   1159                     }
   1160 
   1161                     String16 comment(
   1162                         block.getComment(&len) ? block.getComment(&len) : nulStr);
   1163                     styleSymbols->appendComment(String8(ident), comment, srcPos);
   1164                 } else {
   1165                     symbols = NULL;
   1166                 }
   1167 
   1168                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
   1169                     if (code == ResXMLTree::START_TAG) {
   1170                         if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
   1171                             while ((code=block.next()) != ResXMLTree::END_DOCUMENT
   1172                                    && code != ResXMLTree::BAD_DOCUMENT) {
   1173                                 if (code == ResXMLTree::END_TAG) {
   1174                                     if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
   1175                                         break;
   1176                                     }
   1177                                 }
   1178                             }
   1179                             continue;
   1180                         } else if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
   1181                             while ((code=block.next()) != ResXMLTree::END_DOCUMENT
   1182                                    && code != ResXMLTree::BAD_DOCUMENT) {
   1183                                 if (code == ResXMLTree::END_TAG) {
   1184                                     if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
   1185                                         break;
   1186                                     }
   1187                                 }
   1188                             }
   1189                             continue;
   1190                         } else if (strcmp16(block.getElementName(&len), attr16.string()) != 0) {
   1191                             SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1192                                     "Tag <%s> can not appear inside <declare-styleable>, only <attr>\n",
   1193                                     String8(block.getElementName(&len)).string());
   1194                             return UNKNOWN_ERROR;
   1195                         }
   1196 
   1197                         String16 comment(
   1198                             block.getComment(&len) ? block.getComment(&len) : nulStr);
   1199                         String16 itemIdent;
   1200                         err = compileAttribute(in, block, myPackage, outTable, &itemIdent, true);
   1201                         if (err != NO_ERROR) {
   1202                             hasErrors = localHasErrors = true;
   1203                         }
   1204 
   1205                         if (symbols != NULL) {
   1206                             SourcePos srcPos(String8(in->getPrintableSource()), block.getLineNumber());
   1207                             symbols->addSymbol(String8(itemIdent), 0, srcPos);
   1208                             symbols->appendComment(String8(itemIdent), comment, srcPos);
   1209                             //printf("Attribute %s comment: %s\n", String8(itemIdent).string(),
   1210                             //     String8(comment).string());
   1211                         }
   1212                     } else if (code == ResXMLTree::END_TAG) {
   1213                         if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
   1214                             break;
   1215                         }
   1216 
   1217                         SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1218                                 "Found tag </%s> where </attr> is expected\n",
   1219                                 String8(block.getElementName(&len)).string());
   1220                         return UNKNOWN_ERROR;
   1221                     }
   1222                 }
   1223                 continue;
   1224 
   1225             } else if (strcmp16(block.getElementName(&len), attr16.string()) == 0) {
   1226                 err = compileAttribute(in, block, myPackage, outTable, NULL);
   1227                 if (err != NO_ERROR) {
   1228                     hasErrors = true;
   1229                 }
   1230                 continue;
   1231 
   1232             } else if (strcmp16(block.getElementName(&len), item16.string()) == 0) {
   1233                 curTag = &item16;
   1234                 ssize_t attri = block.indexOfAttribute(NULL, "type");
   1235                 if (attri >= 0) {
   1236                     curType = String16(block.getAttributeStringValue(attri, &len));
   1237                     ssize_t formatIdx = block.indexOfAttribute(NULL, "format");
   1238                     if (formatIdx >= 0) {
   1239                         String16 formatStr = String16(block.getAttributeStringValue(
   1240                                 formatIdx, &len));
   1241                         curFormat = parse_flags(formatStr.string(), formatStr.size(),
   1242                                                 gFormatFlags);
   1243                         if (curFormat == 0) {
   1244                             SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1245                                     "Tag <item> 'format' attribute value \"%s\" not valid\n",
   1246                                     String8(formatStr).string());
   1247                             hasErrors = localHasErrors = true;
   1248                         }
   1249                     }
   1250                 } else {
   1251                     SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1252                             "A 'type' attribute is required for <item>\n");
   1253                     hasErrors = localHasErrors = true;
   1254                 }
   1255                 curIsStyled = true;
   1256             } else if (strcmp16(block.getElementName(&len), string16.string()) == 0) {
   1257                 // Note the existence and locale of every string we process
   1258                 char rawLocale[16];
   1259                 curParams.getLocale(rawLocale);
   1260                 String8 locale(rawLocale);
   1261                 String16 name;
   1262                 String16 translatable;
   1263                 String16 formatted;
   1264 
   1265                 size_t n = block.getAttributeCount();
   1266                 for (size_t i = 0; i < n; i++) {
   1267                     size_t length;
   1268                     const uint16_t* attr = block.getAttributeName(i, &length);
   1269                     if (strcmp16(attr, name16.string()) == 0) {
   1270                         name.setTo(block.getAttributeStringValue(i, &length));
   1271                     } else if (strcmp16(attr, translatable16.string()) == 0) {
   1272                         translatable.setTo(block.getAttributeStringValue(i, &length));
   1273                     } else if (strcmp16(attr, formatted16.string()) == 0) {
   1274                         formatted.setTo(block.getAttributeStringValue(i, &length));
   1275                     }
   1276                 }
   1277 
   1278                 if (name.size() > 0) {
   1279                     if (translatable == false16) {
   1280                         curIsFormatted = false;
   1281                         // Untranslatable strings must only exist in the default [empty] locale
   1282                         if (locale.size() > 0) {
   1283                             fprintf(stderr, "aapt: warning: string '%s' in %s marked untranslatable but exists"
   1284                                     " in locale '%s'\n", String8(name).string(),
   1285                                     bundle->getResourceSourceDirs()[0],
   1286                                     locale.string());
   1287                             // hasErrors = localHasErrors = true;
   1288                         } else {
   1289                             // Intentionally empty block:
   1290                             //
   1291                             // Don't add untranslatable strings to the localization table; that
   1292                             // way if we later see localizations of them, they'll be flagged as
   1293                             // having no default translation.
   1294                         }
   1295                     } else {
   1296                         outTable->addLocalization(name, locale);
   1297                     }
   1298 
   1299                     if (formatted == false16) {
   1300                         curIsFormatted = false;
   1301                     }
   1302                 }
   1303 
   1304                 curTag = &string16;
   1305                 curType = string16;
   1306                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
   1307                 curIsStyled = true;
   1308                 curIsPseudolocalizable = true;
   1309             } else if (strcmp16(block.getElementName(&len), drawable16.string()) == 0) {
   1310                 curTag = &drawable16;
   1311                 curType = drawable16;
   1312                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR;
   1313             } else if (strcmp16(block.getElementName(&len), color16.string()) == 0) {
   1314                 curTag = &color16;
   1315                 curType = color16;
   1316                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR;
   1317             } else if (strcmp16(block.getElementName(&len), bool16.string()) == 0) {
   1318                 curTag = &bool16;
   1319                 curType = bool16;
   1320                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_BOOLEAN;
   1321             } else if (strcmp16(block.getElementName(&len), integer16.string()) == 0) {
   1322                 curTag = &integer16;
   1323                 curType = integer16;
   1324                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
   1325             } else if (strcmp16(block.getElementName(&len), dimen16.string()) == 0) {
   1326                 curTag = &dimen16;
   1327                 curType = dimen16;
   1328                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_DIMENSION;
   1329             } else if (strcmp16(block.getElementName(&len), fraction16.string()) == 0) {
   1330                 curTag = &fraction16;
   1331                 curType = fraction16;
   1332                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_FRACTION;
   1333             } else if (strcmp16(block.getElementName(&len), bag16.string()) == 0) {
   1334                 curTag = &bag16;
   1335                 curIsBag = true;
   1336                 ssize_t attri = block.indexOfAttribute(NULL, "type");
   1337                 if (attri >= 0) {
   1338                     curType = String16(block.getAttributeStringValue(attri, &len));
   1339                 } else {
   1340                     SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1341                             "A 'type' attribute is required for <bag>\n");
   1342                     hasErrors = localHasErrors = true;
   1343                 }
   1344             } else if (strcmp16(block.getElementName(&len), style16.string()) == 0) {
   1345                 curTag = &style16;
   1346                 curType = style16;
   1347                 curIsBag = true;
   1348             } else if (strcmp16(block.getElementName(&len), plurals16.string()) == 0) {
   1349                 curTag = &plurals16;
   1350                 curType = plurals16;
   1351                 curIsBag = true;
   1352             } else if (strcmp16(block.getElementName(&len), array16.string()) == 0) {
   1353                 curTag = &array16;
   1354                 curType = array16;
   1355                 curIsBag = true;
   1356                 curIsBagReplaceOnOverwrite = true;
   1357                 ssize_t formatIdx = block.indexOfAttribute(NULL, "format");
   1358                 if (formatIdx >= 0) {
   1359                     String16 formatStr = String16(block.getAttributeStringValue(
   1360                             formatIdx, &len));
   1361                     curFormat = parse_flags(formatStr.string(), formatStr.size(),
   1362                                             gFormatFlags);
   1363                     if (curFormat == 0) {
   1364                         SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1365                                 "Tag <array> 'format' attribute value \"%s\" not valid\n",
   1366                                 String8(formatStr).string());
   1367                         hasErrors = localHasErrors = true;
   1368                     }
   1369                 }
   1370             } else if (strcmp16(block.getElementName(&len), string_array16.string()) == 0) {
   1371                 // Check whether these strings need valid formats.
   1372                 // (simplified form of what string16 does above)
   1373                 size_t n = block.getAttributeCount();
   1374                 for (size_t i = 0; i < n; i++) {
   1375                     size_t length;
   1376                     const uint16_t* attr = block.getAttributeName(i, &length);
   1377                     if (strcmp16(attr, translatable16.string()) == 0
   1378                             || strcmp16(attr, formatted16.string()) == 0) {
   1379                         const uint16_t* value = block.getAttributeStringValue(i, &length);
   1380                         if (strcmp16(value, false16.string()) == 0) {
   1381                             curIsFormatted = false;
   1382                             break;
   1383                         }
   1384                     }
   1385                 }
   1386 
   1387                 curTag = &string_array16;
   1388                 curType = array16;
   1389                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
   1390                 curIsBag = true;
   1391                 curIsBagReplaceOnOverwrite = true;
   1392                 curIsPseudolocalizable = true;
   1393             } else if (strcmp16(block.getElementName(&len), integer_array16.string()) == 0) {
   1394                 curTag = &integer_array16;
   1395                 curType = array16;
   1396                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
   1397                 curIsBag = true;
   1398                 curIsBagReplaceOnOverwrite = true;
   1399             } else {
   1400                 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1401                         "Found tag %s where item is expected\n",
   1402                         String8(block.getElementName(&len)).string());
   1403                 return UNKNOWN_ERROR;
   1404             }
   1405 
   1406             String16 ident;
   1407             ssize_t identIdx = block.indexOfAttribute(NULL, "name");
   1408             if (identIdx >= 0) {
   1409                 ident = String16(block.getAttributeStringValue(identIdx, &len));
   1410             } else {
   1411                 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1412                         "A 'name' attribute is required for <%s>\n",
   1413                         String8(*curTag).string());
   1414                 hasErrors = localHasErrors = true;
   1415             }
   1416 
   1417             String16 product;
   1418             identIdx = block.indexOfAttribute(NULL, "product");
   1419             if (identIdx >= 0) {
   1420                 product = String16(block.getAttributeStringValue(identIdx, &len));
   1421             }
   1422 
   1423             String16 comment(block.getComment(&len) ? block.getComment(&len) : nulStr);
   1424 
   1425             if (curIsBag) {
   1426                 // Figure out the parent of this bag...
   1427                 String16 parentIdent;
   1428                 ssize_t parentIdentIdx = block.indexOfAttribute(NULL, "parent");
   1429                 if (parentIdentIdx >= 0) {
   1430                     parentIdent = String16(block.getAttributeStringValue(parentIdentIdx, &len));
   1431                 } else {
   1432                     ssize_t sep = ident.findLast('.');
   1433                     if (sep >= 0) {
   1434                         parentIdent.setTo(ident, sep);
   1435                     }
   1436                 }
   1437 
   1438                 if (!localHasErrors) {
   1439                     err = outTable->startBag(SourcePos(in->getPrintableSource(),
   1440                             block.getLineNumber()), myPackage, curType, ident,
   1441                             parentIdent, &curParams,
   1442                             overwrite, curIsBagReplaceOnOverwrite);
   1443                     if (err != NO_ERROR) {
   1444                         hasErrors = localHasErrors = true;
   1445                     }
   1446                 }
   1447 
   1448                 ssize_t elmIndex = 0;
   1449                 char elmIndexStr[14];
   1450                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
   1451                         && code != ResXMLTree::BAD_DOCUMENT) {
   1452 
   1453                     if (code == ResXMLTree::START_TAG) {
   1454                         if (strcmp16(block.getElementName(&len), item16.string()) != 0) {
   1455                             SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1456                                     "Tag <%s> can not appear inside <%s>, only <item>\n",
   1457                                     String8(block.getElementName(&len)).string(),
   1458                                     String8(*curTag).string());
   1459                             return UNKNOWN_ERROR;
   1460                         }
   1461 
   1462                         String16 itemIdent;
   1463                         if (curType == array16) {
   1464                             sprintf(elmIndexStr, "^index_%d", (int)elmIndex++);
   1465                             itemIdent = String16(elmIndexStr);
   1466                         } else if (curType == plurals16) {
   1467                             ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "quantity");
   1468                             if (itemIdentIdx >= 0) {
   1469                                 String16 quantity16(block.getAttributeStringValue(itemIdentIdx, &len));
   1470                                 if (quantity16 == other16) {
   1471                                     itemIdent = quantityOther16;
   1472                                 }
   1473                                 else if (quantity16 == zero16) {
   1474                                     itemIdent = quantityZero16;
   1475                                 }
   1476                                 else if (quantity16 == one16) {
   1477                                     itemIdent = quantityOne16;
   1478                                 }
   1479                                 else if (quantity16 == two16) {
   1480                                     itemIdent = quantityTwo16;
   1481                                 }
   1482                                 else if (quantity16 == few16) {
   1483                                     itemIdent = quantityFew16;
   1484                                 }
   1485                                 else if (quantity16 == many16) {
   1486                                     itemIdent = quantityMany16;
   1487                                 }
   1488                                 else {
   1489                                     SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1490                                             "Illegal 'quantity' attribute is <item> inside <plurals>\n");
   1491                                     hasErrors = localHasErrors = true;
   1492                                 }
   1493                             } else {
   1494                                 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1495                                         "A 'quantity' attribute is required for <item> inside <plurals>\n");
   1496                                 hasErrors = localHasErrors = true;
   1497                             }
   1498                         } else {
   1499                             ssize_t itemIdentIdx = block.indexOfAttribute(NULL, "name");
   1500                             if (itemIdentIdx >= 0) {
   1501                                 itemIdent = String16(block.getAttributeStringValue(itemIdentIdx, &len));
   1502                             } else {
   1503                                 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1504                                         "A 'name' attribute is required for <item>\n");
   1505                                 hasErrors = localHasErrors = true;
   1506                             }
   1507                         }
   1508 
   1509                         ResXMLParser::ResXMLPosition parserPosition;
   1510                         block.getPosition(&parserPosition);
   1511 
   1512                         err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType,
   1513                                 ident, parentIdent, itemIdent, curFormat, curIsFormatted,
   1514                                 product, false, overwrite, outTable);
   1515                         if (err == NO_ERROR) {
   1516                             if (curIsPseudolocalizable && localeIsDefined(curParams)
   1517                                     && bundle->getPseudolocalize()) {
   1518                                 // pseudolocalize here
   1519 #if 1
   1520                                 block.setPosition(parserPosition);
   1521                                 err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage,
   1522                                         curType, ident, parentIdent, itemIdent, curFormat,
   1523                                         curIsFormatted, product, true, overwrite, outTable);
   1524 #endif
   1525                             }
   1526                         }
   1527                         if (err != NO_ERROR) {
   1528                             hasErrors = localHasErrors = true;
   1529                         }
   1530                     } else if (code == ResXMLTree::END_TAG) {
   1531                         if (strcmp16(block.getElementName(&len), curTag->string()) != 0) {
   1532                             SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1533                                     "Found tag </%s> where </%s> is expected\n",
   1534                                     String8(block.getElementName(&len)).string(),
   1535                                     String8(*curTag).string());
   1536                             return UNKNOWN_ERROR;
   1537                         }
   1538                         break;
   1539                     }
   1540                 }
   1541             } else {
   1542                 ResXMLParser::ResXMLPosition parserPosition;
   1543                 block.getPosition(&parserPosition);
   1544 
   1545                 err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident,
   1546                         *curTag, curIsStyled, curFormat, curIsFormatted,
   1547                         product, false, overwrite, outTable);
   1548 
   1549                 if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR?
   1550                     hasErrors = localHasErrors = true;
   1551                 }
   1552                 else if (err == NO_ERROR) {
   1553                     if (curIsPseudolocalizable && localeIsDefined(curParams)
   1554                             && bundle->getPseudolocalize()) {
   1555                         // pseudolocalize here
   1556                         block.setPosition(parserPosition);
   1557                         err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType,
   1558                                 ident, *curTag, curIsStyled, curFormat,
   1559                                 curIsFormatted, product,
   1560                                 true, overwrite, outTable);
   1561                         if (err != NO_ERROR) {
   1562                             hasErrors = localHasErrors = true;
   1563                         }
   1564                     }
   1565                 }
   1566             }
   1567 
   1568 #if 0
   1569             if (comment.size() > 0) {
   1570                 printf("Comment for @%s:%s/%s: %s\n", String8(myPackage).string(),
   1571                        String8(curType).string(), String8(ident).string(),
   1572                        String8(comment).string());
   1573             }
   1574 #endif
   1575             if (!localHasErrors) {
   1576                 outTable->appendComment(myPackage, curType, ident, comment, false);
   1577             }
   1578         }
   1579         else if (code == ResXMLTree::END_TAG) {
   1580             if (strcmp16(block.getElementName(&len), resources16.string()) != 0) {
   1581                 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1582                         "Unexpected end tag %s\n", String8(block.getElementName(&len)).string());
   1583                 return UNKNOWN_ERROR;
   1584             }
   1585         }
   1586         else if (code == ResXMLTree::START_NAMESPACE || code == ResXMLTree::END_NAMESPACE) {
   1587         }
   1588         else if (code == ResXMLTree::TEXT) {
   1589             if (isWhitespace(block.getText(&len))) {
   1590                 continue;
   1591             }
   1592             SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
   1593                     "Found text \"%s\" where item tag is expected\n",
   1594                     String8(block.getText(&len)).string());
   1595             return UNKNOWN_ERROR;
   1596         }
   1597     }
   1598 
   1599     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
   1600 }
   1601 
   1602 ResourceTable::ResourceTable(Bundle* bundle, const String16& assetsPackage)
   1603     : mAssetsPackage(assetsPackage), mNextPackageId(1), mHaveAppPackage(false),
   1604       mIsAppPackage(!bundle->getExtending()),
   1605       mNumLocal(0),
   1606       mBundle(bundle)
   1607 {
   1608 }
   1609 
   1610 status_t ResourceTable::addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets)
   1611 {
   1612     status_t err = assets->buildIncludedResources(bundle);
   1613     if (err != NO_ERROR) {
   1614         return err;
   1615     }
   1616 
   1617     // For future reference to included resources.
   1618     mAssets = assets;
   1619 
   1620     const ResTable& incl = assets->getIncludedResources();
   1621 
   1622     // Retrieve all the packages.
   1623     const size_t N = incl.getBasePackageCount();
   1624     for (size_t phase=0; phase<2; phase++) {
   1625         for (size_t i=0; i<N; i++) {
   1626             String16 name(incl.getBasePackageName(i));
   1627             uint32_t id = incl.getBasePackageId(i);
   1628             // First time through: only add base packages (id
   1629             // is not 0); second time through add the other
   1630             // packages.
   1631             if (phase != 0) {
   1632                 if (id != 0) {
   1633                     // Skip base packages -- already one.
   1634                     id = 0;
   1635                 } else {
   1636                     // Assign a dynamic id.
   1637                     id = mNextPackageId;
   1638                 }
   1639             } else if (id != 0) {
   1640                 if (id == 127) {
   1641                     if (mHaveAppPackage) {
   1642                         fprintf(stderr, "Included resources have two application packages!\n");
   1643                         return UNKNOWN_ERROR;
   1644                     }
   1645                     mHaveAppPackage = true;
   1646                 }
   1647                 if (mNextPackageId > id) {
   1648                     fprintf(stderr, "Included base package ID %d already in use!\n", id);
   1649                     return UNKNOWN_ERROR;
   1650                 }
   1651             }
   1652             if (id != 0) {
   1653                 NOISY(printf("Including package %s with ID=%d\n",
   1654                              String8(name).string(), id));
   1655                 sp<Package> p = new Package(name, id);
   1656                 mPackages.add(name, p);
   1657                 mOrderedPackages.add(p);
   1658 
   1659                 if (id >= mNextPackageId) {
   1660                     mNextPackageId = id+1;
   1661                 }
   1662             }
   1663         }
   1664     }
   1665 
   1666     // Every resource table always has one first entry, the bag attributes.
   1667     const SourcePos unknown(String8("????"), 0);
   1668     sp<Type> attr = getType(mAssetsPackage, String16("attr"), unknown);
   1669 
   1670     return NO_ERROR;
   1671 }
   1672 
   1673 status_t ResourceTable::addPublic(const SourcePos& sourcePos,
   1674                                   const String16& package,
   1675                                   const String16& type,
   1676                                   const String16& name,
   1677                                   const uint32_t ident)
   1678 {
   1679     uint32_t rid = mAssets->getIncludedResources()
   1680         .identifierForName(name.string(), name.size(),
   1681                            type.string(), type.size(),
   1682                            package.string(), package.size());
   1683     if (rid != 0) {
   1684         sourcePos.error("Error declaring public resource %s/%s for included package %s\n",
   1685                 String8(type).string(), String8(name).string(),
   1686                 String8(package).string());
   1687         return UNKNOWN_ERROR;
   1688     }
   1689 
   1690     sp<Type> t = getType(package, type, sourcePos);
   1691     if (t == NULL) {
   1692         return UNKNOWN_ERROR;
   1693     }
   1694     return t->addPublic(sourcePos, name, ident);
   1695 }
   1696 
   1697 status_t ResourceTable::addEntry(const SourcePos& sourcePos,
   1698                                  const String16& package,
   1699                                  const String16& type,
   1700                                  const String16& name,
   1701                                  const String16& value,
   1702                                  const Vector<StringPool::entry_style_span>* style,
   1703                                  const ResTable_config* params,
   1704                                  const bool doSetIndex,
   1705                                  const int32_t format,
   1706                                  const bool overwrite)
   1707 {
   1708     // Check for adding entries in other packages...  for now we do
   1709     // nothing.  We need to do the right thing here to support skinning.
   1710     uint32_t rid = mAssets->getIncludedResources()
   1711         .identifierForName(name.string(), name.size(),
   1712                            type.string(), type.size(),
   1713                            package.string(), package.size());
   1714     if (rid != 0) {
   1715         return NO_ERROR;
   1716     }
   1717 
   1718 #if 0
   1719     if (name == String16("left")) {
   1720         printf("Adding entry left: file=%s, line=%d, type=%s, value=%s\n",
   1721                sourcePos.file.string(), sourcePos.line, String8(type).string(),
   1722                String8(value).string());
   1723     }
   1724 #endif
   1725 
   1726     sp<Entry> e = getEntry(package, type, name, sourcePos, overwrite,
   1727                            params, doSetIndex);
   1728     if (e == NULL) {
   1729         return UNKNOWN_ERROR;
   1730     }
   1731     status_t err = e->setItem(sourcePos, value, style, format, overwrite);
   1732     if (err == NO_ERROR) {
   1733         mNumLocal++;
   1734     }
   1735     return err;
   1736 }
   1737 
   1738 status_t ResourceTable::startBag(const SourcePos& sourcePos,
   1739                                  const String16& package,
   1740                                  const String16& type,
   1741                                  const String16& name,
   1742                                  const String16& bagParent,
   1743                                  const ResTable_config* params,
   1744                                  bool overlay,
   1745                                  bool replace, bool isId)
   1746 {
   1747     status_t result = NO_ERROR;
   1748 
   1749     // Check for adding entries in other packages...  for now we do
   1750     // nothing.  We need to do the right thing here to support skinning.
   1751     uint32_t rid = mAssets->getIncludedResources()
   1752     .identifierForName(name.string(), name.size(),
   1753                        type.string(), type.size(),
   1754                        package.string(), package.size());
   1755     if (rid != 0) {
   1756         return NO_ERROR;
   1757     }
   1758 
   1759 #if 0
   1760     if (name == String16("left")) {
   1761         printf("Adding bag left: file=%s, line=%d, type=%s\n",
   1762                sourcePos.file.striing(), sourcePos.line, String8(type).string());
   1763     }
   1764 #endif
   1765     if (overlay && !mBundle->getAutoAddOverlay() && !hasBagOrEntry(package, type, name)) {
   1766         bool canAdd = false;
   1767         sp<Package> p = mPackages.valueFor(package);
   1768         if (p != NULL) {
   1769             sp<Type> t = p->getTypes().valueFor(type);
   1770             if (t != NULL) {
   1771                 if (t->getCanAddEntries().indexOf(name) >= 0) {
   1772                     canAdd = true;
   1773                 }
   1774             }
   1775         }
   1776         if (!canAdd) {
   1777             sourcePos.error("Resource does not already exist in overlay at '%s'; use <add-resource> to add.\n",
   1778                             String8(name).string());
   1779             return UNKNOWN_ERROR;
   1780         }
   1781     }
   1782     sp<Entry> e = getEntry(package, type, name, sourcePos, overlay, params);
   1783     if (e == NULL) {
   1784         return UNKNOWN_ERROR;
   1785     }
   1786 
   1787     // If a parent is explicitly specified, set it.
   1788     if (bagParent.size() > 0) {
   1789         e->setParent(bagParent);
   1790     }
   1791 
   1792     if ((result = e->makeItABag(sourcePos)) != NO_ERROR) {
   1793         return result;
   1794     }
   1795 
   1796     if (overlay && replace) {
   1797         return e->emptyBag(sourcePos);
   1798     }
   1799     return result;
   1800 }
   1801 
   1802 status_t ResourceTable::addBag(const SourcePos& sourcePos,
   1803                                const String16& package,
   1804                                const String16& type,
   1805                                const String16& name,
   1806                                const String16& bagParent,
   1807                                const String16& bagKey,
   1808                                const String16& value,
   1809                                const Vector<StringPool::entry_style_span>* style,
   1810                                const ResTable_config* params,
   1811                                bool replace, bool isId, const int32_t format)
   1812 {
   1813     // Check for adding entries in other packages...  for now we do
   1814     // nothing.  We need to do the right thing here to support skinning.
   1815     uint32_t rid = mAssets->getIncludedResources()
   1816         .identifierForName(name.string(), name.size(),
   1817                            type.string(), type.size(),
   1818                            package.string(), package.size());
   1819     if (rid != 0) {
   1820         return NO_ERROR;
   1821     }
   1822 
   1823 #if 0
   1824     if (name == String16("left")) {
   1825         printf("Adding bag left: file=%s, line=%d, type=%s\n",
   1826                sourcePos.file.striing(), sourcePos.line, String8(type).string());
   1827     }
   1828 #endif
   1829     sp<Entry> e = getEntry(package, type, name, sourcePos, replace, params);
   1830     if (e == NULL) {
   1831         return UNKNOWN_ERROR;
   1832     }
   1833 
   1834     // If a parent is explicitly specified, set it.
   1835     if (bagParent.size() > 0) {
   1836         e->setParent(bagParent);
   1837     }
   1838 
   1839     const bool first = e->getBag().indexOfKey(bagKey) < 0;
   1840     status_t err = e->addToBag(sourcePos, bagKey, value, style, replace, isId, format);
   1841     if (err == NO_ERROR && first) {
   1842         mNumLocal++;
   1843     }
   1844     return err;
   1845 }
   1846 
   1847 bool ResourceTable::hasBagOrEntry(const String16& package,
   1848                                   const String16& type,
   1849                                   const String16& name) const
   1850 {
   1851     // First look for this in the included resources...
   1852     uint32_t rid = mAssets->getIncludedResources()
   1853         .identifierForName(name.string(), name.size(),
   1854                            type.string(), type.size(),
   1855                            package.string(), package.size());
   1856     if (rid != 0) {
   1857         return true;
   1858     }
   1859 
   1860     sp<Package> p = mPackages.valueFor(package);
   1861     if (p != NULL) {
   1862         sp<Type> t = p->getTypes().valueFor(type);
   1863         if (t != NULL) {
   1864             sp<ConfigList> c =  t->getConfigs().valueFor(name);
   1865             if (c != NULL) return true;
   1866         }
   1867     }
   1868 
   1869     return false;
   1870 }
   1871 
   1872 bool ResourceTable::hasBagOrEntry(const String16& package,
   1873                                   const String16& type,
   1874                                   const String16& name,
   1875                                   const ResTable_config& config) const
   1876 {
   1877     // First look for this in the included resources...
   1878     uint32_t rid = mAssets->getIncludedResources()
   1879         .identifierForName(name.string(), name.size(),
   1880                            type.string(), type.size(),
   1881                            package.string(), package.size());
   1882     if (rid != 0) {
   1883         return true;
   1884     }
   1885 
   1886     sp<Package> p = mPackages.valueFor(package);
   1887     if (p != NULL) {
   1888         sp<Type> t = p->getTypes().valueFor(type);
   1889         if (t != NULL) {
   1890             sp<ConfigList> c =  t->getConfigs().valueFor(name);
   1891             if (c != NULL) {
   1892                 sp<Entry> e = c->getEntries().valueFor(config);
   1893                 if (e != NULL) {
   1894                     return true;
   1895                 }
   1896             }
   1897         }
   1898     }
   1899 
   1900     return false;
   1901 }
   1902 
   1903 bool ResourceTable::hasBagOrEntry(const String16& ref,
   1904                                   const String16* defType,
   1905                                   const String16* defPackage)
   1906 {
   1907     String16 package, type, name;
   1908     if (!ResTable::expandResourceRef(ref.string(), ref.size(), &package, &type, &name,
   1909                 defType, defPackage ? defPackage:&mAssetsPackage, NULL)) {
   1910         return false;
   1911     }
   1912     return hasBagOrEntry(package, type, name);
   1913 }
   1914 
   1915 bool ResourceTable::appendComment(const String16& package,
   1916                                   const String16& type,
   1917                                   const String16& name,
   1918                                   const String16& comment,
   1919                                   bool onlyIfEmpty)
   1920 {
   1921     if (comment.size() <= 0) {
   1922         return true;
   1923     }
   1924 
   1925     sp<Package> p = mPackages.valueFor(package);
   1926     if (p != NULL) {
   1927         sp<Type> t = p->getTypes().valueFor(type);
   1928         if (t != NULL) {
   1929             sp<ConfigList> c =  t->getConfigs().valueFor(name);
   1930             if (c != NULL) {
   1931                 c->appendComment(comment, onlyIfEmpty);
   1932                 return true;
   1933             }
   1934         }
   1935     }
   1936     return false;
   1937 }
   1938 
   1939 bool ResourceTable::appendTypeComment(const String16& package,
   1940                                       const String16& type,
   1941                                       const String16& name,
   1942                                       const String16& comment)
   1943 {
   1944     if (comment.size() <= 0) {
   1945         return true;
   1946     }
   1947 
   1948     sp<Package> p = mPackages.valueFor(package);
   1949     if (p != NULL) {
   1950         sp<Type> t = p->getTypes().valueFor(type);
   1951         if (t != NULL) {
   1952             sp<ConfigList> c =  t->getConfigs().valueFor(name);
   1953             if (c != NULL) {
   1954                 c->appendTypeComment(comment);
   1955                 return true;
   1956             }
   1957         }
   1958     }
   1959     return false;
   1960 }
   1961 
   1962 void ResourceTable::canAddEntry(const SourcePos& pos,
   1963         const String16& package, const String16& type, const String16& name)
   1964 {
   1965     sp<Type> t = getType(package, type, pos);
   1966     if (t != NULL) {
   1967         t->canAddEntry(name);
   1968     }
   1969 }
   1970 
   1971 size_t ResourceTable::size() const {
   1972     return mPackages.size();
   1973 }
   1974 
   1975 size_t ResourceTable::numLocalResources() const {
   1976     return mNumLocal;
   1977 }
   1978 
   1979 bool ResourceTable::hasResources() const {
   1980     return mNumLocal > 0;
   1981 }
   1982 
   1983 sp<AaptFile> ResourceTable::flatten(Bundle* bundle)
   1984 {
   1985     sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
   1986     status_t err = flatten(bundle, data);
   1987     return err == NO_ERROR ? data : NULL;
   1988 }
   1989 
   1990 inline uint32_t ResourceTable::getResId(const sp<Package>& p,
   1991                                         const sp<Type>& t,
   1992                                         uint32_t nameId)
   1993 {
   1994     return makeResId(p->getAssignedId(), t->getIndex(), nameId);
   1995 }
   1996 
   1997 uint32_t ResourceTable::getResId(const String16& package,
   1998                                  const String16& type,
   1999                                  const String16& name,
   2000                                  bool onlyPublic) const
   2001 {
   2002     uint32_t id = ResourceIdCache::lookup(package, type, name, onlyPublic);
   2003     if (id != 0) return id;     // cache hit
   2004 
   2005     sp<Package> p = mPackages.valueFor(package);
   2006     if (p == NULL) return 0;
   2007 
   2008     // First look for this in the included resources...
   2009     uint32_t specFlags = 0;
   2010     uint32_t rid = mAssets->getIncludedResources()
   2011         .identifierForName(name.string(), name.size(),
   2012                            type.string(), type.size(),
   2013                            package.string(), package.size(),
   2014                            &specFlags);
   2015     if (rid != 0) {
   2016         if (onlyPublic) {
   2017             if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) {
   2018                 return 0;
   2019             }
   2020         }
   2021 
   2022         if (Res_INTERNALID(rid)) {
   2023             return ResourceIdCache::store(package, type, name, onlyPublic, rid);
   2024         }
   2025         return ResourceIdCache::store(package, type, name, onlyPublic,
   2026                 Res_MAKEID(p->getAssignedId()-1, Res_GETTYPE(rid), Res_GETENTRY(rid)));
   2027     }
   2028 
   2029     sp<Type> t = p->getTypes().valueFor(type);
   2030     if (t == NULL) return 0;
   2031     sp<ConfigList> c =  t->getConfigs().valueFor(name);
   2032     if (c == NULL) return 0;
   2033     int32_t ei = c->getEntryIndex();
   2034     if (ei < 0) return 0;
   2035 
   2036     return ResourceIdCache::store(package, type, name, onlyPublic,
   2037             getResId(p, t, ei));
   2038 }
   2039 
   2040 uint32_t ResourceTable::getResId(const String16& ref,
   2041                                  const String16* defType,
   2042                                  const String16* defPackage,
   2043                                  const char** outErrorMsg,
   2044                                  bool onlyPublic) const
   2045 {
   2046     String16 package, type, name;
   2047     bool refOnlyPublic = true;
   2048     if (!ResTable::expandResourceRef(
   2049         ref.string(), ref.size(), &package, &type, &name,
   2050         defType, defPackage ? defPackage:&mAssetsPackage,
   2051         outErrorMsg, &refOnlyPublic)) {
   2052         NOISY(printf("Expanding resource: ref=%s\n",
   2053                      String8(ref).string()));
   2054         NOISY(printf("Expanding resource: defType=%s\n",
   2055                      defType ? String8(*defType).string() : "NULL"));
   2056         NOISY(printf("Expanding resource: defPackage=%s\n",
   2057                      defPackage ? String8(*defPackage).string() : "NULL"));
   2058         NOISY(printf("Expanding resource: ref=%s\n", String8(ref).string()));
   2059         NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=0\n",
   2060                      String8(package).string(), String8(type).string(),
   2061                      String8(name).string()));
   2062         return 0;
   2063     }
   2064     uint32_t res = getResId(package, type, name, onlyPublic && refOnlyPublic);
   2065     NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n",
   2066                  String8(package).string(), String8(type).string(),
   2067                  String8(name).string(), res));
   2068     if (res == 0) {
   2069         if (outErrorMsg)
   2070             *outErrorMsg = "No resource found that matches the given name";
   2071     }
   2072     return res;
   2073 }
   2074 
   2075 bool ResourceTable::isValidResourceName(const String16& s)
   2076 {
   2077     const char16_t* p = s.string();
   2078     bool first = true;
   2079     while (*p) {
   2080         if ((*p >= 'a' && *p <= 'z')
   2081             || (*p >= 'A' && *p <= 'Z')
   2082             || *p == '_'
   2083             || (!first && *p >= '0' && *p <= '9')) {
   2084             first = false;
   2085             p++;
   2086             continue;
   2087         }
   2088         return false;
   2089     }
   2090     return true;
   2091 }
   2092 
   2093 bool ResourceTable::stringToValue(Res_value* outValue, StringPool* pool,
   2094                                   const String16& str,
   2095                                   bool preserveSpaces, bool coerceType,
   2096                                   uint32_t attrID,
   2097                                   const Vector<StringPool::entry_style_span>* style,
   2098                                   String16* outStr, void* accessorCookie,
   2099                                   uint32_t attrType, const String8* configTypeName,
   2100                                   const ConfigDescription* config)
   2101 {
   2102     String16 finalStr;
   2103 
   2104     bool res = true;
   2105     if (style == NULL || style->size() == 0) {
   2106         // Text is not styled so it can be any type...  let's figure it out.
   2107         res = mAssets->getIncludedResources()
   2108             .stringToValue(outValue, &finalStr, str.string(), str.size(), preserveSpaces,
   2109                             coerceType, attrID, NULL, &mAssetsPackage, this,
   2110                            accessorCookie, attrType);
   2111     } else {
   2112         // Styled text can only be a string, and while collecting the style
   2113         // information we have already processed that string!
   2114         outValue->size = sizeof(Res_value);
   2115         outValue->res0 = 0;
   2116         outValue->dataType = outValue->TYPE_STRING;
   2117         outValue->data = 0;
   2118         finalStr = str;
   2119     }
   2120 
   2121     if (!res) {
   2122         return false;
   2123     }
   2124 
   2125     if (outValue->dataType == outValue->TYPE_STRING) {
   2126         // Should do better merging styles.
   2127         if (pool) {
   2128             String8 configStr;
   2129             if (config != NULL) {
   2130                 configStr = config->toString();
   2131             } else {
   2132                 configStr = "(null)";
   2133             }
   2134             NOISY(printf("Adding to pool string style #%d config %s: %s\n",
   2135                     style != NULL ? style->size() : 0,
   2136                     configStr.string(), String8(finalStr).string()));
   2137             if (style != NULL && style->size() > 0) {
   2138                 outValue->data = pool->add(finalStr, *style, configTypeName, config);
   2139             } else {
   2140                 outValue->data = pool->add(finalStr, true, configTypeName, config);
   2141             }
   2142         } else {
   2143             // Caller will fill this in later.
   2144             outValue->data = 0;
   2145         }
   2146 
   2147         if (outStr) {
   2148             *outStr = finalStr;
   2149         }
   2150 
   2151     }
   2152 
   2153     return true;
   2154 }
   2155 
   2156 uint32_t ResourceTable::getCustomResource(
   2157     const String16& package, const String16& type, const String16& name) const
   2158 {
   2159     //printf("getCustomResource: %s %s %s\n", String8(package).string(),
   2160     //       String8(type).string(), String8(name).string());
   2161     sp<Package> p = mPackages.valueFor(package);
   2162     if (p == NULL) return 0;
   2163     sp<Type> t = p->getTypes().valueFor(type);
   2164     if (t == NULL) return 0;
   2165     sp<ConfigList> c =  t->getConfigs().valueFor(name);
   2166     if (c == NULL) return 0;
   2167     int32_t ei = c->getEntryIndex();
   2168     if (ei < 0) return 0;
   2169     return getResId(p, t, ei);
   2170 }
   2171 
   2172 uint32_t ResourceTable::getCustomResourceWithCreation(
   2173         const String16& package, const String16& type, const String16& name,
   2174         const bool createIfNotFound)
   2175 {
   2176     uint32_t resId = getCustomResource(package, type, name);
   2177     if (resId != 0 || !createIfNotFound) {
   2178         return resId;
   2179     }
   2180     String16 value("false");
   2181 
   2182     status_t status = addEntry(mCurrentXmlPos, package, type, name, value, NULL, NULL, true);
   2183     if (status == NO_ERROR) {
   2184         resId = getResId(package, type, name);
   2185         return resId;
   2186     }
   2187     return 0;
   2188 }
   2189 
   2190 uint32_t ResourceTable::getRemappedPackage(uint32_t origPackage) const
   2191 {
   2192     return origPackage;
   2193 }
   2194 
   2195 bool ResourceTable::getAttributeType(uint32_t attrID, uint32_t* outType)
   2196 {
   2197     //printf("getAttributeType #%08x\n", attrID);
   2198     Res_value value;
   2199     if (getItemValue(attrID, ResTable_map::ATTR_TYPE, &value)) {
   2200         //printf("getAttributeType #%08x (%s): #%08x\n", attrID,
   2201         //       String8(getEntry(attrID)->getName()).string(), value.data);
   2202         *outType = value.data;
   2203         return true;
   2204     }
   2205     return false;
   2206 }
   2207 
   2208 bool ResourceTable::getAttributeMin(uint32_t attrID, uint32_t* outMin)
   2209 {
   2210     //printf("getAttributeMin #%08x\n", attrID);
   2211     Res_value value;
   2212     if (getItemValue(attrID, ResTable_map::ATTR_MIN, &value)) {
   2213         *outMin = value.data;
   2214         return true;
   2215     }
   2216     return false;
   2217 }
   2218 
   2219 bool ResourceTable::getAttributeMax(uint32_t attrID, uint32_t* outMax)
   2220 {
   2221     //printf("getAttributeMax #%08x\n", attrID);
   2222     Res_value value;
   2223     if (getItemValue(attrID, ResTable_map::ATTR_MAX, &value)) {
   2224         *outMax = value.data;
   2225         return true;
   2226     }
   2227     return false;
   2228 }
   2229 
   2230 uint32_t ResourceTable::getAttributeL10N(uint32_t attrID)
   2231 {
   2232     //printf("getAttributeL10N #%08x\n", attrID);
   2233     Res_value value;
   2234     if (getItemValue(attrID, ResTable_map::ATTR_L10N, &value)) {
   2235         return value.data;
   2236     }
   2237     return ResTable_map::L10N_NOT_REQUIRED;
   2238 }
   2239 
   2240 bool ResourceTable::getLocalizationSetting()
   2241 {
   2242     return mBundle->getRequireLocalization();
   2243 }
   2244 
   2245 void ResourceTable::reportError(void* accessorCookie, const char* fmt, ...)
   2246 {
   2247     if (accessorCookie != NULL && fmt != NULL) {
   2248         AccessorCookie* ac = (AccessorCookie*)accessorCookie;
   2249         int retval=0;
   2250         char buf[1024];
   2251         va_list ap;
   2252         va_start(ap, fmt);
   2253         retval = vsnprintf(buf, sizeof(buf), fmt, ap);
   2254         va_end(ap);
   2255         ac->sourcePos.error("Error: %s (at '%s' with value '%s').\n",
   2256                             buf, ac->attr.string(), ac->value.string());
   2257     }
   2258 }
   2259 
   2260 bool ResourceTable::getAttributeKeys(
   2261     uint32_t attrID, Vector<String16>* outKeys)
   2262 {
   2263     sp<const Entry> e = getEntry(attrID);
   2264     if (e != NULL) {
   2265         const size_t N = e->getBag().size();
   2266         for (size_t i=0; i<N; i++) {
   2267             const String16& key = e->getBag().keyAt(i);
   2268             if (key.size() > 0 && key.string()[0] != '^') {
   2269                 outKeys->add(key);
   2270             }
   2271         }
   2272         return true;
   2273     }
   2274     return false;
   2275 }
   2276 
   2277 bool ResourceTable::getAttributeEnum(
   2278     uint32_t attrID, const char16_t* name, size_t nameLen,
   2279     Res_value* outValue)
   2280 {
   2281     //printf("getAttributeEnum #%08x %s\n", attrID, String8(name, nameLen).string());
   2282     String16 nameStr(name, nameLen);
   2283     sp<const Entry> e = getEntry(attrID);
   2284     if (e != NULL) {
   2285         const size_t N = e->getBag().size();
   2286         for (size_t i=0; i<N; i++) {
   2287             //printf("Comparing %s to %s\n", String8(name, nameLen).string(),
   2288             //       String8(e->getBag().keyAt(i)).string());
   2289             if (e->getBag().keyAt(i) == nameStr) {
   2290                 return getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, outValue);
   2291             }
   2292         }
   2293     }
   2294     return false;
   2295 }
   2296 
   2297 bool ResourceTable::getAttributeFlags(
   2298     uint32_t attrID, const char16_t* name, size_t nameLen,
   2299     Res_value* outValue)
   2300 {
   2301     outValue->dataType = Res_value::TYPE_INT_HEX;
   2302     outValue->data = 0;
   2303 
   2304     //printf("getAttributeFlags #%08x %s\n", attrID, String8(name, nameLen).string());
   2305     String16 nameStr(name, nameLen);
   2306     sp<const Entry> e = getEntry(attrID);
   2307     if (e != NULL) {
   2308         const size_t N = e->getBag().size();
   2309 
   2310         const char16_t* end = name + nameLen;
   2311         const char16_t* pos = name;
   2312         while (pos < end) {
   2313             const char16_t* start = pos;
   2314             while (pos < end && *pos != '|') {
   2315                 pos++;
   2316             }
   2317 
   2318             String16 nameStr(start, pos-start);
   2319             size_t i;
   2320             for (i=0; i<N; i++) {
   2321                 //printf("Comparing \"%s\" to \"%s\"\n", String8(nameStr).string(),
   2322                 //       String8(e->getBag().keyAt(i)).string());
   2323                 if (e->getBag().keyAt(i) == nameStr) {
   2324                     Res_value val;
   2325                     bool got = getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, &val);
   2326                     if (!got) {
   2327                         return false;
   2328                     }
   2329                     //printf("Got value: 0x%08x\n", val.data);
   2330                     outValue->data |= val.data;
   2331                     break;
   2332                 }
   2333             }
   2334 
   2335             if (i >= N) {
   2336                 // Didn't find this flag identifier.
   2337                 return false;
   2338             }
   2339             pos++;
   2340         }
   2341 
   2342         return true;
   2343     }
   2344     return false;
   2345 }
   2346 
   2347 status_t ResourceTable::assignResourceIds()
   2348 {
   2349     const size_t N = mOrderedPackages.size();
   2350     size_t pi;
   2351     status_t firstError = NO_ERROR;
   2352 
   2353     // First generate all bag attributes and assign indices.
   2354     for (pi=0; pi<N; pi++) {
   2355         sp<Package> p = mOrderedPackages.itemAt(pi);
   2356         if (p == NULL || p->getTypes().size() == 0) {
   2357             // Empty, skip!
   2358             continue;
   2359         }
   2360 
   2361         status_t err = p->applyPublicTypeOrder();
   2362         if (err != NO_ERROR && firstError == NO_ERROR) {
   2363             firstError = err;
   2364         }
   2365 
   2366         // Generate attributes...
   2367         const size_t N = p->getOrderedTypes().size();
   2368         size_t ti;
   2369         for (ti=0; ti<N; ti++) {
   2370             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2371             if (t == NULL) {
   2372                 continue;
   2373             }
   2374             const size_t N = t->getOrderedConfigs().size();
   2375             for (size_t ci=0; ci<N; ci++) {
   2376                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
   2377                 if (c == NULL) {
   2378                     continue;
   2379                 }
   2380                 const size_t N = c->getEntries().size();
   2381                 for (size_t ei=0; ei<N; ei++) {
   2382                     sp<Entry> e = c->getEntries().valueAt(ei);
   2383                     if (e == NULL) {
   2384                         continue;
   2385                     }
   2386                     status_t err = e->generateAttributes(this, p->getName());
   2387                     if (err != NO_ERROR && firstError == NO_ERROR) {
   2388                         firstError = err;
   2389                     }
   2390                 }
   2391             }
   2392         }
   2393 
   2394         const SourcePos unknown(String8("????"), 0);
   2395         sp<Type> attr = p->getType(String16("attr"), unknown);
   2396 
   2397         // Assign indices...
   2398         for (ti=0; ti<N; ti++) {
   2399             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2400             if (t == NULL) {
   2401                 continue;
   2402             }
   2403             err = t->applyPublicEntryOrder();
   2404             if (err != NO_ERROR && firstError == NO_ERROR) {
   2405                 firstError = err;
   2406             }
   2407 
   2408             const size_t N = t->getOrderedConfigs().size();
   2409             t->setIndex(ti+1);
   2410 
   2411             LOG_ALWAYS_FATAL_IF(ti == 0 && attr != t,
   2412                                 "First type is not attr!");
   2413 
   2414             for (size_t ei=0; ei<N; ei++) {
   2415                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ei);
   2416                 if (c == NULL) {
   2417                     continue;
   2418                 }
   2419                 c->setEntryIndex(ei);
   2420             }
   2421         }
   2422 
   2423         // Assign resource IDs to keys in bags...
   2424         for (ti=0; ti<N; ti++) {
   2425             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2426             if (t == NULL) {
   2427                 continue;
   2428             }
   2429             const size_t N = t->getOrderedConfigs().size();
   2430             for (size_t ci=0; ci<N; ci++) {
   2431                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
   2432                 //printf("Ordered config #%d: %p\n", ci, c.get());
   2433                 const size_t N = c->getEntries().size();
   2434                 for (size_t ei=0; ei<N; ei++) {
   2435                     sp<Entry> e = c->getEntries().valueAt(ei);
   2436                     if (e == NULL) {
   2437                         continue;
   2438                     }
   2439                     status_t err = e->assignResourceIds(this, p->getName());
   2440                     if (err != NO_ERROR && firstError == NO_ERROR) {
   2441                         firstError = err;
   2442                     }
   2443                 }
   2444             }
   2445         }
   2446     }
   2447     return firstError;
   2448 }
   2449 
   2450 status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols) {
   2451     const size_t N = mOrderedPackages.size();
   2452     size_t pi;
   2453 
   2454     for (pi=0; pi<N; pi++) {
   2455         sp<Package> p = mOrderedPackages.itemAt(pi);
   2456         if (p->getTypes().size() == 0) {
   2457             // Empty, skip!
   2458             continue;
   2459         }
   2460 
   2461         const size_t N = p->getOrderedTypes().size();
   2462         size_t ti;
   2463 
   2464         for (ti=0; ti<N; ti++) {
   2465             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2466             if (t == NULL) {
   2467                 continue;
   2468             }
   2469             const size_t N = t->getOrderedConfigs().size();
   2470             sp<AaptSymbols> typeSymbols;
   2471             typeSymbols = outSymbols->addNestedSymbol(String8(t->getName()), t->getPos());
   2472             for (size_t ci=0; ci<N; ci++) {
   2473                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
   2474                 if (c == NULL) {
   2475                     continue;
   2476                 }
   2477                 uint32_t rid = getResId(p, t, ci);
   2478                 if (rid == 0) {
   2479                     return UNKNOWN_ERROR;
   2480                 }
   2481                 if (Res_GETPACKAGE(rid) == (size_t)(p->getAssignedId()-1)) {
   2482                     typeSymbols->addSymbol(String8(c->getName()), rid, c->getPos());
   2483 
   2484                     String16 comment(c->getComment());
   2485                     typeSymbols->appendComment(String8(c->getName()), comment, c->getPos());
   2486                     //printf("Type symbol %s comment: %s\n", String8(e->getName()).string(),
   2487                     //     String8(comment).string());
   2488                     comment = c->getTypeComment();
   2489                     typeSymbols->appendTypeComment(String8(c->getName()), comment);
   2490                 } else {
   2491 #if 0
   2492                     printf("**** NO MATCH: 0x%08x vs 0x%08x\n",
   2493                            Res_GETPACKAGE(rid), p->getAssignedId());
   2494 #endif
   2495                 }
   2496             }
   2497         }
   2498     }
   2499     return NO_ERROR;
   2500 }
   2501 
   2502 
   2503 void
   2504 ResourceTable::addLocalization(const String16& name, const String8& locale)
   2505 {
   2506     mLocalizations[name].insert(locale);
   2507 }
   2508 
   2509 
   2510 /*!
   2511  * Flag various sorts of localization problems.  '+' indicates checks already implemented;
   2512  * '-' indicates checks that will be implemented in the future.
   2513  *
   2514  * + A localized string for which no default-locale version exists => warning
   2515  * + A string for which no version in an explicitly-requested locale exists => warning
   2516  * + A localized translation of an translateable="false" string => warning
   2517  * - A localized string not provided in every locale used by the table
   2518  */
   2519 status_t
   2520 ResourceTable::validateLocalizations(void)
   2521 {
   2522     status_t err = NO_ERROR;
   2523     const String8 defaultLocale;
   2524 
   2525     // For all strings...
   2526     for (map<String16, set<String8> >::iterator nameIter = mLocalizations.begin();
   2527          nameIter != mLocalizations.end();
   2528          nameIter++) {
   2529         const set<String8>& configSet = nameIter->second;   // naming convenience
   2530 
   2531         // Look for strings with no default localization
   2532         if (configSet.count(defaultLocale) == 0) {
   2533             fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:",
   2534                     String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]);
   2535             for (set<String8>::const_iterator locales = configSet.begin();
   2536                  locales != configSet.end();
   2537                  locales++) {
   2538                 fprintf(stdout, " %s", (*locales).string());
   2539             }
   2540             fprintf(stdout, "\n");
   2541             // !!! TODO: throw an error here in some circumstances
   2542         }
   2543 
   2544         // Check that all requested localizations are present for this string
   2545         if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) {
   2546             const char* allConfigs = mBundle->getConfigurations();
   2547             const char* start = allConfigs;
   2548             const char* comma;
   2549 
   2550             do {
   2551                 String8 config;
   2552                 comma = strchr(start, ',');
   2553                 if (comma != NULL) {
   2554                     config.setTo(start, comma - start);
   2555                     start = comma + 1;
   2556                 } else {
   2557                     config.setTo(start);
   2558                 }
   2559 
   2560                 // don't bother with the pseudolocale "zz_ZZ"
   2561                 if (config != "zz_ZZ") {
   2562                     if (configSet.find(config) == configSet.end()) {
   2563                         // okay, no specific localization found.  it's possible that we are
   2564                         // requiring a specific regional localization [e.g. de_DE] but there is an
   2565                         // available string in the generic language localization [e.g. de];
   2566                         // consider that string to have fulfilled the localization requirement.
   2567                         String8 region(config.string(), 2);
   2568                         if (configSet.find(region) == configSet.end()) {
   2569                             if (configSet.count(defaultLocale) == 0) {
   2570                                 fprintf(stdout, "aapt: warning: "
   2571                                         "**** string '%s' has no default or required localization "
   2572                                         "for '%s' in %s\n",
   2573                                         String8(nameIter->first).string(),
   2574                                         config.string(),
   2575                                         mBundle->getResourceSourceDirs()[0]);
   2576                             }
   2577                         }
   2578                     }
   2579                 }
   2580            } while (comma != NULL);
   2581         }
   2582     }
   2583 
   2584     return err;
   2585 }
   2586 
   2587 status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
   2588 {
   2589     ResourceFilter filter;
   2590     status_t err = filter.parse(bundle->getConfigurations());
   2591     if (err != NO_ERROR) {
   2592         return err;
   2593     }
   2594 
   2595     const ConfigDescription nullConfig;
   2596 
   2597     const size_t N = mOrderedPackages.size();
   2598     size_t pi;
   2599 
   2600     const static String16 mipmap16("mipmap");
   2601 
   2602     bool useUTF8 = !bundle->getUTF16StringsOption();
   2603 
   2604     // Iterate through all data, collecting all values (strings,
   2605     // references, etc).
   2606     StringPool valueStrings(useUTF8);
   2607     Vector<sp<Entry> > allEntries;
   2608     for (pi=0; pi<N; pi++) {
   2609         sp<Package> p = mOrderedPackages.itemAt(pi);
   2610         if (p->getTypes().size() == 0) {
   2611             // Empty, skip!
   2612             continue;
   2613         }
   2614 
   2615         StringPool typeStrings(useUTF8);
   2616         StringPool keyStrings(useUTF8);
   2617 
   2618         const size_t N = p->getOrderedTypes().size();
   2619         for (size_t ti=0; ti<N; ti++) {
   2620             sp<Type> t = p->getOrderedTypes().itemAt(ti);
   2621             if (t == NULL) {
   2622                 typeStrings.add(String16("<empty>"), false);
   2623                 continue;
   2624             }
   2625             const String16 typeName(t->getName());
   2626             typeStrings.add(typeName, false);
   2627 
   2628             // This is a hack to tweak the sorting order of the final strings,
   2629             // to put stuff that is generally not language-specific first.
   2630             String8 configTypeName(typeName);
   2631             if (configTypeName == "drawable" || configTypeName == "layout"
   2632                     || configTypeName == "color" || configTypeName == "anim"
   2633                     || configTypeName == "interpolator" || configTypeName == "animator"
   2634                     || configTypeName == "xml" || configTypeName == "menu"
   2635                     || configTypeName == "mipmap" || configTypeName == "raw") {
   2636                 configTypeName = "1complex";
   2637             } else {
   2638                 configTypeName = "2value";
   2639             }
   2640 
   2641             const bool filterable = (typeName != mipmap16);
   2642 
   2643             const size_t N = t->getOrderedConfigs().size();
   2644             for (size_t ci=0; ci<N; ci++) {
   2645                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
   2646                 if (c == NULL) {
   2647                     continue;
   2648                 }
   2649                 const size_t N = c->getEntries().size();
   2650                 for (size_t ei=0; ei<N; ei++) {
   2651                     ConfigDescription config = c->getEntries().keyAt(ei);
   2652                     if (filterable && !filter.match(config)) {
   2653                         continue;
   2654                     }
   2655                     sp<Entry> e = c->getEntries().valueAt(ei);
   2656                     if (e == NULL) {
   2657                         continue;
   2658                     }
   2659                     e->setNameIndex(keyStrings.add(e->getName(), true));
   2660 
   2661                     // If this entry has no values for other configs,
   2662                     // and is the default config, then it is special.  Otherwise
   2663                     // we want to add it with the config info.
   2664                     ConfigDescription* valueConfig = NULL;
   2665                     if (N != 1 || config == nullConfig) {
   2666                         valueConfig = &config;
   2667                     }
   2668 
   2669                     status_t err = e->prepareFlatten(&valueStrings, this,
   2670                             &configTypeName, &config);
   2671                     if (err != NO_ERROR) {
   2672                         return err;
   2673                     }
   2674                     allEntries.add(e);
   2675                 }
   2676             }
   2677         }
   2678 
   2679         p->setTypeStrings(typeStrings.createStringBlock());
   2680         p->setKeyStrings(keyStrings.createStringBlock());
   2681     }
   2682 
   2683     if (bundle->getOutputAPKFile() != NULL) {
   2684         // Now we want to sort the value strings for better locality.  This will
   2685         // cause the positions of the strings to change, so we need to go back
   2686         // through out resource entries and update them accordingly.  Only need
   2687         // to do this if actually writing the output file.
   2688         valueStrings.sortByConfig();
   2689         for (pi=0; pi<allEntries.size(); pi++) {
   2690             allEntries[pi]->remapStringValue(&valueStrings);
   2691         }
   2692     }
   2693 
   2694     ssize_t strAmt = 0;
   2695 
   2696     // Now build the array of package chunks.
   2697     Vector<sp<AaptFile> > flatPackages;
   2698     for (pi=0; pi<N; pi++) {
   2699         sp<Package> p = mOrderedPackages.itemAt(pi);
   2700         if (p->getTypes().size() == 0) {
   2701             // Empty, skip!
   2702             continue;
   2703         }
   2704 
   2705         const size_t N = p->getTypeStrings().size();
   2706 
   2707         const size_t baseSize = sizeof(ResTable_package);
   2708 
   2709         // Start the package data.
   2710         sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
   2711         ResTable_package* header = (ResTable_package*)data->editData(baseSize);
   2712         if (header == NULL) {
   2713             fprintf(stderr, "ERROR: out of memory creating ResTable_package\n");
   2714             return NO_MEMORY;
   2715         }
   2716         memset(header, 0, sizeof(*header));
   2717         header->header.type = htods(RES_TABLE_PACKAGE_TYPE);
   2718         header->header.headerSize = htods(sizeof(*header));
   2719         header->id = htodl(p->getAssignedId());
   2720         strcpy16_htod(header->name, p->getName().string());
   2721 
   2722         // Write the string blocks.
   2723         const size_t typeStringsStart = data->getSize();
   2724         sp<AaptFile> strFile = p->getTypeStringsData();
   2725         ssize_t amt = data->writeData(strFile->getData(), strFile->getSize());
   2726         #if PRINT_STRING_METRICS
   2727         fprintf(stderr, "**** type strings: %d\n", amt);
   2728         #endif
   2729         strAmt += amt;
   2730         if (amt < 0) {
   2731             return amt;
   2732         }
   2733         const size_t keyStringsStart = data->getSize();
   2734         strFile = p->getKeyStringsData();
   2735         amt = data->writeData(strFile->getData(), strFile->getSize());
   2736         #if PRINT_STRING_METRICS
   2737         fprintf(stderr, "**** key strings: %d\n", amt);
   2738         #endif
   2739         strAmt += amt;
   2740         if (amt < 0) {
   2741             return amt;
   2742         }
   2743 
   2744         // Build the type chunks inside of this package.
   2745         for (size_t ti=0; ti<N; ti++) {
   2746             // Retrieve them in the same order as the type string block.
   2747             size_t len;
   2748             String16 typeName(p->getTypeStrings().stringAt(ti, &len));
   2749             sp<Type> t = p->getTypes().valueFor(typeName);
   2750             LOG_ALWAYS_FATAL_IF(t == NULL && typeName != String16("<empty>"),
   2751                                 "Type name %s not found",
   2752                                 String8(typeName).string());
   2753 
   2754             const bool filterable = (typeName != mipmap16);
   2755 
   2756             const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
   2757 
   2758             // Until a non-NO_ENTRY value has been written for a resource,
   2759             // that resource is invalid; validResources[i] represents
   2760             // the item at t->getOrderedConfigs().itemAt(i).
   2761             Vector<bool> validResources;
   2762             validResources.insertAt(false, 0, N);
   2763 
   2764             // First write the typeSpec chunk, containing information about
   2765             // each resource entry in this type.
   2766             {
   2767                 const size_t typeSpecSize = sizeof(ResTable_typeSpec) + sizeof(uint32_t)*N;
   2768                 const size_t typeSpecStart = data->getSize();
   2769                 ResTable_typeSpec* tsHeader = (ResTable_typeSpec*)
   2770                     (((uint8_t*)data->editData(typeSpecStart+typeSpecSize)) + typeSpecStart);
   2771                 if (tsHeader == NULL) {
   2772                     fprintf(stderr, "ERROR: out of memory creating ResTable_typeSpec\n");
   2773                     return NO_MEMORY;
   2774                 }
   2775                 memset(tsHeader, 0, sizeof(*tsHeader));
   2776                 tsHeader->header.type = htods(RES_TABLE_TYPE_SPEC_TYPE);
   2777                 tsHeader->header.headerSize = htods(sizeof(*tsHeader));
   2778                 tsHeader->header.size = htodl(typeSpecSize);
   2779                 tsHeader->id = ti+1;
   2780                 tsHeader->entryCount = htodl(N);
   2781 
   2782                 uint32_t* typeSpecFlags = (uint32_t*)
   2783                     (((uint8_t*)data->editData())
   2784                         + typeSpecStart + sizeof(ResTable_typeSpec));
   2785                 memset(typeSpecFlags, 0, sizeof(uint32_t)*N);
   2786 
   2787                 for (size_t ei=0; ei<N; ei++) {
   2788                     sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
   2789                     if (cl->getPublic()) {
   2790                         typeSpecFlags[ei] |= htodl(ResTable_typeSpec::SPEC_PUBLIC);
   2791                     }
   2792                     const size_t CN = cl->getEntries().size();
   2793                     for (size_t ci=0; ci<CN; ci++) {
   2794                         if (filterable && !filter.match(cl->getEntries().keyAt(ci))) {
   2795                             continue;
   2796                         }
   2797                         for (size_t cj=ci+1; cj<CN; cj++) {
   2798                             if (filterable && !filter.match(cl->getEntries().keyAt(cj))) {
   2799                                 continue;
   2800                             }
   2801                             typeSpecFlags[ei] |= htodl(
   2802                                 cl->getEntries().keyAt(ci).diff(cl->getEntries().keyAt(cj)));
   2803                         }
   2804                     }
   2805                 }
   2806             }
   2807 
   2808             // We need to write one type chunk for each configuration for
   2809             // which we have entries in this type.
   2810             const size_t NC = t->getUniqueConfigs().size();
   2811 
   2812             const size_t typeSize = sizeof(ResTable_type) + sizeof(uint32_t)*N;
   2813 
   2814             for (size_t ci=0; ci<NC; ci++) {
   2815                 ConfigDescription config = t->getUniqueConfigs().itemAt(ci);
   2816 
   2817                 NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
   2818                      "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
   2819                      "sw%ddp w%ddp h%ddp dir:%d\n",
   2820                       ti+1,
   2821                       config.mcc, config.mnc,
   2822                       config.language[0] ? config.language[0] : '-',
   2823                       config.language[1] ? config.language[1] : '-',
   2824                       config.country[0] ? config.country[0] : '-',
   2825                       config.country[1] ? config.country[1] : '-',
   2826                       config.orientation,
   2827                       config.uiMode,
   2828                       config.touchscreen,
   2829                       config.density,
   2830                       config.keyboard,
   2831                       config.inputFlags,
   2832                       config.navigation,
   2833                       config.screenWidth,
   2834                       config.screenHeight,
   2835                       config.smallestScreenWidthDp,
   2836                       config.screenWidthDp,
   2837                       config.screenHeightDp,
   2838                       config.layoutDirection));
   2839 
   2840                 if (filterable && !filter.match(config)) {
   2841                     continue;
   2842                 }
   2843 
   2844                 const size_t typeStart = data->getSize();
   2845 
   2846                 ResTable_type* tHeader = (ResTable_type*)
   2847                     (((uint8_t*)data->editData(typeStart+typeSize)) + typeStart);
   2848                 if (tHeader == NULL) {
   2849                     fprintf(stderr, "ERROR: out of memory creating ResTable_type\n");
   2850                     return NO_MEMORY;
   2851                 }
   2852 
   2853                 memset(tHeader, 0, sizeof(*tHeader));
   2854                 tHeader->header.type = htods(RES_TABLE_TYPE_TYPE);
   2855                 tHeader->header.headerSize = htods(sizeof(*tHeader));
   2856                 tHeader->id = ti+1;
   2857                 tHeader->entryCount = htodl(N);
   2858                 tHeader->entriesStart = htodl(typeSize);
   2859                 tHeader->config = config;
   2860                 NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
   2861                      "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
   2862                      "sw%ddp w%ddp h%ddp dir:%d\n",
   2863                       ti+1,
   2864                       tHeader->config.mcc, tHeader->config.mnc,
   2865                       tHeader->config.language[0] ? tHeader->config.language[0] : '-',
   2866                       tHeader->config.language[1] ? tHeader->config.language[1] : '-',
   2867                       tHeader->config.country[0] ? tHeader->config.country[0] : '-',
   2868                       tHeader->config.country[1] ? tHeader->config.country[1] : '-',
   2869                       tHeader->config.orientation,
   2870                       tHeader->config.uiMode,
   2871                       tHeader->config.touchscreen,
   2872                       tHeader->config.density,
   2873                       tHeader->config.keyboard,
   2874                       tHeader->config.inputFlags,
   2875                       tHeader->config.navigation,
   2876                       tHeader->config.screenWidth,
   2877                       tHeader->config.screenHeight,
   2878                       tHeader->config.smallestScreenWidthDp,
   2879                       tHeader->config.screenWidthDp,
   2880                       tHeader->config.screenHeightDp,
   2881                       tHeader->config.layoutDirection));
   2882                 tHeader->config.swapHtoD();
   2883 
   2884                 // Build the entries inside of this type.
   2885                 for (size_t ei=0; ei<N; ei++) {
   2886                     sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
   2887                     sp<Entry> e = cl->getEntries().valueFor(config);
   2888 
   2889                     // Set the offset for this entry in its type.
   2890                     uint32_t* index = (uint32_t*)
   2891                         (((uint8_t*)data->editData())
   2892                             + typeStart + sizeof(ResTable_type));
   2893                     if (e != NULL) {
   2894                         index[ei] = htodl(data->getSize()-typeStart-typeSize);
   2895 
   2896                         // Create the entry.
   2897                         ssize_t amt = e->flatten(bundle, data, cl->getPublic());
   2898                         if (amt < 0) {
   2899                             return amt;
   2900                         }
   2901                         validResources.editItemAt(ei) = true;
   2902                     } else {
   2903                         index[ei] = htodl(ResTable_type::NO_ENTRY);
   2904                     }
   2905                 }
   2906 
   2907                 // Fill in the rest of the type information.
   2908                 tHeader = (ResTable_type*)
   2909                     (((uint8_t*)data->editData()) + typeStart);
   2910                 tHeader->header.size = htodl(data->getSize()-typeStart);
   2911             }
   2912 
   2913             for (size_t i = 0; i < N; ++i) {
   2914                 if (!validResources[i]) {
   2915                     sp<ConfigList> c = t->getOrderedConfigs().itemAt(i);
   2916                     fprintf(stderr, "warning: no entries written for %s/%s\n",
   2917                             String8(typeName).string(), String8(c->getName()).string());
   2918                 }
   2919             }
   2920         }
   2921 
   2922         // Fill in the rest of the package information.
   2923         header = (ResTable_package*)data->editData();
   2924         header->header.size = htodl(data->getSize());
   2925         header->typeStrings = htodl(typeStringsStart);
   2926         header->lastPublicType = htodl(p->getTypeStrings().size());
   2927         header->keyStrings = htodl(keyStringsStart);
   2928         header->lastPublicKey = htodl(p->getKeyStrings().size());
   2929 
   2930         flatPackages.add(data);
   2931     }
   2932 
   2933     // And now write out the final chunks.
   2934     const size_t dataStart = dest->getSize();
   2935 
   2936     {
   2937         // blah
   2938         ResTable_header header;
   2939         memset(&header, 0, sizeof(header));
   2940         header.header.type = htods(RES_TABLE_TYPE);
   2941         header.header.headerSize = htods(sizeof(header));
   2942         header.packageCount = htodl(flatPackages.size());
   2943         status_t err = dest->writeData(&header, sizeof(header));
   2944         if (err != NO_ERROR) {
   2945             fprintf(stderr, "ERROR: out of memory creating ResTable_header\n");
   2946             return err;
   2947         }
   2948     }
   2949 
   2950     ssize_t strStart = dest->getSize();
   2951     err = valueStrings.writeStringBlock(dest);
   2952     if (err != NO_ERROR) {
   2953         return err;
   2954     }
   2955 
   2956     ssize_t amt = (dest->getSize()-strStart);
   2957     strAmt += amt;
   2958     #if PRINT_STRING_METRICS
   2959     fprintf(stderr, "**** value strings: %d\n", amt);
   2960     fprintf(stderr, "**** total strings: %d\n", strAmt);
   2961     #endif
   2962 
   2963     for (pi=0; pi<flatPackages.size(); pi++) {
   2964         err = dest->writeData(flatPackages[pi]->getData(),
   2965                               flatPackages[pi]->getSize());
   2966         if (err != NO_ERROR) {
   2967             fprintf(stderr, "ERROR: out of memory creating package chunk for ResTable_header\n");
   2968             return err;
   2969         }
   2970     }
   2971 
   2972     ResTable_header* header = (ResTable_header*)
   2973         (((uint8_t*)dest->getData()) + dataStart);
   2974     header->header.size = htodl(dest->getSize() - dataStart);
   2975 
   2976     NOISY(aout << "Resource table:"
   2977           << HexDump(dest->getData(), dest->getSize()) << endl);
   2978 
   2979     #if PRINT_STRING_METRICS
   2980     fprintf(stderr, "**** total resource table size: %d / %d%% strings\n",
   2981         dest->getSize(), (strAmt*100)/dest->getSize());
   2982     #endif
   2983 
   2984     return NO_ERROR;
   2985 }
   2986 
   2987 void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp)
   2988 {
   2989     fprintf(fp,
   2990     "<!-- This file contains <public> resource definitions for all\n"
   2991     "     resources that were generated from the source data. -->\n"
   2992     "\n"
   2993     "<resources>\n");
   2994 
   2995     writePublicDefinitions(package, fp, true);
   2996     writePublicDefinitions(package, fp, false);
   2997 
   2998     fprintf(fp,
   2999     "\n"
   3000     "</resources>\n");
   3001 }
   3002 
   3003 void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp, bool pub)
   3004 {
   3005     bool didHeader = false;
   3006 
   3007     sp<Package> pkg = mPackages.valueFor(package);
   3008     if (pkg != NULL) {
   3009         const size_t NT = pkg->getOrderedTypes().size();
   3010         for (size_t i=0; i<NT; i++) {
   3011             sp<Type> t = pkg->getOrderedTypes().itemAt(i);
   3012             if (t == NULL) {
   3013                 continue;
   3014             }
   3015 
   3016             bool didType = false;
   3017 
   3018             const size_t NC = t->getOrderedConfigs().size();
   3019             for (size_t j=0; j<NC; j++) {
   3020                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(j);
   3021                 if (c == NULL) {
   3022                     continue;
   3023                 }
   3024 
   3025                 if (c->getPublic() != pub) {
   3026                     continue;
   3027                 }
   3028 
   3029                 if (!didType) {
   3030                     fprintf(fp, "\n");
   3031                     didType = true;
   3032                 }
   3033                 if (!didHeader) {
   3034                     if (pub) {
   3035                         fprintf(fp,"  <!-- PUBLIC SECTION.  These resources have been declared public.\n");
   3036                         fprintf(fp,"       Changes to these definitions will break binary compatibility. -->\n\n");
   3037                     } else {
   3038                         fprintf(fp,"  <!-- PRIVATE SECTION.  These resources have not been declared public.\n");
   3039                         fprintf(fp,"       You can make them public my moving these lines into a file in res/values. -->\n\n");
   3040                     }
   3041                     didHeader = true;
   3042                 }
   3043                 if (!pub) {
   3044                     const size_t NE = c->getEntries().size();
   3045                     for (size_t k=0; k<NE; k++) {
   3046                         const SourcePos& pos = c->getEntries().valueAt(k)->getPos();
   3047                         if (pos.file != "") {
   3048                             fprintf(fp,"  <!-- Declared at %s:%d -->\n",
   3049                                     pos.file.string(), pos.line);
   3050                         }
   3051                     }
   3052                 }
   3053                 fprintf(fp, "  <public type=\"%s\" name=\"%s\" id=\"0x%08x\" />\n",
   3054                         String8(t->getName()).string(),
   3055                         String8(c->getName()).string(),
   3056                         getResId(pkg, t, c->getEntryIndex()));
   3057             }
   3058         }
   3059     }
   3060 }
   3061 
   3062 ResourceTable::Item::Item(const SourcePos& _sourcePos,
   3063                           bool _isId,
   3064                           const String16& _value,
   3065                           const Vector<StringPool::entry_style_span>* _style,
   3066                           int32_t _format)
   3067     : sourcePos(_sourcePos)
   3068     , isId(_isId)
   3069     , value(_value)
   3070     , format(_format)
   3071     , bagKeyId(0)
   3072     , evaluating(false)
   3073 {
   3074     if (_style) {
   3075         style = *_style;
   3076     }
   3077 }
   3078 
   3079 status_t ResourceTable::Entry::makeItABag(const SourcePos& sourcePos)
   3080 {
   3081     if (mType == TYPE_BAG) {
   3082         return NO_ERROR;
   3083     }
   3084     if (mType == TYPE_UNKNOWN) {
   3085         mType = TYPE_BAG;
   3086         return NO_ERROR;
   3087     }
   3088     sourcePos.error("Resource entry %s is already defined as a single item.\n"
   3089                     "%s:%d: Originally defined here.\n",
   3090                     String8(mName).string(),
   3091                     mItem.sourcePos.file.string(), mItem.sourcePos.line);
   3092     return UNKNOWN_ERROR;
   3093 }
   3094 
   3095 status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos,
   3096                                        const String16& value,
   3097                                        const Vector<StringPool::entry_style_span>* style,
   3098                                        int32_t format,
   3099                                        const bool overwrite)
   3100 {
   3101     Item item(sourcePos, false, value, style);
   3102 
   3103     if (mType == TYPE_BAG) {
   3104         const Item& item(mBag.valueAt(0));
   3105         sourcePos.error("Resource entry %s is already defined as a bag.\n"
   3106                         "%s:%d: Originally defined here.\n",
   3107                         String8(mName).string(),
   3108                         item.sourcePos.file.string(), item.sourcePos.line);
   3109         return UNKNOWN_ERROR;
   3110     }
   3111     if ( (mType != TYPE_UNKNOWN) && (overwrite == false) ) {
   3112         sourcePos.error("Resource entry %s is already defined.\n"
   3113                         "%s:%d: Originally defined here.\n",
   3114                         String8(mName).string(),
   3115                         mItem.sourcePos.file.string(), mItem.sourcePos.line);
   3116         return UNKNOWN_ERROR;
   3117     }
   3118 
   3119     mType = TYPE_ITEM;
   3120     mItem = item;
   3121     mItemFormat = format;
   3122     return NO_ERROR;
   3123 }
   3124 
   3125 status_t ResourceTable::Entry::addToBag(const SourcePos& sourcePos,
   3126                                         const String16& key, const String16& value,
   3127                                         const Vector<StringPool::entry_style_span>* style,
   3128                                         bool replace, bool isId, int32_t format)
   3129 {
   3130     status_t err = makeItABag(sourcePos);
   3131     if (err != NO_ERROR) {
   3132         return err;
   3133     }
   3134 
   3135     Item item(sourcePos, isId, value, style, format);
   3136 
   3137     // XXX NOTE: there is an error if you try to have a bag with two keys,
   3138     // one an attr and one an id, with the same name.  Not something we
   3139     // currently ever have to worry about.
   3140     ssize_t origKey = mBag.indexOfKey(key);
   3141     if (origKey >= 0) {
   3142         if (!replace) {
   3143             const Item& item(mBag.valueAt(origKey));
   3144             sourcePos.error("Resource entry %s already has bag item %s.\n"
   3145                     "%s:%d: Originally defined here.\n",
   3146                     String8(mName).string(), String8(key).string(),
   3147                     item.sourcePos.file.string(), item.sourcePos.line);
   3148             return UNKNOWN_ERROR;
   3149         }
   3150         //printf("Replacing %s with %s\n",
   3151         //       String8(mBag.valueFor(key).value).string(), String8(value).string());
   3152         mBag.replaceValueFor(key, item);
   3153     }
   3154 
   3155     mBag.add(key, item);
   3156     return NO_ERROR;
   3157 }
   3158 
   3159 status_t ResourceTable::Entry::emptyBag(const SourcePos& sourcePos)
   3160 {
   3161     status_t err = makeItABag(sourcePos);
   3162     if (err != NO_ERROR) {
   3163         return err;
   3164     }
   3165 
   3166     mBag.clear();
   3167     return NO_ERROR;
   3168 }
   3169 
   3170 status_t ResourceTable::Entry::generateAttributes(ResourceTable* table,
   3171                                                   const String16& package)
   3172 {
   3173     const String16 attr16("attr");
   3174     const String16 id16("id");
   3175     const size_t N = mBag.size();
   3176     for (size_t i=0; i<N; i++) {
   3177         const String16& key = mBag.keyAt(i);
   3178         const Item& it = mBag.valueAt(i);
   3179         if (it.isId) {
   3180             if (!table->hasBagOrEntry(key, &id16, &package)) {
   3181                 String16 value("false");
   3182                 status_t err = table->addEntry(SourcePos(String8("<generated>"), 0), package,
   3183                                                id16, key, value);
   3184                 if (err != NO_ERROR) {
   3185                     return err;
   3186                 }
   3187             }
   3188         } else if (!table->hasBagOrEntry(key, &attr16, &package)) {
   3189 
   3190 #if 1
   3191 //             fprintf(stderr, "ERROR: Bag attribute '%s' has not been defined.\n",
   3192 //                     String8(key).string());
   3193 //             const Item& item(mBag.valueAt(i));
   3194 //             fprintf(stderr, "Referenced from file %s line %d\n",
   3195 //                     item.sourcePos.file.string(), item.sourcePos.line);
   3196 //             return UNKNOWN_ERROR;
   3197 #else
   3198             char numberStr[16];
   3199             sprintf(numberStr, "%d", ResTable_map::TYPE_ANY);
   3200             status_t err = table->addBag(SourcePos("<generated>", 0), package,
   3201                                          attr16, key, String16(""),
   3202                                          String16("^type"),
   3203                                          String16(numberStr), NULL, NULL);
   3204             if (err != NO_ERROR) {
   3205                 return err;
   3206             }
   3207 #endif
   3208         }
   3209     }
   3210     return NO_ERROR;
   3211 }
   3212 
   3213 status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table,
   3214                                                  const String16& package)
   3215 {
   3216     bool hasErrors = false;
   3217 
   3218     if (mType == TYPE_BAG) {
   3219         const char* errorMsg;
   3220         const String16 style16("style");
   3221         const String16 attr16("attr");
   3222         const String16 id16("id");
   3223         mParentId = 0;
   3224         if (mParent.size() > 0) {
   3225             mParentId = table->getResId(mParent, &style16, NULL, &errorMsg);
   3226             if (mParentId == 0) {
   3227                 mPos.error("Error retrieving parent for item: %s '%s'.\n",
   3228                         errorMsg, String8(mParent).string());
   3229                 hasErrors = true;
   3230             }
   3231         }
   3232         const size_t N = mBag.size();
   3233         for (size_t i=0; i<N; i++) {
   3234             const String16& key = mBag.keyAt(i);
   3235             Item& it = mBag.editValueAt(i);
   3236             it.bagKeyId = table->getResId(key,
   3237                     it.isId ? &id16 : &attr16, NULL, &errorMsg);
   3238             //printf("Bag key of %s: #%08x\n", String8(key).string(), it.bagKeyId);
   3239             if (it.bagKeyId == 0) {
   3240                 it.sourcePos.error("Error: %s: %s '%s'.\n", errorMsg,
   3241                         String8(it.isId ? id16 : attr16).string(),
   3242                         String8(key).string());
   3243                 hasErrors = true;
   3244             }
   3245         }
   3246     }
   3247     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
   3248 }
   3249 
   3250 status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table,
   3251         const String8* configTypeName, const ConfigDescription* config)
   3252 {
   3253     if (mType == TYPE_ITEM) {
   3254         Item& it = mItem;
   3255         AccessorCookie ac(it.sourcePos, String8(mName), String8(it.value));
   3256         if (!table->stringToValue(&it.parsedValue, strings,
   3257                                   it.value, false, true, 0,
   3258                                   &it.style, NULL, &ac, mItemFormat,
   3259                                   configTypeName, config)) {
   3260             return UNKNOWN_ERROR;
   3261         }
   3262     } else if (mType == TYPE_BAG) {
   3263         const size_t N = mBag.size();
   3264         for (size_t i=0; i<N; i++) {
   3265             const String16& key = mBag.keyAt(i);
   3266             Item& it = mBag.editValueAt(i);
   3267             AccessorCookie ac(it.sourcePos, String8(key), String8(it.value));
   3268             if (!table->stringToValue(&it.parsedValue, strings,
   3269                                       it.value, false, true, it.bagKeyId,
   3270                                       &it.style, NULL, &ac, it.format,
   3271                                       configTypeName, config)) {
   3272                 return UNKNOWN_ERROR;
   3273             }
   3274         }
   3275     } else {
   3276         mPos.error("Error: entry %s is not a single item or a bag.\n",
   3277                    String8(mName).string());
   3278         return UNKNOWN_ERROR;
   3279     }
   3280     return NO_ERROR;
   3281 }
   3282 
   3283 status_t ResourceTable::Entry::remapStringValue(StringPool* strings)
   3284 {
   3285     if (mType == TYPE_ITEM) {
   3286         Item& it = mItem;
   3287         if (it.parsedValue.dataType == Res_value::TYPE_STRING) {
   3288             it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data);
   3289         }
   3290     } else if (mType == TYPE_BAG) {
   3291         const size_t N = mBag.size();
   3292         for (size_t i=0; i<N; i++) {
   3293             Item& it = mBag.editValueAt(i);
   3294             if (it.parsedValue.dataType == Res_value::TYPE_STRING) {
   3295                 it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data);
   3296             }
   3297         }
   3298     } else {
   3299         mPos.error("Error: entry %s is not a single item or a bag.\n",
   3300                    String8(mName).string());
   3301         return UNKNOWN_ERROR;
   3302     }
   3303     return NO_ERROR;
   3304 }
   3305 
   3306 ssize_t ResourceTable::Entry::flatten(Bundle* bundle, const sp<AaptFile>& data, bool isPublic)
   3307 {
   3308     size_t amt = 0;
   3309     ResTable_entry header;
   3310     memset(&header, 0, sizeof(header));
   3311     header.size = htods(sizeof(header));
   3312     const type ty = this != NULL ? mType : TYPE_ITEM;
   3313     if (this != NULL) {
   3314         if (ty == TYPE_BAG) {
   3315             header.flags |= htods(header.FLAG_COMPLEX);
   3316         }
   3317         if (isPublic) {
   3318             header.flags |= htods(header.FLAG_PUBLIC);
   3319         }
   3320         header.key.index = htodl(mNameIndex);
   3321     }
   3322     if (ty != TYPE_BAG) {
   3323         status_t err = data->writeData(&header, sizeof(header));
   3324         if (err != NO_ERROR) {
   3325             fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
   3326             return err;
   3327         }
   3328 
   3329         const Item& it = mItem;
   3330         Res_value par;
   3331         memset(&par, 0, sizeof(par));
   3332         par.size = htods(it.parsedValue.size);
   3333         par.dataType = it.parsedValue.dataType;
   3334         par.res0 = it.parsedValue.res0;
   3335         par.data = htodl(it.parsedValue.data);
   3336         #if 0
   3337         printf("Writing item (%s): type=%d, data=0x%x, res0=0x%x\n",
   3338                String8(mName).string(), it.parsedValue.dataType,
   3339                it.parsedValue.data, par.res0);
   3340         #endif
   3341         err = data->writeData(&par, it.parsedValue.size);
   3342         if (err != NO_ERROR) {
   3343             fprintf(stderr, "ERROR: out of memory creating Res_value\n");
   3344             return err;
   3345         }
   3346         amt += it.parsedValue.size;
   3347     } else {
   3348         size_t N = mBag.size();
   3349         size_t i;
   3350         // Create correct ordering of items.
   3351         KeyedVector<uint32_t, const Item*> items;
   3352         for (i=0; i<N; i++) {
   3353             const Item& it = mBag.valueAt(i);
   3354             items.add(it.bagKeyId, &it);
   3355         }
   3356         N = items.size();
   3357 
   3358         ResTable_map_entry mapHeader;
   3359         memcpy(&mapHeader, &header, sizeof(header));
   3360         mapHeader.size = htods(sizeof(mapHeader));
   3361         mapHeader.parent.ident = htodl(mParentId);
   3362         mapHeader.count = htodl(N);
   3363         status_t err = data->writeData(&mapHeader, sizeof(mapHeader));
   3364         if (err != NO_ERROR) {
   3365             fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
   3366             return err;
   3367         }
   3368 
   3369         for (i=0; i<N; i++) {
   3370             const Item& it = *items.valueAt(i);
   3371             ResTable_map map;
   3372             map.name.ident = htodl(it.bagKeyId);
   3373             map.value.size = htods(it.parsedValue.size);
   3374             map.value.dataType = it.parsedValue.dataType;
   3375             map.value.res0 = it.parsedValue.res0;
   3376             map.value.data = htodl(it.parsedValue.data);
   3377             err = data->writeData(&map, sizeof(map));
   3378             if (err != NO_ERROR) {
   3379                 fprintf(stderr, "ERROR: out of memory creating Res_value\n");
   3380                 return err;
   3381             }
   3382             amt += sizeof(map);
   3383         }
   3384     }
   3385     return amt;
   3386 }
   3387 
   3388 void ResourceTable::ConfigList::appendComment(const String16& comment,
   3389                                               bool onlyIfEmpty)
   3390 {
   3391     if (comment.size() <= 0) {
   3392         return;
   3393     }
   3394     if (onlyIfEmpty && mComment.size() > 0) {
   3395         return;
   3396     }
   3397     if (mComment.size() > 0) {
   3398         mComment.append(String16("\n"));
   3399     }
   3400     mComment.append(comment);
   3401 }
   3402 
   3403 void ResourceTable::ConfigList::appendTypeComment(const String16& comment)
   3404 {
   3405     if (comment.size() <= 0) {
   3406         return;
   3407     }
   3408     if (mTypeComment.size() > 0) {
   3409         mTypeComment.append(String16("\n"));
   3410     }
   3411     mTypeComment.append(comment);
   3412 }
   3413 
   3414 status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos,
   3415                                         const String16& name,
   3416                                         const uint32_t ident)
   3417 {
   3418     #if 0
   3419     int32_t entryIdx = Res_GETENTRY(ident);
   3420     if (entryIdx < 0) {
   3421         sourcePos.error("Public resource %s/%s has an invalid 0 identifier (0x%08x).\n",
   3422                 String8(mName).string(), String8(name).string(), ident);
   3423         return UNKNOWN_ERROR;
   3424     }
   3425     #endif
   3426 
   3427     int32_t typeIdx = Res_GETTYPE(ident);
   3428     if (typeIdx >= 0) {
   3429         typeIdx++;
   3430         if (mPublicIndex > 0 && mPublicIndex != typeIdx) {
   3431             sourcePos.error("Public resource %s/%s has conflicting type codes for its"
   3432                     " public identifiers (0x%x vs 0x%x).\n",
   3433                     String8(mName).string(), String8(name).string(),
   3434                     mPublicIndex, typeIdx);
   3435             return UNKNOWN_ERROR;
   3436         }
   3437         mPublicIndex = typeIdx;
   3438     }
   3439 
   3440     if (mFirstPublicSourcePos == NULL) {
   3441         mFirstPublicSourcePos = new SourcePos(sourcePos);
   3442     }
   3443 
   3444     if (mPublic.indexOfKey(name) < 0) {
   3445         mPublic.add(name, Public(sourcePos, String16(), ident));
   3446     } else {
   3447         Public& p = mPublic.editValueFor(name);
   3448         if (p.ident != ident) {
   3449             sourcePos.error("Public resource %s/%s has conflicting public identifiers"
   3450                     " (0x%08x vs 0x%08x).\n"
   3451                     "%s:%d: Originally defined here.\n",
   3452                     String8(mName).string(), String8(name).string(), p.ident, ident,
   3453                     p.sourcePos.file.string(), p.sourcePos.line);
   3454             return UNKNOWN_ERROR;
   3455         }
   3456     }
   3457 
   3458     return NO_ERROR;
   3459 }
   3460 
   3461 void ResourceTable::Type::canAddEntry(const String16& name)
   3462 {
   3463     mCanAddEntries.add(name);
   3464 }
   3465 
   3466 sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
   3467                                                        const SourcePos& sourcePos,
   3468                                                        const ResTable_config* config,
   3469                                                        bool doSetIndex,
   3470                                                        bool overlay,
   3471                                                        bool autoAddOverlay)
   3472 {
   3473     int pos = -1;
   3474     sp<ConfigList> c = mConfigs.valueFor(entry);
   3475     if (c == NULL) {
   3476         if (overlay && !autoAddOverlay && mCanAddEntries.indexOf(entry) < 0) {
   3477             sourcePos.error("Resource at %s appears in overlay but not"
   3478                             " in the base package; use <add-resource> to add.\n",
   3479                             String8(entry).string());
   3480             return NULL;
   3481         }
   3482         c = new ConfigList(entry, sourcePos);
   3483         mConfigs.add(entry, c);
   3484         pos = (int)mOrderedConfigs.size();
   3485         mOrderedConfigs.add(c);
   3486         if (doSetIndex) {
   3487             c->setEntryIndex(pos);
   3488         }
   3489     }
   3490 
   3491     ConfigDescription cdesc;
   3492     if (config) cdesc = *config;
   3493 
   3494     sp<Entry> e = c->getEntries().valueFor(cdesc);
   3495     if (e == NULL) {
   3496         if (config != NULL) {
   3497             NOISY(printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
   3498                     "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
   3499                     "sw%ddp w%ddp h%ddp dir:%d\n",
   3500                       sourcePos.file.string(), sourcePos.line,
   3501                       config->mcc, config->mnc,
   3502                       config->language[0] ? config->language[0] : '-',
   3503                       config->language[1] ? config->language[1] : '-',
   3504                       config->country[0] ? config->country[0] : '-',
   3505                       config->country[1] ? config->country[1] : '-',
   3506                       config->orientation,
   3507                       config->touchscreen,
   3508                       config->density,
   3509                       config->keyboard,
   3510                       config->inputFlags,
   3511                       config->navigation,
   3512                       config->screenWidth,
   3513                       config->screenHeight,
   3514                       config->smallestScreenWidthDp,
   3515                       config->screenWidthDp,
   3516                       config->screenHeightDp,
   3517                       config->layoutDirection));
   3518         } else {
   3519             NOISY(printf("New entry at %s:%d: NULL config\n",
   3520                       sourcePos.file.string(), sourcePos.line));
   3521         }
   3522         e = new Entry(entry, sourcePos);
   3523         c->addEntry(cdesc, e);
   3524         /*
   3525         if (doSetIndex) {
   3526             if (pos < 0) {
   3527                 for (pos=0; pos<(int)mOrderedConfigs.size(); pos++) {
   3528                     if (mOrderedConfigs[pos] == c) {
   3529                         break;
   3530                     }
   3531                 }
   3532                 if (pos >= (int)mOrderedConfigs.size()) {
   3533                     sourcePos.error("Internal error: config not found in mOrderedConfigs when adding entry");
   3534                     return NULL;
   3535                 }
   3536             }
   3537             e->setEntryIndex(pos);
   3538         }
   3539         */
   3540     }
   3541 
   3542     mUniqueConfigs.add(cdesc);
   3543 
   3544     return e;
   3545 }
   3546 
   3547 status_t ResourceTable::Type::applyPublicEntryOrder()
   3548 {
   3549     size_t N = mOrderedConfigs.size();
   3550     Vector<sp<ConfigList> > origOrder(mOrderedConfigs);
   3551     bool hasError = false;
   3552 
   3553     size_t i;
   3554     for (i=0; i<N; i++) {
   3555         mOrderedConfigs.replaceAt(NULL, i);
   3556     }
   3557 
   3558     const size_t NP = mPublic.size();
   3559     //printf("Ordering %d configs from %d public defs\n", N, NP);
   3560     size_t j;
   3561     for (j=0; j<NP; j++) {
   3562         const String16& name = mPublic.keyAt(j);
   3563         const Public& p = mPublic.valueAt(j);
   3564         int32_t idx = Res_GETENTRY(p.ident);
   3565         //printf("Looking for entry \"%s\"/\"%s\" (0x%08x) in %d...\n",
   3566         //       String8(mName).string(), String8(name).string(), p.ident, N);
   3567         bool found = false;
   3568         for (i=0; i<N; i++) {
   3569             sp<ConfigList> e = origOrder.itemAt(i);
   3570             //printf("#%d: \"%s\"\n", i, String8(e->getName()).string());
   3571             if (e->getName() == name) {
   3572                 if (idx >= (int32_t)mOrderedConfigs.size()) {
   3573                     p.sourcePos.error("Public entry identifier 0x%x entry index "
   3574                             "is larger than available symbols (index %d, total symbols %d).\n",
   3575                             p.ident, idx, mOrderedConfigs.size());
   3576                     hasError = true;
   3577                 } else if (mOrderedConfigs.itemAt(idx) == NULL) {
   3578                     e->setPublic(true);
   3579                     e->setPublicSourcePos(p.sourcePos);
   3580                     mOrderedConfigs.replaceAt(e, idx);
   3581                     origOrder.removeAt(i);
   3582                     N--;
   3583                     found = true;
   3584                     break;
   3585                 } else {
   3586                     sp<ConfigList> oe = mOrderedConfigs.itemAt(idx);
   3587 
   3588                     p.sourcePos.error("Multiple entry names declared for public entry"
   3589                             " identifier 0x%x in type %s (%s vs %s).\n"
   3590                             "%s:%d: Originally defined here.",
   3591                             idx+1, String8(mName).string(),
   3592                             String8(oe->getName()).string(),
   3593                             String8(name).string(),
   3594                             oe->getPublicSourcePos().file.string(),
   3595                             oe->getPublicSourcePos().line);
   3596                     hasError = true;
   3597                 }
   3598             }
   3599         }
   3600 
   3601         if (!found) {
   3602             p.sourcePos.error("Public symbol %s/%s declared here is not defined.",
   3603                     String8(mName).string(), String8(name).string());
   3604             hasError = true;
   3605         }
   3606     }
   3607 
   3608     //printf("Copying back in %d non-public configs, have %d\n", N, origOrder.size());
   3609 
   3610     if (N != origOrder.size()) {
   3611         printf("Internal error: remaining private symbol count mismatch\n");
   3612         N = origOrder.size();
   3613     }
   3614 
   3615     j = 0;
   3616     for (i=0; i<N; i++) {
   3617         sp<ConfigList> e = origOrder.itemAt(i);
   3618         // There will always be enough room for the remaining entries.
   3619         while (mOrderedConfigs.itemAt(j) != NULL) {
   3620             j++;
   3621         }
   3622         mOrderedConfigs.replaceAt(e, j);
   3623         j++;
   3624     }
   3625 
   3626     return hasError ? UNKNOWN_ERROR : NO_ERROR;
   3627 }
   3628 
   3629 ResourceTable::Package::Package(const String16& name, ssize_t includedId)
   3630     : mName(name), mIncludedId(includedId),
   3631       mTypeStringsMapping(0xffffffff),
   3632       mKeyStringsMapping(0xffffffff)
   3633 {
   3634 }
   3635 
   3636 sp<ResourceTable::Type> ResourceTable::Package::getType(const String16& type,
   3637                                                         const SourcePos& sourcePos,
   3638                                                         bool doSetIndex)
   3639 {
   3640     sp<Type> t = mTypes.valueFor(type);
   3641     if (t == NULL) {
   3642         t = new Type(type, sourcePos);
   3643         mTypes.add(type, t);
   3644         mOrderedTypes.add(t);
   3645         if (doSetIndex) {
   3646             // For some reason the type's index is set to one plus the index
   3647             // in the mOrderedTypes list, rather than just the index.
   3648             t->setIndex(mOrderedTypes.size());
   3649         }
   3650     }
   3651     return t;
   3652 }
   3653 
   3654 status_t ResourceTable::Package::setTypeStrings(const sp<AaptFile>& data)
   3655 {
   3656     mTypeStringsData = data;
   3657     status_t err = setStrings(data, &mTypeStrings, &mTypeStringsMapping);
   3658     if (err != NO_ERROR) {
   3659         fprintf(stderr, "ERROR: Type string data is corrupt!\n");
   3660     }
   3661     return err;
   3662 }
   3663 
   3664 status_t ResourceTable::Package::setKeyStrings(const sp<AaptFile>& data)
   3665 {
   3666     mKeyStringsData = data;
   3667     status_t err = setStrings(data, &mKeyStrings, &mKeyStringsMapping);
   3668     if (err != NO_ERROR) {
   3669         fprintf(stderr, "ERROR: Key string data is corrupt!\n");
   3670     }
   3671     return err;
   3672 }
   3673 
   3674 status_t ResourceTable::Package::setStrings(const sp<AaptFile>& data,
   3675                                             ResStringPool* strings,
   3676                                             DefaultKeyedVector<String16, uint32_t>* mappings)
   3677 {
   3678     if (data->getData() == NULL) {
   3679         return UNKNOWN_ERROR;
   3680     }
   3681 
   3682     NOISY(aout << "Setting restable string pool: "
   3683           << HexDump(data->getData(), data->getSize()) << endl);
   3684 
   3685     status_t err = strings->setTo(data->getData(), data->getSize());
   3686     if (err == NO_ERROR) {
   3687         const size_t N = strings->size();
   3688         for (size_t i=0; i<N; i++) {
   3689             size_t len;
   3690             mappings->add(String16(strings->stringAt(i, &len)), i);
   3691         }
   3692     }
   3693     return err;
   3694 }
   3695 
   3696 status_t ResourceTable::Package::applyPublicTypeOrder()
   3697 {
   3698     size_t N = mOrderedTypes.size();
   3699     Vector<sp<Type> > origOrder(mOrderedTypes);
   3700 
   3701     size_t i;
   3702     for (i=0; i<N; i++) {
   3703         mOrderedTypes.replaceAt(NULL, i);
   3704     }
   3705 
   3706     for (i=0; i<N; i++) {
   3707         sp<Type> t = origOrder.itemAt(i);
   3708         int32_t idx = t->getPublicIndex();
   3709         if (idx > 0) {
   3710             idx--;
   3711             while (idx >= (int32_t)mOrderedTypes.size()) {
   3712                 mOrderedTypes.add();
   3713             }
   3714             if (mOrderedTypes.itemAt(idx) != NULL) {
   3715                 sp<Type> ot = mOrderedTypes.itemAt(idx);
   3716                 t->getFirstPublicSourcePos().error("Multiple type names declared for public type"
   3717                         " identifier 0x%x (%s vs %s).\n"
   3718                         "%s:%d: Originally defined here.",
   3719                         idx, String8(ot->getName()).string(),
   3720                         String8(t->getName()).string(),
   3721                         ot->getFirstPublicSourcePos().file.string(),
   3722                         ot->getFirstPublicSourcePos().line);
   3723                 return UNKNOWN_ERROR;
   3724             }
   3725             mOrderedTypes.replaceAt(t, idx);
   3726             origOrder.removeAt(i);
   3727             i--;
   3728             N--;
   3729         }
   3730     }
   3731 
   3732     size_t j=0;
   3733     for (i=0; i<N; i++) {
   3734         sp<Type> t = origOrder.itemAt(i);
   3735         // There will always be enough room for the remaining types.
   3736         while (mOrderedTypes.itemAt(j) != NULL) {
   3737             j++;
   3738         }
   3739         mOrderedTypes.replaceAt(t, j);
   3740     }
   3741 
   3742     return NO_ERROR;
   3743 }
   3744 
   3745 sp<ResourceTable::Package> ResourceTable::getPackage(const String16& package)
   3746 {
   3747     sp<Package> p = mPackages.valueFor(package);
   3748     if (p == NULL) {
   3749         if (mIsAppPackage) {
   3750             if (mHaveAppPackage) {
   3751                 fprintf(stderr, "Adding multiple application package resources; only one is allowed.\n"
   3752                                 "Use -x to create extended resources.\n");
   3753                 return NULL;
   3754             }
   3755             mHaveAppPackage = true;
   3756             p = new Package(package, 127);
   3757         } else {
   3758             p = new Package(package, mNextPackageId);
   3759         }
   3760         //printf("*** NEW PACKAGE: \"%s\" id=%d\n",
   3761         //       String8(package).string(), p->getAssignedId());
   3762         mPackages.add(package, p);
   3763         mOrderedPackages.add(p);
   3764         mNextPackageId++;
   3765     }
   3766     return p;
   3767 }
   3768 
   3769 sp<ResourceTable::Type> ResourceTable::getType(const String16& package,
   3770                                                const String16& type,
   3771                                                const SourcePos& sourcePos,
   3772                                                bool doSetIndex)
   3773 {
   3774     sp<Package> p = getPackage(package);
   3775     if (p == NULL) {
   3776         return NULL;
   3777     }
   3778     return p->getType(type, sourcePos, doSetIndex);
   3779 }
   3780 
   3781 sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package,
   3782                                                  const String16& type,
   3783                                                  const String16& name,
   3784                                                  const SourcePos& sourcePos,
   3785                                                  bool overlay,
   3786                                                  const ResTable_config* config,
   3787                                                  bool doSetIndex)
   3788 {
   3789     sp<Type> t = getType(package, type, sourcePos, doSetIndex);
   3790     if (t == NULL) {
   3791         return NULL;
   3792     }
   3793     return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay());
   3794 }
   3795 
   3796 sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
   3797                                                        const ResTable_config* config) const
   3798 {
   3799     int pid = Res_GETPACKAGE(resID)+1;
   3800     const size_t N = mOrderedPackages.size();
   3801     size_t i;
   3802     sp<Package> p;
   3803     for (i=0; i<N; i++) {
   3804         sp<Package> check = mOrderedPackages[i];
   3805         if (check->getAssignedId() == pid) {
   3806             p = check;
   3807             break;
   3808         }
   3809 
   3810     }
   3811     if (p == NULL) {
   3812         fprintf(stderr, "warning: Package not found for resource #%08x\n", resID);
   3813         return NULL;
   3814     }
   3815 
   3816     int tid = Res_GETTYPE(resID);
   3817     if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) {
   3818         fprintf(stderr, "warning: Type not found for resource #%08x\n", resID);
   3819         return NULL;
   3820     }
   3821     sp<Type> t = p->getOrderedTypes()[tid];
   3822 
   3823     int eid = Res_GETENTRY(resID);
   3824     if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) {
   3825         fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
   3826         return NULL;
   3827     }
   3828 
   3829     sp<ConfigList> c = t->getOrderedConfigs()[eid];
   3830     if (c == NULL) {
   3831         fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
   3832         return NULL;
   3833     }
   3834 
   3835     ConfigDescription cdesc;
   3836     if (config) cdesc = *config;
   3837     sp<Entry> e = c->getEntries().valueFor(cdesc);
   3838     if (c == NULL) {
   3839         fprintf(stderr, "warning: Entry configuration not found for resource #%08x\n", resID);
   3840         return NULL;
   3841     }
   3842 
   3843     return e;
   3844 }
   3845 
   3846 const ResourceTable::Item* ResourceTable::getItem(uint32_t resID, uint32_t attrID) const
   3847 {
   3848     sp<const Entry> e = getEntry(resID);
   3849     if (e == NULL) {
   3850         return NULL;
   3851     }
   3852 
   3853     const size_t N = e->getBag().size();
   3854     for (size_t i=0; i<N; i++) {
   3855         const Item& it = e->getBag().valueAt(i);
   3856         if (it.bagKeyId == 0) {
   3857             fprintf(stderr, "warning: ID not yet assigned to '%s' in bag '%s'\n",
   3858                     String8(e->getName()).string(),
   3859                     String8(e->getBag().keyAt(i)).string());
   3860         }
   3861         if (it.bagKeyId == attrID) {
   3862             return &it;
   3863         }
   3864     }
   3865 
   3866     return NULL;
   3867 }
   3868 
   3869 bool ResourceTable::getItemValue(
   3870     uint32_t resID, uint32_t attrID, Res_value* outValue)
   3871 {
   3872     const Item* item = getItem(resID, attrID);
   3873 
   3874     bool res = false;
   3875     if (item != NULL) {
   3876         if (item->evaluating) {
   3877             sp<const Entry> e = getEntry(resID);
   3878             const size_t N = e->getBag().size();
   3879             size_t i;
   3880             for (i=0; i<N; i++) {
   3881                 if (&e->getBag().valueAt(i) == item) {
   3882                     break;
   3883                 }
   3884             }
   3885             fprintf(stderr, "warning: Circular reference detected in key '%s' of bag '%s'\n",
   3886                     String8(e->getName()).string(),
   3887                     String8(e->getBag().keyAt(i)).string());
   3888             return false;
   3889         }
   3890         item->evaluating = true;
   3891         res = stringToValue(outValue, NULL, item->value, false, false, item->bagKeyId);
   3892         NOISY(
   3893             if (res) {
   3894                 printf("getItemValue of #%08x[#%08x] (%s): type=#%08x, data=#%08x\n",
   3895                        resID, attrID, String8(getEntry(resID)->getName()).string(),
   3896                        outValue->dataType, outValue->data);
   3897             } else {
   3898                 printf("getItemValue of #%08x[#%08x]: failed\n",
   3899                        resID, attrID);
   3900             }
   3901         );
   3902         item->evaluating = false;
   3903     }
   3904     return res;
   3905 }
   3906