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