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