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