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