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