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