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