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