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