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