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