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