• 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         char buf[1024];
2479         va_list ap;
2480         va_start(ap, fmt);
2481         vsnprintf(buf, sizeof(buf), fmt, ap);
2482         va_end(ap);
2483         ac->sourcePos.error("Error: %s (at '%s' with value '%s').\n",
2484                             buf, ac->attr.string(), ac->value.string());
2485     }
2486 }
2487 
getAttributeKeys(uint32_t attrID,Vector<String16> * outKeys)2488 bool ResourceTable::getAttributeKeys(
2489     uint32_t attrID, Vector<String16>* outKeys)
2490 {
2491     sp<const Entry> e = getEntry(attrID);
2492     if (e != NULL) {
2493         const size_t N = e->getBag().size();
2494         for (size_t i=0; i<N; i++) {
2495             const String16& key = e->getBag().keyAt(i);
2496             if (key.size() > 0 && key.string()[0] != '^') {
2497                 outKeys->add(key);
2498             }
2499         }
2500         return true;
2501     }
2502     return false;
2503 }
2504 
getAttributeEnum(uint32_t attrID,const char16_t * name,size_t nameLen,Res_value * outValue)2505 bool ResourceTable::getAttributeEnum(
2506     uint32_t attrID, const char16_t* name, size_t nameLen,
2507     Res_value* outValue)
2508 {
2509     //printf("getAttributeEnum #%08x %s\n", attrID, String8(name, nameLen).string());
2510     String16 nameStr(name, nameLen);
2511     sp<const Entry> e = getEntry(attrID);
2512     if (e != NULL) {
2513         const size_t N = e->getBag().size();
2514         for (size_t i=0; i<N; i++) {
2515             //printf("Comparing %s to %s\n", String8(name, nameLen).string(),
2516             //       String8(e->getBag().keyAt(i)).string());
2517             if (e->getBag().keyAt(i) == nameStr) {
2518                 return getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, outValue);
2519             }
2520         }
2521     }
2522     return false;
2523 }
2524 
getAttributeFlags(uint32_t attrID,const char16_t * name,size_t nameLen,Res_value * outValue)2525 bool ResourceTable::getAttributeFlags(
2526     uint32_t attrID, const char16_t* name, size_t nameLen,
2527     Res_value* outValue)
2528 {
2529     outValue->dataType = Res_value::TYPE_INT_HEX;
2530     outValue->data = 0;
2531 
2532     //printf("getAttributeFlags #%08x %s\n", attrID, String8(name, nameLen).string());
2533     String16 nameStr(name, nameLen);
2534     sp<const Entry> e = getEntry(attrID);
2535     if (e != NULL) {
2536         const size_t N = e->getBag().size();
2537 
2538         const char16_t* end = name + nameLen;
2539         const char16_t* pos = name;
2540         while (pos < end) {
2541             const char16_t* start = pos;
2542             while (pos < end && *pos != '|') {
2543                 pos++;
2544             }
2545 
2546             String16 nameStr(start, pos-start);
2547             size_t i;
2548             for (i=0; i<N; i++) {
2549                 //printf("Comparing \"%s\" to \"%s\"\n", String8(nameStr).string(),
2550                 //       String8(e->getBag().keyAt(i)).string());
2551                 if (e->getBag().keyAt(i) == nameStr) {
2552                     Res_value val;
2553                     bool got = getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, &val);
2554                     if (!got) {
2555                         return false;
2556                     }
2557                     //printf("Got value: 0x%08x\n", val.data);
2558                     outValue->data |= val.data;
2559                     break;
2560                 }
2561             }
2562 
2563             if (i >= N) {
2564                 // Didn't find this flag identifier.
2565                 return false;
2566             }
2567             pos++;
2568         }
2569 
2570         return true;
2571     }
2572     return false;
2573 }
2574 
assignResourceIds()2575 status_t ResourceTable::assignResourceIds()
2576 {
2577     const size_t N = mOrderedPackages.size();
2578     size_t pi;
2579     status_t firstError = NO_ERROR;
2580 
2581     // First generate all bag attributes and assign indices.
2582     for (pi=0; pi<N; pi++) {
2583         sp<Package> p = mOrderedPackages.itemAt(pi);
2584         if (p == NULL || p->getTypes().size() == 0) {
2585             // Empty, skip!
2586             continue;
2587         }
2588 
2589         if (mPackageType == System) {
2590             p->movePrivateAttrs();
2591         }
2592 
2593         // This has no sense for packages being built as AppFeature (aka with a non-zero offset).
2594         status_t err = p->applyPublicTypeOrder();
2595         if (err != NO_ERROR && firstError == NO_ERROR) {
2596             firstError = err;
2597         }
2598 
2599         // Generate attributes...
2600         const size_t N = p->getOrderedTypes().size();
2601         size_t ti;
2602         for (ti=0; ti<N; ti++) {
2603             sp<Type> t = p->getOrderedTypes().itemAt(ti);
2604             if (t == NULL) {
2605                 continue;
2606             }
2607             const size_t N = t->getOrderedConfigs().size();
2608             for (size_t ci=0; ci<N; ci++) {
2609                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2610                 if (c == NULL) {
2611                     continue;
2612                 }
2613                 const size_t N = c->getEntries().size();
2614                 for (size_t ei=0; ei<N; ei++) {
2615                     sp<Entry> e = c->getEntries().valueAt(ei);
2616                     if (e == NULL) {
2617                         continue;
2618                     }
2619                     status_t err = e->generateAttributes(this, p->getName());
2620                     if (err != NO_ERROR && firstError == NO_ERROR) {
2621                         firstError = err;
2622                     }
2623                 }
2624             }
2625         }
2626 
2627         uint32_t typeIdOffset = 0;
2628         if (mPackageType == AppFeature && p->getName() == mAssetsPackage) {
2629             typeIdOffset = mTypeIdOffset;
2630         }
2631 
2632         const SourcePos unknown(String8("????"), 0);
2633         sp<Type> attr = p->getType(String16("attr"), unknown);
2634 
2635         // Force creation of ID if we are building feature splits.
2636         // Auto-generated ID resources won't apply the type ID offset correctly unless
2637         // the offset is applied here first.
2638         // b/30607637
2639         if (mPackageType == AppFeature && p->getName() == mAssetsPackage) {
2640             sp<Type> id = p->getType(String16("id"), unknown);
2641         }
2642 
2643         // Assign indices...
2644         const size_t typeCount = p->getOrderedTypes().size();
2645         for (size_t ti = 0; ti < typeCount; ti++) {
2646             sp<Type> t = p->getOrderedTypes().itemAt(ti);
2647             if (t == NULL) {
2648                 continue;
2649             }
2650 
2651             err = t->applyPublicEntryOrder();
2652             if (err != NO_ERROR && firstError == NO_ERROR) {
2653                 firstError = err;
2654             }
2655 
2656             const size_t N = t->getOrderedConfigs().size();
2657             t->setIndex(ti + 1 + typeIdOffset);
2658 
2659             LOG_ALWAYS_FATAL_IF(ti == 0 && attr != t,
2660                                 "First type is not attr!");
2661 
2662             for (size_t ei=0; ei<N; ei++) {
2663                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ei);
2664                 if (c == NULL) {
2665                     continue;
2666                 }
2667                 c->setEntryIndex(ei);
2668             }
2669         }
2670 
2671 
2672         // Assign resource IDs to keys in bags...
2673         for (size_t ti = 0; ti < typeCount; ti++) {
2674             sp<Type> t = p->getOrderedTypes().itemAt(ti);
2675             if (t == NULL) {
2676                 continue;
2677             }
2678 
2679             const size_t N = t->getOrderedConfigs().size();
2680             for (size_t ci=0; ci<N; ci++) {
2681                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2682                 if (c == NULL) {
2683                     continue;
2684                 }
2685                 //printf("Ordered config #%d: %p\n", ci, c.get());
2686                 const size_t N = c->getEntries().size();
2687                 for (size_t ei=0; ei<N; ei++) {
2688                     sp<Entry> e = c->getEntries().valueAt(ei);
2689                     if (e == NULL) {
2690                         continue;
2691                     }
2692                     status_t err = e->assignResourceIds(this, p->getName());
2693                     if (err != NO_ERROR && firstError == NO_ERROR) {
2694                         firstError = err;
2695                     }
2696                 }
2697             }
2698         }
2699     }
2700     return firstError;
2701 }
2702 
addSymbols(const sp<AaptSymbols> & outSymbols,bool skipSymbolsWithoutDefaultLocalization)2703 status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols,
2704         bool skipSymbolsWithoutDefaultLocalization) {
2705     const size_t N = mOrderedPackages.size();
2706     const String8 defaultLocale;
2707     const String16 stringType("string");
2708     size_t pi;
2709 
2710     for (pi=0; pi<N; pi++) {
2711         sp<Package> p = mOrderedPackages.itemAt(pi);
2712         if (p->getTypes().size() == 0) {
2713             // Empty, skip!
2714             continue;
2715         }
2716 
2717         const size_t N = p->getOrderedTypes().size();
2718         size_t ti;
2719 
2720         for (ti=0; ti<N; ti++) {
2721             sp<Type> t = p->getOrderedTypes().itemAt(ti);
2722             if (t == NULL) {
2723                 continue;
2724             }
2725 
2726             const size_t N = t->getOrderedConfigs().size();
2727             sp<AaptSymbols> typeSymbols;
2728             if (t->getName() == String16(kAttrPrivateType)) {
2729                 typeSymbols = outSymbols->addNestedSymbol(String8("attr"), t->getPos());
2730             } else {
2731                 typeSymbols = outSymbols->addNestedSymbol(String8(t->getName()), t->getPos());
2732             }
2733 
2734             if (typeSymbols == NULL) {
2735                 return UNKNOWN_ERROR;
2736             }
2737 
2738             for (size_t ci=0; ci<N; ci++) {
2739                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2740                 if (c == NULL) {
2741                     continue;
2742                 }
2743                 uint32_t rid = getResId(p, t, ci);
2744                 if (rid == 0) {
2745                     return UNKNOWN_ERROR;
2746                 }
2747                 if (Res_GETPACKAGE(rid) + 1 == p->getAssignedId()) {
2748 
2749                     if (skipSymbolsWithoutDefaultLocalization &&
2750                             t->getName() == stringType) {
2751 
2752                         // Don't generate symbols for strings without a default localization.
2753                         if (mHasDefaultLocalization.find(c->getName())
2754                                 == mHasDefaultLocalization.end()) {
2755                             // printf("Skip symbol [%08x] %s\n", rid,
2756                             //          String8(c->getName()).string());
2757                             continue;
2758                         }
2759                     }
2760 
2761                     typeSymbols->addSymbol(String8(c->getName()), rid, c->getPos());
2762 
2763                     String16 comment(c->getComment());
2764                     typeSymbols->appendComment(String8(c->getName()), comment, c->getPos());
2765                     //printf("Type symbol [%08x] %s comment: %s\n", rid,
2766                     //        String8(c->getName()).string(), String8(comment).string());
2767                     comment = c->getTypeComment();
2768                     typeSymbols->appendTypeComment(String8(c->getName()), comment);
2769                 }
2770             }
2771         }
2772     }
2773     return NO_ERROR;
2774 }
2775 
2776 
2777 void
addLocalization(const String16 & name,const String8 & locale,const SourcePos & src)2778 ResourceTable::addLocalization(const String16& name, const String8& locale, const SourcePos& src)
2779 {
2780     mLocalizations[name][locale] = src;
2781 }
2782 
2783 void
addDefaultLocalization(const String16 & name)2784 ResourceTable::addDefaultLocalization(const String16& name)
2785 {
2786     mHasDefaultLocalization.insert(name);
2787 }
2788 
2789 
2790 /*!
2791  * Flag various sorts of localization problems.  '+' indicates checks already implemented;
2792  * '-' indicates checks that will be implemented in the future.
2793  *
2794  * + A localized string for which no default-locale version exists => warning
2795  * + A string for which no version in an explicitly-requested locale exists => warning
2796  * + A localized translation of an translateable="false" string => warning
2797  * - A localized string not provided in every locale used by the table
2798  */
2799 status_t
validateLocalizations(void)2800 ResourceTable::validateLocalizations(void)
2801 {
2802     status_t err = NO_ERROR;
2803     const String8 defaultLocale;
2804 
2805     // For all strings...
2806     for (const auto& nameIter : mLocalizations) {
2807         const std::map<String8, SourcePos>& configSrcMap = nameIter.second;
2808 
2809         // Look for strings with no default localization
2810         if (configSrcMap.count(defaultLocale) == 0) {
2811             SourcePos().warning("string '%s' has no default translation.",
2812                     String8(nameIter.first).string());
2813             if (mBundle->getVerbose()) {
2814                 for (const auto& locale : configSrcMap) {
2815                     locale.second.printf("locale %s found", locale.first.string());
2816                 }
2817             }
2818             // !!! TODO: throw an error here in some circumstances
2819         }
2820 
2821         // Check that all requested localizations are present for this string
2822         if (mBundle->getConfigurations().size() > 0 && mBundle->getRequireLocalization()) {
2823             const char* allConfigs = mBundle->getConfigurations().string();
2824             const char* start = allConfigs;
2825             const char* comma;
2826 
2827             std::set<String8> missingConfigs;
2828             AaptLocaleValue locale;
2829             do {
2830                 String8 config;
2831                 comma = strchr(start, ',');
2832                 if (comma != NULL) {
2833                     config.setTo(start, comma - start);
2834                     start = comma + 1;
2835                 } else {
2836                     config.setTo(start);
2837                 }
2838 
2839                 if (!locale.initFromFilterString(config)) {
2840                     continue;
2841                 }
2842 
2843                 // don't bother with the pseudolocale "en_XA" or "ar_XB"
2844                 if (config != "en_XA" && config != "ar_XB") {
2845                     if (configSrcMap.find(config) == configSrcMap.end()) {
2846                         // okay, no specific localization found.  it's possible that we are
2847                         // requiring a specific regional localization [e.g. de_DE] but there is an
2848                         // available string in the generic language localization [e.g. de];
2849                         // consider that string to have fulfilled the localization requirement.
2850                         String8 region(config.string(), 2);
2851                         if (configSrcMap.find(region) == configSrcMap.end() &&
2852                                 configSrcMap.count(defaultLocale) == 0) {
2853                             missingConfigs.insert(config);
2854                         }
2855                     }
2856                 }
2857             } while (comma != NULL);
2858 
2859             if (!missingConfigs.empty()) {
2860                 String8 configStr;
2861                 for (const auto& iter : missingConfigs) {
2862                     configStr.appendFormat(" %s", iter.string());
2863                 }
2864                 SourcePos().warning("string '%s' is missing %u required localizations:%s",
2865                         String8(nameIter.first).string(),
2866                         (unsigned int)missingConfigs.size(),
2867                         configStr.string());
2868             }
2869         }
2870     }
2871 
2872     return err;
2873 }
2874 
flatten(Bundle * bundle,const sp<const ResourceFilter> & filter,const sp<AaptFile> & dest,const bool isBase)2875 status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter,
2876         const sp<AaptFile>& dest,
2877         const bool isBase)
2878 {
2879     const ConfigDescription nullConfig;
2880 
2881     const size_t N = mOrderedPackages.size();
2882     size_t pi;
2883 
2884     const static String16 mipmap16("mipmap");
2885 
2886     bool useUTF8 = !bundle->getUTF16StringsOption();
2887 
2888     // The libraries this table references.
2889     Vector<sp<Package> > libraryPackages;
2890     const ResTable& table = mAssets->getIncludedResources();
2891     const size_t basePackageCount = table.getBasePackageCount();
2892     for (size_t i = 0; i < basePackageCount; i++) {
2893         size_t packageId = table.getBasePackageId(i);
2894         String16 packageName(table.getBasePackageName(i));
2895         if (packageId > 0x01 && packageId != 0x7f &&
2896                 packageName != String16("android")) {
2897             libraryPackages.add(sp<Package>(new Package(packageName, packageId)));
2898         }
2899     }
2900 
2901     // Iterate through all data, collecting all values (strings,
2902     // references, etc).
2903     StringPool valueStrings(useUTF8);
2904     Vector<sp<Entry> > allEntries;
2905     for (pi=0; pi<N; pi++) {
2906         sp<Package> p = mOrderedPackages.itemAt(pi);
2907         if (p->getTypes().size() == 0) {
2908             continue;
2909         }
2910 
2911         StringPool typeStrings(useUTF8);
2912         StringPool keyStrings(useUTF8);
2913 
2914         ssize_t stringsAdded = 0;
2915         const size_t N = p->getOrderedTypes().size();
2916         for (size_t ti=0; ti<N; ti++) {
2917             sp<Type> t = p->getOrderedTypes().itemAt(ti);
2918             if (t == NULL) {
2919                 typeStrings.add(String16("<empty>"), false);
2920                 stringsAdded++;
2921                 continue;
2922             }
2923 
2924             while (stringsAdded < t->getIndex() - 1) {
2925                 typeStrings.add(String16("<empty>"), false);
2926                 stringsAdded++;
2927             }
2928 
2929             const String16 typeName(t->getName());
2930             typeStrings.add(typeName, false);
2931             stringsAdded++;
2932 
2933             // This is a hack to tweak the sorting order of the final strings,
2934             // to put stuff that is generally not language-specific first.
2935             String8 configTypeName(typeName);
2936             if (configTypeName == "drawable" || configTypeName == "layout"
2937                     || configTypeName == "color" || configTypeName == "anim"
2938                     || configTypeName == "interpolator" || configTypeName == "animator"
2939                     || configTypeName == "xml" || configTypeName == "menu"
2940                     || configTypeName == "mipmap" || configTypeName == "raw") {
2941                 configTypeName = "1complex";
2942             } else {
2943                 configTypeName = "2value";
2944             }
2945 
2946             // mipmaps don't get filtered, so they will
2947             // allways end up in the base. Make sure they
2948             // don't end up in a split.
2949             if (typeName == mipmap16 && !isBase) {
2950                 continue;
2951             }
2952 
2953             const bool filterable = (typeName != mipmap16);
2954 
2955             const size_t N = t->getOrderedConfigs().size();
2956             for (size_t ci=0; ci<N; ci++) {
2957                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
2958                 if (c == NULL) {
2959                     continue;
2960                 }
2961                 const size_t N = c->getEntries().size();
2962                 for (size_t ei=0; ei<N; ei++) {
2963                     ConfigDescription config = c->getEntries().keyAt(ei);
2964                     if (filterable && !filter->match(config)) {
2965                         continue;
2966                     }
2967                     sp<Entry> e = c->getEntries().valueAt(ei);
2968                     if (e == NULL) {
2969                         continue;
2970                     }
2971                     e->setNameIndex(keyStrings.add(e->getName(), true));
2972 
2973                     status_t err = e->prepareFlatten(&valueStrings, this,
2974                             &configTypeName, &config);
2975                     if (err != NO_ERROR) {
2976                         return err;
2977                     }
2978                     allEntries.add(e);
2979                 }
2980             }
2981         }
2982 
2983         p->setTypeStrings(typeStrings.createStringBlock());
2984         p->setKeyStrings(keyStrings.createStringBlock());
2985     }
2986 
2987     if (bundle->getOutputAPKFile() != NULL) {
2988         // Now we want to sort the value strings for better locality.  This will
2989         // cause the positions of the strings to change, so we need to go back
2990         // through out resource entries and update them accordingly.  Only need
2991         // to do this if actually writing the output file.
2992         valueStrings.sortByConfig();
2993         for (pi=0; pi<allEntries.size(); pi++) {
2994             allEntries[pi]->remapStringValue(&valueStrings);
2995         }
2996     }
2997 
2998     ssize_t strAmt = 0;
2999 
3000     // Now build the array of package chunks.
3001     Vector<sp<AaptFile> > flatPackages;
3002     for (pi=0; pi<N; pi++) {
3003         sp<Package> p = mOrderedPackages.itemAt(pi);
3004         if (p->getTypes().size() == 0) {
3005             // Empty, skip!
3006             continue;
3007         }
3008 
3009         const size_t N = p->getTypeStrings().size();
3010 
3011         const size_t baseSize = sizeof(ResTable_package);
3012 
3013         // Start the package data.
3014         sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
3015         ResTable_package* header = (ResTable_package*)data->editData(baseSize);
3016         if (header == NULL) {
3017             fprintf(stderr, "ERROR: out of memory creating ResTable_package\n");
3018             return NO_MEMORY;
3019         }
3020         memset(header, 0, sizeof(*header));
3021         header->header.type = htods(RES_TABLE_PACKAGE_TYPE);
3022         header->header.headerSize = htods(sizeof(*header));
3023         header->id = htodl(static_cast<uint32_t>(p->getAssignedId()));
3024         strcpy16_htod(header->name, p->getName().string());
3025 
3026         // Write the string blocks.
3027         const size_t typeStringsStart = data->getSize();
3028         sp<AaptFile> strFile = p->getTypeStringsData();
3029         ssize_t amt = data->writeData(strFile->getData(), strFile->getSize());
3030         if (kPrintStringMetrics) {
3031             fprintf(stderr, "**** type strings: %zd\n", amt);
3032         }
3033         strAmt += amt;
3034         if (amt < 0) {
3035             return amt;
3036         }
3037         const size_t keyStringsStart = data->getSize();
3038         strFile = p->getKeyStringsData();
3039         amt = data->writeData(strFile->getData(), strFile->getSize());
3040         if (kPrintStringMetrics) {
3041             fprintf(stderr, "**** key strings: %zd\n", amt);
3042         }
3043         strAmt += amt;
3044         if (amt < 0) {
3045             return amt;
3046         }
3047 
3048         if (isBase) {
3049             status_t err = flattenLibraryTable(data, libraryPackages);
3050             if (err != NO_ERROR) {
3051                 fprintf(stderr, "ERROR: failed to write library table\n");
3052                 return err;
3053             }
3054         }
3055 
3056         // Build the type chunks inside of this package.
3057         for (size_t ti=0; ti<N; ti++) {
3058             // Retrieve them in the same order as the type string block.
3059             size_t len;
3060             String16 typeName(UnpackOptionalString(p->getTypeStrings().stringAt(ti), &len));
3061             sp<Type> t = p->getTypes().valueFor(typeName);
3062             LOG_ALWAYS_FATAL_IF(t == NULL && typeName != String16("<empty>"),
3063                                 "Type name %s not found",
3064                                 String8(typeName).string());
3065             if (t == NULL) {
3066                 continue;
3067             }
3068             const bool filterable = (typeName != mipmap16);
3069             const bool skipEntireType = (typeName == mipmap16 && !isBase);
3070 
3071             const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
3072 
3073             // Until a non-NO_ENTRY value has been written for a resource,
3074             // that resource is invalid; validResources[i] represents
3075             // the item at t->getOrderedConfigs().itemAt(i).
3076             Vector<bool> validResources;
3077             validResources.insertAt(false, 0, N);
3078 
3079             // First write the typeSpec chunk, containing information about
3080             // each resource entry in this type.
3081             {
3082                 const size_t typeSpecSize = sizeof(ResTable_typeSpec) + sizeof(uint32_t)*N;
3083                 const size_t typeSpecStart = data->getSize();
3084                 ResTable_typeSpec* tsHeader = (ResTable_typeSpec*)
3085                     (((uint8_t*)data->editData(typeSpecStart+typeSpecSize)) + typeSpecStart);
3086                 if (tsHeader == NULL) {
3087                     fprintf(stderr, "ERROR: out of memory creating ResTable_typeSpec\n");
3088                     return NO_MEMORY;
3089                 }
3090                 memset(tsHeader, 0, sizeof(*tsHeader));
3091                 tsHeader->header.type = htods(RES_TABLE_TYPE_SPEC_TYPE);
3092                 tsHeader->header.headerSize = htods(sizeof(*tsHeader));
3093                 tsHeader->header.size = htodl(typeSpecSize);
3094                 tsHeader->id = ti+1;
3095                 tsHeader->entryCount = htodl(N);
3096 
3097                 uint32_t* typeSpecFlags = (uint32_t*)
3098                     (((uint8_t*)data->editData())
3099                         + typeSpecStart + sizeof(ResTable_typeSpec));
3100                 memset(typeSpecFlags, 0, sizeof(uint32_t)*N);
3101 
3102                 for (size_t ei=0; ei<N; ei++) {
3103                     sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
3104                     if (cl == NULL) {
3105                         continue;
3106                     }
3107 
3108                     if (cl->getPublic()) {
3109                         typeSpecFlags[ei] |= htodl(ResTable_typeSpec::SPEC_PUBLIC);
3110                     }
3111 
3112                     if (skipEntireType) {
3113                         continue;
3114                     }
3115 
3116                     const size_t CN = cl->getEntries().size();
3117                     for (size_t ci=0; ci<CN; ci++) {
3118                         if (filterable && !filter->match(cl->getEntries().keyAt(ci))) {
3119                             continue;
3120                         }
3121                         for (size_t cj=ci+1; cj<CN; cj++) {
3122                             if (filterable && !filter->match(cl->getEntries().keyAt(cj))) {
3123                                 continue;
3124                             }
3125                             typeSpecFlags[ei] |= htodl(
3126                                 cl->getEntries().keyAt(ci).diff(cl->getEntries().keyAt(cj)));
3127                         }
3128                     }
3129                 }
3130             }
3131 
3132             if (skipEntireType) {
3133                 continue;
3134             }
3135 
3136             // We need to write one type chunk for each configuration for
3137             // which we have entries in this type.
3138             SortedVector<ConfigDescription> uniqueConfigs;
3139             if (t != NULL) {
3140                 uniqueConfigs = t->getUniqueConfigs();
3141             }
3142 
3143             const size_t typeSize = sizeof(ResTable_type) + sizeof(uint32_t)*N;
3144 
3145             const size_t NC = uniqueConfigs.size();
3146             for (size_t ci=0; ci<NC; ci++) {
3147                 const ConfigDescription& config = uniqueConfigs[ci];
3148 
3149                 if (kIsDebug) {
3150                     printf("Writing config %zu config: imsi:%d/%d lang:%c%c cnt:%c%c "
3151                         "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
3152                         "sw%ddp w%ddp h%ddp layout:%d\n",
3153                         ti + 1,
3154                         config.mcc, config.mnc,
3155                         config.language[0] ? config.language[0] : '-',
3156                         config.language[1] ? config.language[1] : '-',
3157                         config.country[0] ? config.country[0] : '-',
3158                         config.country[1] ? config.country[1] : '-',
3159                         config.orientation,
3160                         config.uiMode,
3161                         config.touchscreen,
3162                         config.density,
3163                         config.keyboard,
3164                         config.inputFlags,
3165                         config.navigation,
3166                         config.screenWidth,
3167                         config.screenHeight,
3168                         config.smallestScreenWidthDp,
3169                         config.screenWidthDp,
3170                         config.screenHeightDp,
3171                         config.screenLayout);
3172                 }
3173 
3174                 if (filterable && !filter->match(config)) {
3175                     continue;
3176                 }
3177 
3178                 const size_t typeStart = data->getSize();
3179 
3180                 ResTable_type* tHeader = (ResTable_type*)
3181                     (((uint8_t*)data->editData(typeStart+typeSize)) + typeStart);
3182                 if (tHeader == NULL) {
3183                     fprintf(stderr, "ERROR: out of memory creating ResTable_type\n");
3184                     return NO_MEMORY;
3185                 }
3186 
3187                 memset(tHeader, 0, sizeof(*tHeader));
3188                 tHeader->header.type = htods(RES_TABLE_TYPE_TYPE);
3189                 tHeader->header.headerSize = htods(sizeof(*tHeader));
3190                 tHeader->id = ti+1;
3191                 tHeader->entryCount = htodl(N);
3192                 tHeader->entriesStart = htodl(typeSize);
3193                 tHeader->config = config;
3194                 if (kIsDebug) {
3195                     printf("Writing type %zu config: imsi:%d/%d lang:%c%c cnt:%c%c "
3196                         "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
3197                         "sw%ddp w%ddp h%ddp layout:%d\n",
3198                         ti + 1,
3199                         tHeader->config.mcc, tHeader->config.mnc,
3200                         tHeader->config.language[0] ? tHeader->config.language[0] : '-',
3201                         tHeader->config.language[1] ? tHeader->config.language[1] : '-',
3202                         tHeader->config.country[0] ? tHeader->config.country[0] : '-',
3203                         tHeader->config.country[1] ? tHeader->config.country[1] : '-',
3204                         tHeader->config.orientation,
3205                         tHeader->config.uiMode,
3206                         tHeader->config.touchscreen,
3207                         tHeader->config.density,
3208                         tHeader->config.keyboard,
3209                         tHeader->config.inputFlags,
3210                         tHeader->config.navigation,
3211                         tHeader->config.screenWidth,
3212                         tHeader->config.screenHeight,
3213                         tHeader->config.smallestScreenWidthDp,
3214                         tHeader->config.screenWidthDp,
3215                         tHeader->config.screenHeightDp,
3216                         tHeader->config.screenLayout);
3217                 }
3218                 tHeader->config.swapHtoD();
3219 
3220                 // Build the entries inside of this type.
3221                 for (size_t ei=0; ei<N; ei++) {
3222                     sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
3223                     sp<Entry> e = NULL;
3224                     if (cl != NULL) {
3225                         e = cl->getEntries().valueFor(config);
3226                     }
3227 
3228                     // Set the offset for this entry in its type.
3229                     uint32_t* index = (uint32_t*)
3230                         (((uint8_t*)data->editData())
3231                             + typeStart + sizeof(ResTable_type));
3232                     if (e != NULL) {
3233                         index[ei] = htodl(data->getSize()-typeStart-typeSize);
3234 
3235                         // Create the entry.
3236                         ssize_t amt = e->flatten(bundle, data, cl->getPublic());
3237                         if (amt < 0) {
3238                             return amt;
3239                         }
3240                         validResources.editItemAt(ei) = true;
3241                     } else {
3242                         index[ei] = htodl(ResTable_type::NO_ENTRY);
3243                     }
3244                 }
3245 
3246                 // Fill in the rest of the type information.
3247                 tHeader = (ResTable_type*)
3248                     (((uint8_t*)data->editData()) + typeStart);
3249                 tHeader->header.size = htodl(data->getSize()-typeStart);
3250             }
3251 
3252             // If we're building splits, then each invocation of the flattening
3253             // step will have 'missing' entries. Don't warn/error for this case.
3254             if (bundle->getSplitConfigurations().isEmpty()) {
3255                 bool missing_entry = false;
3256                 const char* log_prefix = bundle->getErrorOnMissingConfigEntry() ?
3257                         "error" : "warning";
3258                 for (size_t i = 0; i < N; ++i) {
3259                     if (!validResources[i]) {
3260                         sp<ConfigList> c = t->getOrderedConfigs().itemAt(i);
3261                         if (c != NULL) {
3262                             fprintf(stderr, "%s: no entries written for %s/%s (0x%08zx)\n", log_prefix,
3263                                     String8(typeName).string(), String8(c->getName()).string(),
3264                                     Res_MAKEID(p->getAssignedId() - 1, ti, i));
3265                         }
3266                         missing_entry = true;
3267                     }
3268                 }
3269                 if (bundle->getErrorOnMissingConfigEntry() && missing_entry) {
3270                     fprintf(stderr, "Error: Missing entries, quit!\n");
3271                     return NOT_ENOUGH_DATA;
3272                 }
3273             }
3274         }
3275 
3276         // Fill in the rest of the package information.
3277         header = (ResTable_package*)data->editData();
3278         header->header.size = htodl(data->getSize());
3279         header->typeStrings = htodl(typeStringsStart);
3280         header->lastPublicType = htodl(p->getTypeStrings().size());
3281         header->keyStrings = htodl(keyStringsStart);
3282         header->lastPublicKey = htodl(p->getKeyStrings().size());
3283 
3284         flatPackages.add(data);
3285     }
3286 
3287     // And now write out the final chunks.
3288     const size_t dataStart = dest->getSize();
3289 
3290     {
3291         // blah
3292         ResTable_header header;
3293         memset(&header, 0, sizeof(header));
3294         header.header.type = htods(RES_TABLE_TYPE);
3295         header.header.headerSize = htods(sizeof(header));
3296         header.packageCount = htodl(flatPackages.size());
3297         status_t err = dest->writeData(&header, sizeof(header));
3298         if (err != NO_ERROR) {
3299             fprintf(stderr, "ERROR: out of memory creating ResTable_header\n");
3300             return err;
3301         }
3302     }
3303 
3304     ssize_t strStart = dest->getSize();
3305     status_t err = valueStrings.writeStringBlock(dest);
3306     if (err != NO_ERROR) {
3307         return err;
3308     }
3309 
3310     ssize_t amt = (dest->getSize()-strStart);
3311     strAmt += amt;
3312     if (kPrintStringMetrics) {
3313         fprintf(stderr, "**** value strings: %zd\n", amt);
3314         fprintf(stderr, "**** total strings: %zd\n", amt);
3315     }
3316 
3317     for (pi=0; pi<flatPackages.size(); pi++) {
3318         err = dest->writeData(flatPackages[pi]->getData(),
3319                               flatPackages[pi]->getSize());
3320         if (err != NO_ERROR) {
3321             fprintf(stderr, "ERROR: out of memory creating package chunk for ResTable_header\n");
3322             return err;
3323         }
3324     }
3325 
3326     ResTable_header* header = (ResTable_header*)
3327         (((uint8_t*)dest->getData()) + dataStart);
3328     header->header.size = htodl(dest->getSize() - dataStart);
3329 
3330     if (kPrintStringMetrics) {
3331         fprintf(stderr, "**** total resource table size: %zu / %zu%% strings\n",
3332                 dest->getSize(), (size_t)(strAmt*100)/dest->getSize());
3333     }
3334 
3335     return NO_ERROR;
3336 }
3337 
flattenLibraryTable(const sp<AaptFile> & dest,const Vector<sp<Package>> & libs)3338 status_t ResourceTable::flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs) {
3339     // Write out the library table if necessary
3340     if (libs.size() > 0) {
3341         if (kIsDebug) {
3342             fprintf(stderr, "Writing library reference table\n");
3343         }
3344 
3345         const size_t libStart = dest->getSize();
3346         const size_t count = libs.size();
3347         ResTable_lib_header* libHeader = (ResTable_lib_header*) dest->editDataInRange(
3348                 libStart, sizeof(ResTable_lib_header));
3349 
3350         memset(libHeader, 0, sizeof(*libHeader));
3351         libHeader->header.type = htods(RES_TABLE_LIBRARY_TYPE);
3352         libHeader->header.headerSize = htods(sizeof(*libHeader));
3353         libHeader->header.size = htodl(sizeof(*libHeader) + (sizeof(ResTable_lib_entry) * count));
3354         libHeader->count = htodl(count);
3355 
3356         // Write the library entries
3357         for (size_t i = 0; i < count; i++) {
3358             const size_t entryStart = dest->getSize();
3359             sp<Package> libPackage = libs[i];
3360             if (kIsDebug) {
3361                 fprintf(stderr, "  Entry %s -> 0x%02x\n",
3362                         String8(libPackage->getName()).string(),
3363                         (uint8_t)libPackage->getAssignedId());
3364             }
3365 
3366             ResTable_lib_entry* entry = (ResTable_lib_entry*) dest->editDataInRange(
3367                     entryStart, sizeof(ResTable_lib_entry));
3368             memset(entry, 0, sizeof(*entry));
3369             entry->packageId = htodl(libPackage->getAssignedId());
3370             strcpy16_htod(entry->packageName, libPackage->getName().string());
3371         }
3372     }
3373     return NO_ERROR;
3374 }
3375 
writePublicDefinitions(const String16 & package,FILE * fp)3376 void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp)
3377 {
3378     fprintf(fp,
3379     "<!-- This file contains <public> resource definitions for all\n"
3380     "     resources that were generated from the source data. -->\n"
3381     "\n"
3382     "<resources>\n");
3383 
3384     writePublicDefinitions(package, fp, true);
3385     writePublicDefinitions(package, fp, false);
3386 
3387     fprintf(fp,
3388     "\n"
3389     "</resources>\n");
3390 }
3391 
writePublicDefinitions(const String16 & package,FILE * fp,bool pub)3392 void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp, bool pub)
3393 {
3394     bool didHeader = false;
3395 
3396     sp<Package> pkg = mPackages.valueFor(package);
3397     if (pkg != NULL) {
3398         const size_t NT = pkg->getOrderedTypes().size();
3399         for (size_t i=0; i<NT; i++) {
3400             sp<Type> t = pkg->getOrderedTypes().itemAt(i);
3401             if (t == NULL) {
3402                 continue;
3403             }
3404 
3405             bool didType = false;
3406 
3407             const size_t NC = t->getOrderedConfigs().size();
3408             for (size_t j=0; j<NC; j++) {
3409                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(j);
3410                 if (c == NULL) {
3411                     continue;
3412                 }
3413 
3414                 if (c->getPublic() != pub) {
3415                     continue;
3416                 }
3417 
3418                 if (!didType) {
3419                     fprintf(fp, "\n");
3420                     didType = true;
3421                 }
3422                 if (!didHeader) {
3423                     if (pub) {
3424                         fprintf(fp,"  <!-- PUBLIC SECTION.  These resources have been declared public.\n");
3425                         fprintf(fp,"       Changes to these definitions will break binary compatibility. -->\n\n");
3426                     } else {
3427                         fprintf(fp,"  <!-- PRIVATE SECTION.  These resources have not been declared public.\n");
3428                         fprintf(fp,"       You can make them public my moving these lines into a file in res/values. -->\n\n");
3429                     }
3430                     didHeader = true;
3431                 }
3432                 if (!pub) {
3433                     const size_t NE = c->getEntries().size();
3434                     for (size_t k=0; k<NE; k++) {
3435                         const SourcePos& pos = c->getEntries().valueAt(k)->getPos();
3436                         if (pos.file != "") {
3437                             fprintf(fp,"  <!-- Declared at %s:%d -->\n",
3438                                     pos.file.string(), pos.line);
3439                         }
3440                     }
3441                 }
3442                 fprintf(fp, "  <public type=\"%s\" name=\"%s\" id=\"0x%08x\" />\n",
3443                         String8(t->getName()).string(),
3444                         String8(c->getName()).string(),
3445                         getResId(pkg, t, c->getEntryIndex()));
3446             }
3447         }
3448     }
3449 }
3450 
Item(const SourcePos & _sourcePos,bool _isId,const String16 & _value,const Vector<StringPool::entry_style_span> * _style,int32_t _format)3451 ResourceTable::Item::Item(const SourcePos& _sourcePos,
3452                           bool _isId,
3453                           const String16& _value,
3454                           const Vector<StringPool::entry_style_span>* _style,
3455                           int32_t _format)
3456     : sourcePos(_sourcePos)
3457     , isId(_isId)
3458     , value(_value)
3459     , format(_format)
3460     , bagKeyId(0)
3461     , evaluating(false)
3462 {
3463     if (_style) {
3464         style = *_style;
3465     }
3466 }
3467 
Entry(const Entry & entry)3468 ResourceTable::Entry::Entry(const Entry& entry)
3469     : RefBase()
3470     , mName(entry.mName)
3471     , mParent(entry.mParent)
3472     , mType(entry.mType)
3473     , mItem(entry.mItem)
3474     , mItemFormat(entry.mItemFormat)
3475     , mBag(entry.mBag)
3476     , mNameIndex(entry.mNameIndex)
3477     , mParentId(entry.mParentId)
3478     , mPos(entry.mPos) {}
3479 
operator =(const Entry & entry)3480 ResourceTable::Entry& ResourceTable::Entry::operator=(const Entry& entry) {
3481     mName = entry.mName;
3482     mParent = entry.mParent;
3483     mType = entry.mType;
3484     mItem = entry.mItem;
3485     mItemFormat = entry.mItemFormat;
3486     mBag = entry.mBag;
3487     mNameIndex = entry.mNameIndex;
3488     mParentId = entry.mParentId;
3489     mPos = entry.mPos;
3490     return *this;
3491 }
3492 
makeItABag(const SourcePos & sourcePos)3493 status_t ResourceTable::Entry::makeItABag(const SourcePos& sourcePos)
3494 {
3495     if (mType == TYPE_BAG) {
3496         return NO_ERROR;
3497     }
3498     if (mType == TYPE_UNKNOWN) {
3499         mType = TYPE_BAG;
3500         return NO_ERROR;
3501     }
3502     sourcePos.error("Resource entry %s is already defined as a single item.\n"
3503                     "%s:%d: Originally defined here.\n",
3504                     String8(mName).string(),
3505                     mItem.sourcePos.file.string(), mItem.sourcePos.line);
3506     return UNKNOWN_ERROR;
3507 }
3508 
setItem(const SourcePos & sourcePos,const String16 & value,const Vector<StringPool::entry_style_span> * style,int32_t format,const bool overwrite)3509 status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos,
3510                                        const String16& value,
3511                                        const Vector<StringPool::entry_style_span>* style,
3512                                        int32_t format,
3513                                        const bool overwrite)
3514 {
3515     Item item(sourcePos, false, value, style);
3516 
3517     if (mType == TYPE_BAG) {
3518         if (mBag.size() == 0) {
3519             sourcePos.error("Resource entry %s is already defined as a bag.",
3520                     String8(mName).string());
3521         } else {
3522             const Item& item(mBag.valueAt(0));
3523             sourcePos.error("Resource entry %s is already defined as a bag.\n"
3524                             "%s:%d: Originally defined here.\n",
3525                             String8(mName).string(),
3526                             item.sourcePos.file.string(), item.sourcePos.line);
3527         }
3528         return UNKNOWN_ERROR;
3529     }
3530     if ( (mType != TYPE_UNKNOWN) && (overwrite == false) ) {
3531         sourcePos.error("Resource entry %s is already defined.\n"
3532                         "%s:%d: Originally defined here.\n",
3533                         String8(mName).string(),
3534                         mItem.sourcePos.file.string(), mItem.sourcePos.line);
3535         return UNKNOWN_ERROR;
3536     }
3537 
3538     mType = TYPE_ITEM;
3539     mItem = item;
3540     mItemFormat = format;
3541     return NO_ERROR;
3542 }
3543 
addToBag(const SourcePos & sourcePos,const String16 & key,const String16 & value,const Vector<StringPool::entry_style_span> * style,bool replace,bool isId,int32_t format)3544 status_t ResourceTable::Entry::addToBag(const SourcePos& sourcePos,
3545                                         const String16& key, const String16& value,
3546                                         const Vector<StringPool::entry_style_span>* style,
3547                                         bool replace, bool isId, int32_t format)
3548 {
3549     status_t err = makeItABag(sourcePos);
3550     if (err != NO_ERROR) {
3551         return err;
3552     }
3553 
3554     Item item(sourcePos, isId, value, style, format);
3555 
3556     // XXX NOTE: there is an error if you try to have a bag with two keys,
3557     // one an attr and one an id, with the same name.  Not something we
3558     // currently ever have to worry about.
3559     ssize_t origKey = mBag.indexOfKey(key);
3560     if (origKey >= 0) {
3561         if (!replace) {
3562             const Item& item(mBag.valueAt(origKey));
3563             sourcePos.error("Resource entry %s already has bag item %s.\n"
3564                     "%s:%d: Originally defined here.\n",
3565                     String8(mName).string(), String8(key).string(),
3566                     item.sourcePos.file.string(), item.sourcePos.line);
3567             return UNKNOWN_ERROR;
3568         }
3569         //printf("Replacing %s with %s\n",
3570         //       String8(mBag.valueFor(key).value).string(), String8(value).string());
3571         mBag.replaceValueFor(key, item);
3572     }
3573 
3574     mBag.add(key, item);
3575     return NO_ERROR;
3576 }
3577 
removeFromBag(const String16 & key)3578 status_t ResourceTable::Entry::removeFromBag(const String16& key) {
3579     if (mType != Entry::TYPE_BAG) {
3580         return NO_ERROR;
3581     }
3582 
3583     if (mBag.removeItem(key) >= 0) {
3584         return NO_ERROR;
3585     }
3586     return UNKNOWN_ERROR;
3587 }
3588 
emptyBag(const SourcePos & sourcePos)3589 status_t ResourceTable::Entry::emptyBag(const SourcePos& sourcePos)
3590 {
3591     status_t err = makeItABag(sourcePos);
3592     if (err != NO_ERROR) {
3593         return err;
3594     }
3595 
3596     mBag.clear();
3597     return NO_ERROR;
3598 }
3599 
generateAttributes(ResourceTable * table,const String16 & package)3600 status_t ResourceTable::Entry::generateAttributes(ResourceTable* table,
3601                                                   const String16& package)
3602 {
3603     const String16 attr16("attr");
3604     const String16 id16("id");
3605     const size_t N = mBag.size();
3606     for (size_t i=0; i<N; i++) {
3607         const String16& key = mBag.keyAt(i);
3608         const Item& it = mBag.valueAt(i);
3609         if (it.isId) {
3610             if (!table->hasBagOrEntry(key, &id16, &package)) {
3611                 String16 value("false");
3612                 if (kIsDebug) {
3613                     fprintf(stderr, "Generating %s:id/%s\n",
3614                             String8(package).string(),
3615                             String8(key).string());
3616                 }
3617                 status_t err = table->addEntry(SourcePos(String8("<generated>"), 0), package,
3618                                                id16, key, value);
3619                 if (err != NO_ERROR) {
3620                     return err;
3621                 }
3622             }
3623         } else if (!table->hasBagOrEntry(key, &attr16, &package)) {
3624 
3625 #if 1
3626 //             fprintf(stderr, "ERROR: Bag attribute '%s' has not been defined.\n",
3627 //                     String8(key).string());
3628 //             const Item& item(mBag.valueAt(i));
3629 //             fprintf(stderr, "Referenced from file %s line %d\n",
3630 //                     item.sourcePos.file.string(), item.sourcePos.line);
3631 //             return UNKNOWN_ERROR;
3632 #else
3633             char numberStr[16];
3634             sprintf(numberStr, "%d", ResTable_map::TYPE_ANY);
3635             status_t err = table->addBag(SourcePos("<generated>", 0), package,
3636                                          attr16, key, String16(""),
3637                                          String16("^type"),
3638                                          String16(numberStr), NULL, NULL);
3639             if (err != NO_ERROR) {
3640                 return err;
3641             }
3642 #endif
3643         }
3644     }
3645     return NO_ERROR;
3646 }
3647 
assignResourceIds(ResourceTable * table,const String16 &)3648 status_t ResourceTable::Entry::assignResourceIds(ResourceTable* table,
3649                                                  const String16& /* package */)
3650 {
3651     bool hasErrors = false;
3652 
3653     if (mType == TYPE_BAG) {
3654         const char* errorMsg;
3655         const String16 style16("style");
3656         const String16 attr16("attr");
3657         const String16 id16("id");
3658         mParentId = 0;
3659         if (mParent.size() > 0) {
3660             mParentId = table->getResId(mParent, &style16, NULL, &errorMsg);
3661             if (mParentId == 0) {
3662                 mPos.error("Error retrieving parent for item: %s '%s'.\n",
3663                         errorMsg, String8(mParent).string());
3664                 hasErrors = true;
3665             }
3666         }
3667         const size_t N = mBag.size();
3668         for (size_t i=0; i<N; i++) {
3669             const String16& key = mBag.keyAt(i);
3670             Item& it = mBag.editValueAt(i);
3671             it.bagKeyId = table->getResId(key,
3672                     it.isId ? &id16 : &attr16, NULL, &errorMsg);
3673             //printf("Bag key of %s: #%08x\n", String8(key).string(), it.bagKeyId);
3674             if (it.bagKeyId == 0) {
3675                 it.sourcePos.error("Error: %s: %s '%s'.\n", errorMsg,
3676                         String8(it.isId ? id16 : attr16).string(),
3677                         String8(key).string());
3678                 hasErrors = true;
3679             }
3680         }
3681     }
3682     return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR;
3683 }
3684 
prepareFlatten(StringPool * strings,ResourceTable * table,const String8 * configTypeName,const ConfigDescription * config)3685 status_t ResourceTable::Entry::prepareFlatten(StringPool* strings, ResourceTable* table,
3686         const String8* configTypeName, const ConfigDescription* config)
3687 {
3688     if (mType == TYPE_ITEM) {
3689         Item& it = mItem;
3690         AccessorCookie ac(it.sourcePos, String8(mName), String8(it.value));
3691         if (!table->stringToValue(&it.parsedValue, strings,
3692                                   it.value, false, true, 0,
3693                                   &it.style, NULL, &ac, mItemFormat,
3694                                   configTypeName, config)) {
3695             return UNKNOWN_ERROR;
3696         }
3697     } else if (mType == TYPE_BAG) {
3698         const size_t N = mBag.size();
3699         for (size_t i=0; i<N; i++) {
3700             const String16& key = mBag.keyAt(i);
3701             Item& it = mBag.editValueAt(i);
3702             AccessorCookie ac(it.sourcePos, String8(key), String8(it.value));
3703             if (!table->stringToValue(&it.parsedValue, strings,
3704                                       it.value, false, true, it.bagKeyId,
3705                                       &it.style, NULL, &ac, it.format,
3706                                       configTypeName, config)) {
3707                 return UNKNOWN_ERROR;
3708             }
3709         }
3710     } else {
3711         mPos.error("Error: entry %s is not a single item or a bag.\n",
3712                    String8(mName).string());
3713         return UNKNOWN_ERROR;
3714     }
3715     return NO_ERROR;
3716 }
3717 
remapStringValue(StringPool * strings)3718 status_t ResourceTable::Entry::remapStringValue(StringPool* strings)
3719 {
3720     if (mType == TYPE_ITEM) {
3721         Item& it = mItem;
3722         if (it.parsedValue.dataType == Res_value::TYPE_STRING) {
3723             it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data);
3724         }
3725     } else if (mType == TYPE_BAG) {
3726         const size_t N = mBag.size();
3727         for (size_t i=0; i<N; i++) {
3728             Item& it = mBag.editValueAt(i);
3729             if (it.parsedValue.dataType == Res_value::TYPE_STRING) {
3730                 it.parsedValue.data = strings->mapOriginalPosToNewPos(it.parsedValue.data);
3731             }
3732         }
3733     } else {
3734         mPos.error("Error: entry %s is not a single item or a bag.\n",
3735                    String8(mName).string());
3736         return UNKNOWN_ERROR;
3737     }
3738     return NO_ERROR;
3739 }
3740 
flatten(Bundle *,const sp<AaptFile> & data,bool isPublic)3741 ssize_t ResourceTable::Entry::flatten(Bundle* /* bundle */, const sp<AaptFile>& data, bool isPublic)
3742 {
3743     size_t amt = 0;
3744     ResTable_entry header;
3745     memset(&header, 0, sizeof(header));
3746     header.full.size = htods(sizeof(header));
3747     const type ty = mType;
3748     if (ty == TYPE_BAG) {
3749         header.full.flags |= htods(header.FLAG_COMPLEX);
3750     }
3751     if (isPublic) {
3752         header.full.flags |= htods(header.FLAG_PUBLIC);
3753     }
3754     header.full.key.index = htodl(mNameIndex);
3755     if (ty != TYPE_BAG) {
3756         status_t err = data->writeData(&header, sizeof(header));
3757         if (err != NO_ERROR) {
3758             fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
3759             return err;
3760         }
3761 
3762         const Item& it = mItem;
3763         Res_value par;
3764         memset(&par, 0, sizeof(par));
3765         par.size = htods(it.parsedValue.size);
3766         par.dataType = it.parsedValue.dataType;
3767         par.res0 = it.parsedValue.res0;
3768         par.data = htodl(it.parsedValue.data);
3769         #if 0
3770         printf("Writing item (%s): type=%d, data=0x%x, res0=0x%x\n",
3771                String8(mName).string(), it.parsedValue.dataType,
3772                it.parsedValue.data, par.res0);
3773         #endif
3774         err = data->writeData(&par, it.parsedValue.size);
3775         if (err != NO_ERROR) {
3776             fprintf(stderr, "ERROR: out of memory creating Res_value\n");
3777             return err;
3778         }
3779         amt += it.parsedValue.size;
3780     } else {
3781         size_t N = mBag.size();
3782         size_t i;
3783         // Create correct ordering of items.
3784         KeyedVector<uint32_t, const Item*> items;
3785         for (i=0; i<N; i++) {
3786             const Item& it = mBag.valueAt(i);
3787             items.add(it.bagKeyId, &it);
3788         }
3789         N = items.size();
3790 
3791         ResTable_map_entry mapHeader;
3792         memcpy(&mapHeader, &header, sizeof(header));
3793         mapHeader.size = htods(sizeof(mapHeader));
3794         mapHeader.parent.ident = htodl(mParentId);
3795         mapHeader.count = htodl(N);
3796         status_t err = data->writeData(&mapHeader, sizeof(mapHeader));
3797         if (err != NO_ERROR) {
3798             fprintf(stderr, "ERROR: out of memory creating ResTable_entry\n");
3799             return err;
3800         }
3801 
3802         for (i=0; i<N; i++) {
3803             const Item& it = *items.valueAt(i);
3804             ResTable_map map;
3805             map.name.ident = htodl(it.bagKeyId);
3806             map.value.size = htods(it.parsedValue.size);
3807             map.value.dataType = it.parsedValue.dataType;
3808             map.value.res0 = it.parsedValue.res0;
3809             map.value.data = htodl(it.parsedValue.data);
3810             err = data->writeData(&map, sizeof(map));
3811             if (err != NO_ERROR) {
3812                 fprintf(stderr, "ERROR: out of memory creating Res_value\n");
3813                 return err;
3814             }
3815             amt += sizeof(map);
3816         }
3817     }
3818     return amt;
3819 }
3820 
appendComment(const String16 & comment,bool onlyIfEmpty)3821 void ResourceTable::ConfigList::appendComment(const String16& comment,
3822                                               bool onlyIfEmpty)
3823 {
3824     if (comment.size() <= 0) {
3825         return;
3826     }
3827     if (onlyIfEmpty && mComment.size() > 0) {
3828         return;
3829     }
3830     if (mComment.size() > 0) {
3831         mComment.append(String16("\n"));
3832     }
3833     mComment.append(comment);
3834 }
3835 
appendTypeComment(const String16 & comment)3836 void ResourceTable::ConfigList::appendTypeComment(const String16& comment)
3837 {
3838     if (comment.size() <= 0) {
3839         return;
3840     }
3841     if (mTypeComment.size() > 0) {
3842         mTypeComment.append(String16("\n"));
3843     }
3844     mTypeComment.append(comment);
3845 }
3846 
addPublic(const SourcePos & sourcePos,const String16 & name,const uint32_t ident)3847 status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos,
3848                                         const String16& name,
3849                                         const uint32_t ident)
3850 {
3851     #if 0
3852     int32_t entryIdx = Res_GETENTRY(ident);
3853     if (entryIdx < 0) {
3854         sourcePos.error("Public resource %s/%s has an invalid 0 identifier (0x%08x).\n",
3855                 String8(mName).string(), String8(name).string(), ident);
3856         return UNKNOWN_ERROR;
3857     }
3858     #endif
3859 
3860     int32_t typeIdx = Res_GETTYPE(ident);
3861     if (typeIdx >= 0) {
3862         typeIdx++;
3863         if (mPublicIndex > 0 && mPublicIndex != typeIdx) {
3864             sourcePos.error("Public resource %s/%s has conflicting type codes for its"
3865                     " public identifiers (0x%x vs 0x%x).\n",
3866                     String8(mName).string(), String8(name).string(),
3867                     mPublicIndex, typeIdx);
3868             return UNKNOWN_ERROR;
3869         }
3870         mPublicIndex = typeIdx;
3871     }
3872 
3873     if (mFirstPublicSourcePos == NULL) {
3874         mFirstPublicSourcePos = new SourcePos(sourcePos);
3875     }
3876 
3877     if (mPublic.indexOfKey(name) < 0) {
3878         mPublic.add(name, Public(sourcePos, String16(), ident));
3879     } else {
3880         Public& p = mPublic.editValueFor(name);
3881         if (p.ident != ident) {
3882             sourcePos.error("Public resource %s/%s has conflicting public identifiers"
3883                     " (0x%08x vs 0x%08x).\n"
3884                     "%s:%d: Originally defined here.\n",
3885                     String8(mName).string(), String8(name).string(), p.ident, ident,
3886                     p.sourcePos.file.string(), p.sourcePos.line);
3887             return UNKNOWN_ERROR;
3888         }
3889     }
3890 
3891     return NO_ERROR;
3892 }
3893 
canAddEntry(const String16 & name)3894 void ResourceTable::Type::canAddEntry(const String16& name)
3895 {
3896     mCanAddEntries.add(name);
3897 }
3898 
getEntry(const String16 & entry,const SourcePos & sourcePos,const ResTable_config * config,bool doSetIndex,bool overlay,bool autoAddOverlay)3899 sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
3900                                                        const SourcePos& sourcePos,
3901                                                        const ResTable_config* config,
3902                                                        bool doSetIndex,
3903                                                        bool overlay,
3904                                                        bool autoAddOverlay)
3905 {
3906     int pos = -1;
3907     sp<ConfigList> c = mConfigs.valueFor(entry);
3908     if (c == NULL) {
3909         if (overlay && !autoAddOverlay && mCanAddEntries.indexOf(entry) < 0) {
3910             sourcePos.error("Resource at %s appears in overlay but not"
3911                             " in the base package; use <add-resource> to add.\n",
3912                             String8(entry).string());
3913             return NULL;
3914         }
3915         c = new ConfigList(entry, sourcePos);
3916         mConfigs.add(entry, c);
3917         pos = (int)mOrderedConfigs.size();
3918         mOrderedConfigs.add(c);
3919         if (doSetIndex) {
3920             c->setEntryIndex(pos);
3921         }
3922     }
3923 
3924     ConfigDescription cdesc;
3925     if (config) cdesc = *config;
3926 
3927     sp<Entry> e = c->getEntries().valueFor(cdesc);
3928     if (e == NULL) {
3929         if (kIsDebug) {
3930             if (config != NULL) {
3931                 printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
3932                     "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
3933                     "sw%ddp w%ddp h%ddp layout:%d\n",
3934                       sourcePos.file.string(), sourcePos.line,
3935                       config->mcc, config->mnc,
3936                       config->language[0] ? config->language[0] : '-',
3937                       config->language[1] ? config->language[1] : '-',
3938                       config->country[0] ? config->country[0] : '-',
3939                       config->country[1] ? config->country[1] : '-',
3940                       config->orientation,
3941                       config->touchscreen,
3942                       config->density,
3943                       config->keyboard,
3944                       config->inputFlags,
3945                       config->navigation,
3946                       config->screenWidth,
3947                       config->screenHeight,
3948                       config->smallestScreenWidthDp,
3949                       config->screenWidthDp,
3950                       config->screenHeightDp,
3951                       config->screenLayout);
3952             } else {
3953                 printf("New entry at %s:%d: NULL config\n",
3954                         sourcePos.file.string(), sourcePos.line);
3955             }
3956         }
3957         e = new Entry(entry, sourcePos);
3958         c->addEntry(cdesc, e);
3959         /*
3960         if (doSetIndex) {
3961             if (pos < 0) {
3962                 for (pos=0; pos<(int)mOrderedConfigs.size(); pos++) {
3963                     if (mOrderedConfigs[pos] == c) {
3964                         break;
3965                     }
3966                 }
3967                 if (pos >= (int)mOrderedConfigs.size()) {
3968                     sourcePos.error("Internal error: config not found in mOrderedConfigs when adding entry");
3969                     return NULL;
3970                 }
3971             }
3972             e->setEntryIndex(pos);
3973         }
3974         */
3975     }
3976 
3977     return e;
3978 }
3979 
removeEntry(const String16 & entry)3980 sp<ResourceTable::ConfigList> ResourceTable::Type::removeEntry(const String16& entry) {
3981     ssize_t idx = mConfigs.indexOfKey(entry);
3982     if (idx < 0) {
3983         return NULL;
3984     }
3985 
3986     sp<ConfigList> removed = mConfigs.valueAt(idx);
3987     mConfigs.removeItemsAt(idx);
3988 
3989     Vector<sp<ConfigList> >::iterator iter = std::find(
3990             mOrderedConfigs.begin(), mOrderedConfigs.end(), removed);
3991     if (iter != mOrderedConfigs.end()) {
3992         mOrderedConfigs.erase(iter);
3993     }
3994 
3995     mPublic.removeItem(entry);
3996     return removed;
3997 }
3998 
getUniqueConfigs() const3999 SortedVector<ConfigDescription> ResourceTable::Type::getUniqueConfigs() const {
4000     SortedVector<ConfigDescription> unique;
4001     const size_t entryCount = mOrderedConfigs.size();
4002     for (size_t i = 0; i < entryCount; i++) {
4003         if (mOrderedConfigs[i] == NULL) {
4004             continue;
4005         }
4006         const DefaultKeyedVector<ConfigDescription, sp<Entry> >& configs =
4007                 mOrderedConfigs[i]->getEntries();
4008         const size_t configCount = configs.size();
4009         for (size_t j = 0; j < configCount; j++) {
4010             unique.add(configs.keyAt(j));
4011         }
4012     }
4013     return unique;
4014 }
4015 
applyPublicEntryOrder()4016 status_t ResourceTable::Type::applyPublicEntryOrder()
4017 {
4018     size_t N = mOrderedConfigs.size();
4019     Vector<sp<ConfigList> > origOrder(mOrderedConfigs);
4020     bool hasError = false;
4021 
4022     size_t i;
4023     for (i=0; i<N; i++) {
4024         mOrderedConfigs.replaceAt(NULL, i);
4025     }
4026 
4027     const size_t NP = mPublic.size();
4028     //printf("Ordering %d configs from %d public defs\n", N, NP);
4029     size_t j;
4030     for (j=0; j<NP; j++) {
4031         const String16& name = mPublic.keyAt(j);
4032         const Public& p = mPublic.valueAt(j);
4033         int32_t idx = Res_GETENTRY(p.ident);
4034         //printf("Looking for entry \"%s\"/\"%s\" (0x%08x) in %d...\n",
4035         //       String8(mName).string(), String8(name).string(), p.ident, N);
4036         bool found = false;
4037         for (i=0; i<N; i++) {
4038             sp<ConfigList> e = origOrder.itemAt(i);
4039             //printf("#%d: \"%s\"\n", i, String8(e->getName()).string());
4040             if (e->getName() == name) {
4041                 if (idx >= (int32_t)mOrderedConfigs.size()) {
4042                     mOrderedConfigs.resize(idx + 1);
4043                 }
4044 
4045                 if (mOrderedConfigs.itemAt(idx) == NULL) {
4046                     e->setPublic(true);
4047                     e->setPublicSourcePos(p.sourcePos);
4048                     mOrderedConfigs.replaceAt(e, idx);
4049                     origOrder.removeAt(i);
4050                     N--;
4051                     found = true;
4052                     break;
4053                 } else {
4054                     sp<ConfigList> oe = mOrderedConfigs.itemAt(idx);
4055 
4056                     p.sourcePos.error("Multiple entry names declared for public entry"
4057                             " identifier 0x%x in type %s (%s vs %s).\n"
4058                             "%s:%d: Originally defined here.",
4059                             idx+1, String8(mName).string(),
4060                             String8(oe->getName()).string(),
4061                             String8(name).string(),
4062                             oe->getPublicSourcePos().file.string(),
4063                             oe->getPublicSourcePos().line);
4064                     hasError = true;
4065                 }
4066             }
4067         }
4068 
4069         if (!found) {
4070             p.sourcePos.error("Public symbol %s/%s declared here is not defined.",
4071                     String8(mName).string(), String8(name).string());
4072             hasError = true;
4073         }
4074     }
4075 
4076     //printf("Copying back in %d non-public configs, have %d\n", N, origOrder.size());
4077 
4078     if (N != origOrder.size()) {
4079         printf("Internal error: remaining private symbol count mismatch\n");
4080         N = origOrder.size();
4081     }
4082 
4083     j = 0;
4084     for (i=0; i<N; i++) {
4085         const sp<ConfigList>& e = origOrder.itemAt(i);
4086         // There will always be enough room for the remaining entries.
4087         while (mOrderedConfigs.itemAt(j) != NULL) {
4088             j++;
4089         }
4090         mOrderedConfigs.replaceAt(e, j);
4091         j++;
4092     }
4093 
4094     return hasError ? STATUST(UNKNOWN_ERROR) : NO_ERROR;
4095 }
4096 
Package(const String16 & name,size_t packageId)4097 ResourceTable::Package::Package(const String16& name, size_t packageId)
4098     : mName(name), mPackageId(packageId),
4099       mTypeStringsMapping(0xffffffff),
4100       mKeyStringsMapping(0xffffffff)
4101 {
4102 }
4103 
getType(const String16 & type,const SourcePos & sourcePos,bool doSetIndex)4104 sp<ResourceTable::Type> ResourceTable::Package::getType(const String16& type,
4105                                                         const SourcePos& sourcePos,
4106                                                         bool doSetIndex)
4107 {
4108     sp<Type> t = mTypes.valueFor(type);
4109     if (t == NULL) {
4110         t = new Type(type, sourcePos);
4111         mTypes.add(type, t);
4112         mOrderedTypes.add(t);
4113         if (doSetIndex) {
4114             // For some reason the type's index is set to one plus the index
4115             // in the mOrderedTypes list, rather than just the index.
4116             t->setIndex(mOrderedTypes.size());
4117         }
4118     }
4119     return t;
4120 }
4121 
setTypeStrings(const sp<AaptFile> & data)4122 status_t ResourceTable::Package::setTypeStrings(const sp<AaptFile>& data)
4123 {
4124     status_t err = setStrings(data, &mTypeStrings, &mTypeStringsMapping);
4125     if (err != NO_ERROR) {
4126         fprintf(stderr, "ERROR: Type string data is corrupt!\n");
4127         return err;
4128     }
4129 
4130     // Retain a reference to the new data after we've successfully replaced
4131     // all uses of the old reference (in setStrings() ).
4132     mTypeStringsData = data;
4133     return NO_ERROR;
4134 }
4135 
setKeyStrings(const sp<AaptFile> & data)4136 status_t ResourceTable::Package::setKeyStrings(const sp<AaptFile>& data)
4137 {
4138     status_t err = setStrings(data, &mKeyStrings, &mKeyStringsMapping);
4139     if (err != NO_ERROR) {
4140         fprintf(stderr, "ERROR: Key string data is corrupt!\n");
4141         return err;
4142     }
4143 
4144     // Retain a reference to the new data after we've successfully replaced
4145     // all uses of the old reference (in setStrings() ).
4146     mKeyStringsData = data;
4147     return NO_ERROR;
4148 }
4149 
setStrings(const sp<AaptFile> & data,ResStringPool * strings,DefaultKeyedVector<String16,uint32_t> * mappings)4150 status_t ResourceTable::Package::setStrings(const sp<AaptFile>& data,
4151                                             ResStringPool* strings,
4152                                             DefaultKeyedVector<String16, uint32_t>* mappings)
4153 {
4154     if (data->getData() == NULL) {
4155         return UNKNOWN_ERROR;
4156     }
4157 
4158     status_t err = strings->setTo(data->getData(), data->getSize());
4159     if (err == NO_ERROR) {
4160         const size_t N = strings->size();
4161         for (size_t i=0; i<N; i++) {
4162             size_t len;
4163             mappings->add(String16(UnpackOptionalString(strings->stringAt(i), &len)), i);
4164         }
4165     }
4166     return err;
4167 }
4168 
applyPublicTypeOrder()4169 status_t ResourceTable::Package::applyPublicTypeOrder()
4170 {
4171     size_t N = mOrderedTypes.size();
4172     Vector<sp<Type> > origOrder(mOrderedTypes);
4173 
4174     size_t i;
4175     for (i=0; i<N; i++) {
4176         mOrderedTypes.replaceAt(NULL, i);
4177     }
4178 
4179     for (i=0; i<N; i++) {
4180         sp<Type> t = origOrder.itemAt(i);
4181         int32_t idx = t->getPublicIndex();
4182         if (idx > 0) {
4183             idx--;
4184             while (idx >= (int32_t)mOrderedTypes.size()) {
4185                 mOrderedTypes.add();
4186             }
4187             if (mOrderedTypes.itemAt(idx) != NULL) {
4188                 sp<Type> ot = mOrderedTypes.itemAt(idx);
4189                 t->getFirstPublicSourcePos().error("Multiple type names declared for public type"
4190                         " identifier 0x%x (%s vs %s).\n"
4191                         "%s:%d: Originally defined here.",
4192                         idx, String8(ot->getName()).string(),
4193                         String8(t->getName()).string(),
4194                         ot->getFirstPublicSourcePos().file.string(),
4195                         ot->getFirstPublicSourcePos().line);
4196                 return UNKNOWN_ERROR;
4197             }
4198             mOrderedTypes.replaceAt(t, idx);
4199             origOrder.removeAt(i);
4200             i--;
4201             N--;
4202         }
4203     }
4204 
4205     size_t j=0;
4206     for (i=0; i<N; i++) {
4207         const sp<Type>& t = origOrder.itemAt(i);
4208         // There will always be enough room for the remaining types.
4209         while (mOrderedTypes.itemAt(j) != NULL) {
4210             j++;
4211         }
4212         mOrderedTypes.replaceAt(t, j);
4213     }
4214 
4215     return NO_ERROR;
4216 }
4217 
movePrivateAttrs()4218 void ResourceTable::Package::movePrivateAttrs() {
4219     sp<Type> attr = mTypes.valueFor(String16("attr"));
4220     if (attr == NULL) {
4221         // Nothing to do.
4222         return;
4223     }
4224 
4225     Vector<sp<ConfigList> > privateAttrs;
4226 
4227     bool hasPublic = false;
4228     const Vector<sp<ConfigList> >& configs = attr->getOrderedConfigs();
4229     const size_t configCount = configs.size();
4230     for (size_t i = 0; i < configCount; i++) {
4231         if (configs[i] == NULL) {
4232             continue;
4233         }
4234 
4235         if (attr->isPublic(configs[i]->getName())) {
4236             hasPublic = true;
4237         } else {
4238             privateAttrs.add(configs[i]);
4239         }
4240     }
4241 
4242     // Only if we have public attributes do we create a separate type for
4243     // private attributes.
4244     if (!hasPublic) {
4245         return;
4246     }
4247 
4248     // Create a new type for private attributes.
4249     sp<Type> privateAttrType = getType(String16(kAttrPrivateType), SourcePos());
4250 
4251     const size_t privateAttrCount = privateAttrs.size();
4252     for (size_t i = 0; i < privateAttrCount; i++) {
4253         const sp<ConfigList>& cl = privateAttrs[i];
4254 
4255         // Remove the private attributes from their current type.
4256         attr->removeEntry(cl->getName());
4257 
4258         // Add it to the new type.
4259         const DefaultKeyedVector<ConfigDescription, sp<Entry> >& entries = cl->getEntries();
4260         const size_t entryCount = entries.size();
4261         for (size_t j = 0; j < entryCount; j++) {
4262             const sp<Entry>& oldEntry = entries[j];
4263             sp<Entry> entry = privateAttrType->getEntry(
4264                     cl->getName(), oldEntry->getPos(), &entries.keyAt(j));
4265             *entry = *oldEntry;
4266         }
4267 
4268         // Move the symbols to the new type.
4269 
4270     }
4271 }
4272 
getPackage(const String16 & package)4273 sp<ResourceTable::Package> ResourceTable::getPackage(const String16& package)
4274 {
4275     if (package != mAssetsPackage) {
4276         return NULL;
4277     }
4278     return mPackages.valueFor(package);
4279 }
4280 
getType(const String16 & package,const String16 & type,const SourcePos & sourcePos,bool doSetIndex)4281 sp<ResourceTable::Type> ResourceTable::getType(const String16& package,
4282                                                const String16& type,
4283                                                const SourcePos& sourcePos,
4284                                                bool doSetIndex)
4285 {
4286     sp<Package> p = getPackage(package);
4287     if (p == NULL) {
4288         return NULL;
4289     }
4290     return p->getType(type, sourcePos, doSetIndex);
4291 }
4292 
getEntry(const String16 & package,const String16 & type,const String16 & name,const SourcePos & sourcePos,bool overlay,const ResTable_config * config,bool doSetIndex)4293 sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package,
4294                                                  const String16& type,
4295                                                  const String16& name,
4296                                                  const SourcePos& sourcePos,
4297                                                  bool overlay,
4298                                                  const ResTable_config* config,
4299                                                  bool doSetIndex)
4300 {
4301     sp<Type> t = getType(package, type, sourcePos, doSetIndex);
4302     if (t == NULL) {
4303         return NULL;
4304     }
4305     return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay());
4306 }
4307 
getConfigList(const String16 & package,const String16 & type,const String16 & name) const4308 sp<ResourceTable::ConfigList> ResourceTable::getConfigList(const String16& package,
4309         const String16& type, const String16& name) const
4310 {
4311     const size_t packageCount = mOrderedPackages.size();
4312     for (size_t pi = 0; pi < packageCount; pi++) {
4313         const sp<Package>& p = mOrderedPackages[pi];
4314         if (p == NULL || p->getName() != package) {
4315             continue;
4316         }
4317 
4318         const Vector<sp<Type> >& types = p->getOrderedTypes();
4319         const size_t typeCount = types.size();
4320         for (size_t ti = 0; ti < typeCount; ti++) {
4321             const sp<Type>& t = types[ti];
4322             if (t == NULL || t->getName() != type) {
4323                 continue;
4324             }
4325 
4326             const Vector<sp<ConfigList> >& configs = t->getOrderedConfigs();
4327             const size_t configCount = configs.size();
4328             for (size_t ci = 0; ci < configCount; ci++) {
4329                 const sp<ConfigList>& cl = configs[ci];
4330                 if (cl == NULL || cl->getName() != name) {
4331                     continue;
4332                 }
4333 
4334                 return cl;
4335             }
4336         }
4337     }
4338     return NULL;
4339 }
4340 
getEntry(uint32_t resID,const ResTable_config * config) const4341 sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
4342                                                        const ResTable_config* config) const
4343 {
4344     size_t pid = Res_GETPACKAGE(resID)+1;
4345     const size_t N = mOrderedPackages.size();
4346     sp<Package> p;
4347     for (size_t i = 0; i < N; i++) {
4348         sp<Package> check = mOrderedPackages[i];
4349         if (check->getAssignedId() == pid) {
4350             p = check;
4351             break;
4352         }
4353 
4354     }
4355     if (p == NULL) {
4356         fprintf(stderr, "warning: Package not found for resource #%08x\n", resID);
4357         return NULL;
4358     }
4359 
4360     int tid = Res_GETTYPE(resID);
4361     if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) {
4362         fprintf(stderr, "warning: Type not found for resource #%08x\n", resID);
4363         return NULL;
4364     }
4365     sp<Type> t = p->getOrderedTypes()[tid];
4366 
4367     int eid = Res_GETENTRY(resID);
4368     if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) {
4369         fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
4370         return NULL;
4371     }
4372 
4373     sp<ConfigList> c = t->getOrderedConfigs()[eid];
4374     if (c == NULL) {
4375         fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
4376         return NULL;
4377     }
4378 
4379     ConfigDescription cdesc;
4380     if (config) cdesc = *config;
4381     sp<Entry> e = c->getEntries().valueFor(cdesc);
4382     if (c == NULL) {
4383         fprintf(stderr, "warning: Entry configuration not found for resource #%08x\n", resID);
4384         return NULL;
4385     }
4386 
4387     return e;
4388 }
4389 
getItem(uint32_t resID,uint32_t attrID) const4390 const ResourceTable::Item* ResourceTable::getItem(uint32_t resID, uint32_t attrID) const
4391 {
4392     sp<const Entry> e = getEntry(resID);
4393     if (e == NULL) {
4394         return NULL;
4395     }
4396 
4397     const size_t N = e->getBag().size();
4398     for (size_t i=0; i<N; i++) {
4399         const Item& it = e->getBag().valueAt(i);
4400         if (it.bagKeyId == 0) {
4401             fprintf(stderr, "warning: ID not yet assigned to '%s' in bag '%s'\n",
4402                     String8(e->getName()).string(),
4403                     String8(e->getBag().keyAt(i)).string());
4404         }
4405         if (it.bagKeyId == attrID) {
4406             return &it;
4407         }
4408     }
4409 
4410     return NULL;
4411 }
4412 
getItemValue(uint32_t resID,uint32_t attrID,Res_value * outValue)4413 bool ResourceTable::getItemValue(
4414     uint32_t resID, uint32_t attrID, Res_value* outValue)
4415 {
4416     const Item* item = getItem(resID, attrID);
4417 
4418     bool res = false;
4419     if (item != NULL) {
4420         if (item->evaluating) {
4421             sp<const Entry> e = getEntry(resID);
4422             const size_t N = e->getBag().size();
4423             size_t i;
4424             for (i=0; i<N; i++) {
4425                 if (&e->getBag().valueAt(i) == item) {
4426                     break;
4427                 }
4428             }
4429             fprintf(stderr, "warning: Circular reference detected in key '%s' of bag '%s'\n",
4430                     String8(e->getName()).string(),
4431                     String8(e->getBag().keyAt(i)).string());
4432             return false;
4433         }
4434         item->evaluating = true;
4435         res = stringToValue(outValue, NULL, item->value, false, false, item->bagKeyId);
4436         if (kIsDebug) {
4437             if (res) {
4438                 printf("getItemValue of #%08x[#%08x] (%s): type=#%08x, data=#%08x\n",
4439                        resID, attrID, String8(getEntry(resID)->getName()).string(),
4440                        outValue->dataType, outValue->data);
4441             } else {
4442                 printf("getItemValue of #%08x[#%08x]: failed\n",
4443                        resID, attrID);
4444             }
4445         }
4446         item->evaluating = false;
4447     }
4448     return res;
4449 }
4450 
4451 /**
4452  * Returns the SDK version at which the attribute was
4453  * made public, or -1 if the resource ID is not an attribute
4454  * or is not public.
4455  */
getPublicAttributeSdkLevel(uint32_t attrId) const4456 int ResourceTable::getPublicAttributeSdkLevel(uint32_t attrId) const {
4457     if (Res_GETPACKAGE(attrId) + 1 != 0x01 || Res_GETTYPE(attrId) + 1 != 0x01) {
4458         return -1;
4459     }
4460 
4461     uint32_t specFlags;
4462     if (!mAssets->getIncludedResources().getResourceFlags(attrId, &specFlags)) {
4463         return -1;
4464     }
4465 
4466     if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) {
4467         return -1;
4468     }
4469 
4470     const size_t entryId = Res_GETENTRY(attrId);
4471     if (entryId <= 0x021c) {
4472         return 1;
4473     } else if (entryId <= 0x021d) {
4474         return 2;
4475     } else if (entryId <= 0x0269) {
4476         return SDK_CUPCAKE;
4477     } else if (entryId <= 0x028d) {
4478         return SDK_DONUT;
4479     } else if (entryId <= 0x02ad) {
4480         return SDK_ECLAIR;
4481     } else if (entryId <= 0x02b3) {
4482         return SDK_ECLAIR_0_1;
4483     } else if (entryId <= 0x02b5) {
4484         return SDK_ECLAIR_MR1;
4485     } else if (entryId <= 0x02bd) {
4486         return SDK_FROYO;
4487     } else if (entryId <= 0x02cb) {
4488         return SDK_GINGERBREAD;
4489     } else if (entryId <= 0x0361) {
4490         return SDK_HONEYCOMB;
4491     } else if (entryId <= 0x0366) {
4492         return SDK_HONEYCOMB_MR1;
4493     } else if (entryId <= 0x03a6) {
4494         return SDK_HONEYCOMB_MR2;
4495     } else if (entryId <= 0x03ae) {
4496         return SDK_JELLY_BEAN;
4497     } else if (entryId <= 0x03cc) {
4498         return SDK_JELLY_BEAN_MR1;
4499     } else if (entryId <= 0x03da) {
4500         return SDK_JELLY_BEAN_MR2;
4501     } else if (entryId <= 0x03f1) {
4502         return SDK_KITKAT;
4503     } else if (entryId <= 0x03f6) {
4504         return SDK_KITKAT_WATCH;
4505     } else if (entryId <= 0x04ce) {
4506         return SDK_LOLLIPOP;
4507     } else {
4508         // Anything else is marked as defined in
4509         // SDK_LOLLIPOP_MR1 since after this
4510         // version no attribute compat work
4511         // needs to be done.
4512         return SDK_LOLLIPOP_MR1;
4513     }
4514 }
4515 
4516 /**
4517  * First check the Manifest, then check the command line flag.
4518  */
getMinSdkVersion(const Bundle * bundle)4519 static int getMinSdkVersion(const Bundle* bundle) {
4520     if (bundle->getManifestMinSdkVersion() != NULL && strlen(bundle->getManifestMinSdkVersion()) > 0) {
4521         return atoi(bundle->getManifestMinSdkVersion());
4522     } else if (bundle->getMinSdkVersion() != NULL && strlen(bundle->getMinSdkVersion()) > 0) {
4523         return atoi(bundle->getMinSdkVersion());
4524     }
4525     return 0;
4526 }
4527 
shouldGenerateVersionedResource(const sp<ResourceTable::ConfigList> & configList,const ConfigDescription & sourceConfig,const int sdkVersionToGenerate)4528 bool ResourceTable::shouldGenerateVersionedResource(
4529         const sp<ResourceTable::ConfigList>& configList,
4530         const ConfigDescription& sourceConfig,
4531         const int sdkVersionToGenerate) {
4532     assert(sdkVersionToGenerate > sourceConfig.sdkVersion);
4533     assert(configList != NULL);
4534     const DefaultKeyedVector<ConfigDescription, sp<ResourceTable::Entry>>& entries
4535             = configList->getEntries();
4536     ssize_t idx = entries.indexOfKey(sourceConfig);
4537 
4538     // The source config came from this list, so it should be here.
4539     assert(idx >= 0);
4540 
4541     // The next configuration either only varies in sdkVersion, or it is completely different
4542     // and therefore incompatible. If it is incompatible, we must generate the versioned resource.
4543 
4544     // NOTE: The ordering of configurations takes sdkVersion as higher precedence than other
4545     // qualifiers, so we need to iterate through the entire list to be sure there
4546     // are no higher sdk level versions of this resource.
4547     ConfigDescription tempConfig(sourceConfig);
4548     for (size_t i = static_cast<size_t>(idx) + 1; i < entries.size(); i++) {
4549         const ConfigDescription& nextConfig = entries.keyAt(i);
4550         tempConfig.sdkVersion = nextConfig.sdkVersion;
4551         if (tempConfig == nextConfig) {
4552             // The two configs are the same, check the sdk version.
4553             return sdkVersionToGenerate < nextConfig.sdkVersion;
4554         }
4555     }
4556 
4557     // No match was found, so we should generate the versioned resource.
4558     return true;
4559 }
4560 
4561 /**
4562  * Modifies the entries in the resource table to account for compatibility
4563  * issues with older versions of Android.
4564  *
4565  * This primarily handles the issue of private/public attribute clashes
4566  * in framework resources.
4567  *
4568  * AAPT has traditionally assigned resource IDs to public attributes,
4569  * and then followed those public definitions with private attributes.
4570  *
4571  * --- PUBLIC ---
4572  * | 0x01010234 | attr/color
4573  * | 0x01010235 | attr/background
4574  *
4575  * --- PRIVATE ---
4576  * | 0x01010236 | attr/secret
4577  * | 0x01010237 | attr/shhh
4578  *
4579  * Each release, when attributes are added, they take the place of the private
4580  * attributes and the private attributes are shifted down again.
4581  *
4582  * --- PUBLIC ---
4583  * | 0x01010234 | attr/color
4584  * | 0x01010235 | attr/background
4585  * | 0x01010236 | attr/shinyNewAttr
4586  * | 0x01010237 | attr/highlyValuedFeature
4587  *
4588  * --- PRIVATE ---
4589  * | 0x01010238 | attr/secret
4590  * | 0x01010239 | attr/shhh
4591  *
4592  * Platform code may look for private attributes set in a theme. If an app
4593  * compiled against a newer version of the platform uses a new public
4594  * attribute that happens to have the same ID as the private attribute
4595  * the older platform is expecting, then the behavior is undefined.
4596  *
4597  * We get around this by detecting any newly defined attributes (in L),
4598  * copy the resource into a -v21 qualified resource, and delete the
4599  * attribute from the original resource. This ensures that older platforms
4600  * don't see the new attribute, but when running on L+ platforms, the
4601  * attribute will be respected.
4602  */
modifyForCompat(const Bundle * bundle)4603 status_t ResourceTable::modifyForCompat(const Bundle* bundle) {
4604     const int minSdk = getMinSdkVersion(bundle);
4605     if (minSdk >= SDK_LOLLIPOP_MR1) {
4606         // Lollipop MR1 and up handles public attributes differently, no
4607         // need to do any compat modifications.
4608         return NO_ERROR;
4609     }
4610 
4611     const String16 attr16("attr");
4612 
4613     const size_t packageCount = mOrderedPackages.size();
4614     for (size_t pi = 0; pi < packageCount; pi++) {
4615         sp<Package> p = mOrderedPackages.itemAt(pi);
4616         if (p == NULL || p->getTypes().size() == 0) {
4617             // Empty, skip!
4618             continue;
4619         }
4620 
4621         const size_t typeCount = p->getOrderedTypes().size();
4622         for (size_t ti = 0; ti < typeCount; ti++) {
4623             sp<Type> t = p->getOrderedTypes().itemAt(ti);
4624             if (t == NULL) {
4625                 continue;
4626             }
4627 
4628             const size_t configCount = t->getOrderedConfigs().size();
4629             for (size_t ci = 0; ci < configCount; ci++) {
4630                 sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
4631                 if (c == NULL) {
4632                     continue;
4633                 }
4634 
4635                 Vector<key_value_pair_t<ConfigDescription, sp<Entry> > > entriesToAdd;
4636                 const DefaultKeyedVector<ConfigDescription, sp<Entry> >& entries =
4637                         c->getEntries();
4638                 const size_t entryCount = entries.size();
4639                 for (size_t ei = 0; ei < entryCount; ei++) {
4640                     const sp<Entry>& e = entries.valueAt(ei);
4641                     if (e == NULL || e->getType() != Entry::TYPE_BAG) {
4642                         continue;
4643                     }
4644 
4645                     const ConfigDescription& config = entries.keyAt(ei);
4646                     if (config.sdkVersion >= SDK_LOLLIPOP_MR1) {
4647                         continue;
4648                     }
4649 
4650                     KeyedVector<int, Vector<String16> > attributesToRemove;
4651                     const KeyedVector<String16, Item>& bag = e->getBag();
4652                     const size_t bagCount = bag.size();
4653                     for (size_t bi = 0; bi < bagCount; bi++) {
4654                         const uint32_t attrId = getResId(bag.keyAt(bi), &attr16);
4655                         const int sdkLevel = getPublicAttributeSdkLevel(attrId);
4656                         if (sdkLevel > 1 && sdkLevel > config.sdkVersion && sdkLevel > minSdk) {
4657                             AaptUtil::appendValue(attributesToRemove, sdkLevel, bag.keyAt(bi));
4658                         }
4659                     }
4660 
4661                     if (attributesToRemove.isEmpty()) {
4662                         continue;
4663                     }
4664 
4665                     const size_t sdkCount = attributesToRemove.size();
4666                     for (size_t i = 0; i < sdkCount; i++) {
4667                         const int sdkLevel = attributesToRemove.keyAt(i);
4668 
4669                         if (!shouldGenerateVersionedResource(c, config, sdkLevel)) {
4670                             // There is a style that will override this generated one.
4671                             continue;
4672                         }
4673 
4674                         // Duplicate the entry under the same configuration
4675                         // but with sdkVersion == sdkLevel.
4676                         ConfigDescription newConfig(config);
4677                         newConfig.sdkVersion = sdkLevel;
4678 
4679                         sp<Entry> newEntry = new Entry(*e);
4680 
4681                         // Remove all items that have a higher SDK level than
4682                         // the one we are synthesizing.
4683                         for (size_t j = 0; j < sdkCount; j++) {
4684                             if (j == i) {
4685                                 continue;
4686                             }
4687 
4688                             if (attributesToRemove.keyAt(j) > sdkLevel) {
4689                                 const size_t attrCount = attributesToRemove[j].size();
4690                                 for (size_t k = 0; k < attrCount; k++) {
4691                                     newEntry->removeFromBag(attributesToRemove[j][k]);
4692                                 }
4693                             }
4694                         }
4695 
4696                         entriesToAdd.add(key_value_pair_t<ConfigDescription, sp<Entry> >(
4697                                 newConfig, newEntry));
4698                     }
4699 
4700                     // Remove the attribute from the original.
4701                     for (size_t i = 0; i < attributesToRemove.size(); i++) {
4702                         for (size_t j = 0; j < attributesToRemove[i].size(); j++) {
4703                             e->removeFromBag(attributesToRemove[i][j]);
4704                         }
4705                     }
4706                 }
4707 
4708                 const size_t entriesToAddCount = entriesToAdd.size();
4709                 for (size_t i = 0; i < entriesToAddCount; i++) {
4710                     assert(entries.indexOfKey(entriesToAdd[i].key) < 0);
4711 
4712                     if (bundle->getVerbose()) {
4713                         entriesToAdd[i].value->getPos()
4714                                 .printf("using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.",
4715                                         entriesToAdd[i].key.sdkVersion,
4716                                         String8(p->getName()).string(),
4717                                         String8(t->getName()).string(),
4718                                         String8(entriesToAdd[i].value->getName()).string(),
4719                                         entriesToAdd[i].key.toString().string());
4720                     }
4721 
4722                     sp<Entry> newEntry = t->getEntry(c->getName(),
4723                             entriesToAdd[i].value->getPos(),
4724                             &entriesToAdd[i].key);
4725 
4726                     *newEntry = *entriesToAdd[i].value;
4727                 }
4728             }
4729         }
4730     }
4731     return NO_ERROR;
4732 }
4733 
4734 const String16 kTransitionElements[] = {
4735     String16("fade"),
4736     String16("changeBounds"),
4737     String16("slide"),
4738     String16("explode"),
4739     String16("changeImageTransform"),
4740     String16("changeTransform"),
4741     String16("changeClipBounds"),
4742     String16("autoTransition"),
4743     String16("recolor"),
4744     String16("changeScroll"),
4745     String16("transitionSet"),
4746     String16("transition"),
4747     String16("transitionManager"),
4748 };
4749 
IsTransitionElement(const String16 & name)4750 static bool IsTransitionElement(const String16& name) {
4751     for (int i = 0, size = sizeof(kTransitionElements) / sizeof(kTransitionElements[0]);
4752          i < size; ++i) {
4753         if (name == kTransitionElements[i]) {
4754             return true;
4755         }
4756     }
4757     return false;
4758 }
4759 
versionForCompat(const Bundle * bundle,const String16 & resourceName,const sp<AaptFile> & target,const sp<XMLNode> & root)4760 bool ResourceTable::versionForCompat(const Bundle* bundle, const String16& resourceName,
4761                                          const sp<AaptFile>& target, const sp<XMLNode>& root) {
4762     XMLNode* node = root.get();
4763     while (node->getType() != XMLNode::TYPE_ELEMENT) {
4764         // We're assuming the root element is what we're looking for, which can only be under a
4765         // bunch of namespace declarations.
4766         if (node->getChildren().size() != 1) {
4767           // Not sure what to do, bail.
4768           return false;
4769         }
4770         node = node->getChildren().itemAt(0).get();
4771     }
4772 
4773     if (node->getElementNamespace().size() != 0) {
4774         // Not something we care about.
4775         return false;
4776     }
4777 
4778     int versionedSdk = 0;
4779     if (node->getElementName() == String16("adaptive-icon")) {
4780         versionedSdk = SDK_O;
4781     }
4782 
4783     const int minSdkVersion = getMinSdkVersion(bundle);
4784     const ConfigDescription config(target->getGroupEntry().toParams());
4785     if (versionedSdk <= minSdkVersion || versionedSdk <= config.sdkVersion) {
4786         return false;
4787     }
4788 
4789     sp<ConfigList> cl = getConfigList(String16(mAssets->getPackage()),
4790             String16(target->getResourceType()), resourceName);
4791     if (!shouldGenerateVersionedResource(cl, config, versionedSdk)) {
4792         return false;
4793     }
4794 
4795     // Remove the original entry.
4796     cl->removeEntry(config);
4797 
4798     // We need to wholesale version this file.
4799     ConfigDescription newConfig(config);
4800     newConfig.sdkVersion = versionedSdk;
4801     sp<AaptFile> newFile = new AaptFile(target->getSourceFile(),
4802             AaptGroupEntry(newConfig), target->getResourceType());
4803     String8 resPath = String8::format("res/%s/%s.xml",
4804             newFile->getGroupEntry().toDirName(target->getResourceType()).string(),
4805             String8(resourceName).string());
4806     resPath.convertToResPath();
4807 
4808     // Add a resource table entry.
4809     addEntry(SourcePos(),
4810             String16(mAssets->getPackage()),
4811             String16(target->getResourceType()),
4812             resourceName,
4813             String16(resPath),
4814             NULL,
4815             &newConfig);
4816 
4817     // Schedule this to be compiled.
4818     CompileResourceWorkItem item;
4819     item.resourceName = resourceName;
4820     item.resPath = resPath;
4821     item.file = newFile;
4822     item.xmlRoot = root->clone();
4823     item.needsCompiling = true;
4824     mWorkQueue.push(item);
4825 
4826     // Now mark the old entry as deleted.
4827     return true;
4828 }
4829 
modifyForCompat(const Bundle * bundle,const String16 & resourceName,const sp<AaptFile> & target,const sp<XMLNode> & root)4830 status_t ResourceTable::modifyForCompat(const Bundle* bundle,
4831                                         const String16& resourceName,
4832                                         const sp<AaptFile>& target,
4833                                         const sp<XMLNode>& root) {
4834     const String16 vector16("vector");
4835     const String16 animatedVector16("animated-vector");
4836     const String16 pathInterpolator16("pathInterpolator");
4837     const String16 objectAnimator16("objectAnimator");
4838     const String16 gradient16("gradient");
4839     const String16 animatedSelector16("animated-selector");
4840 
4841     const int minSdk = getMinSdkVersion(bundle);
4842     if (minSdk >= SDK_LOLLIPOP_MR1) {
4843         // Lollipop MR1 and up handles public attributes differently, no
4844         // need to do any compat modifications.
4845         return NO_ERROR;
4846     }
4847 
4848     const ConfigDescription config(target->getGroupEntry().toParams());
4849     if (target->getResourceType() == "" || config.sdkVersion >= SDK_LOLLIPOP_MR1) {
4850         // Skip resources that have no type (AndroidManifest.xml) or are already version qualified
4851         // with v21 or higher.
4852         return NO_ERROR;
4853     }
4854 
4855     sp<XMLNode> newRoot = NULL;
4856     int sdkVersionToGenerate = SDK_LOLLIPOP_MR1;
4857 
4858     Vector<sp<XMLNode> > nodesToVisit;
4859     nodesToVisit.push(root);
4860     while (!nodesToVisit.isEmpty()) {
4861         sp<XMLNode> node = nodesToVisit.top();
4862         nodesToVisit.pop();
4863 
4864         if (bundle->getNoVersionVectors() && (node->getElementName() == vector16 ||
4865                     node->getElementName() == animatedVector16 ||
4866                     node->getElementName() == objectAnimator16 ||
4867                     node->getElementName() == pathInterpolator16 ||
4868                     node->getElementName() == gradient16 ||
4869                     node->getElementName() == animatedSelector16)) {
4870             // We were told not to version vector tags, so skip the children here.
4871             continue;
4872         }
4873 
4874         if (bundle->getNoVersionTransitions() && (IsTransitionElement(node->getElementName()))) {
4875             // We were told not to version transition tags, so skip the children here.
4876             continue;
4877         }
4878 
4879         const Vector<XMLNode::attribute_entry>& attrs = node->getAttributes();
4880         for (size_t i = 0; i < attrs.size(); i++) {
4881             const XMLNode::attribute_entry& attr = attrs[i];
4882             const int sdkLevel = getPublicAttributeSdkLevel(attr.nameResId);
4883             if (sdkLevel > 1 && sdkLevel > config.sdkVersion && sdkLevel > minSdk) {
4884                 if (newRoot == NULL) {
4885                     newRoot = root->clone();
4886                 }
4887 
4888                 // Find the smallest sdk version that we need to synthesize for
4889                 // and do that one. Subsequent versions will be processed on
4890                 // the next pass.
4891                 sdkVersionToGenerate = std::min(sdkLevel, sdkVersionToGenerate);
4892 
4893                 if (bundle->getVerbose()) {
4894                     SourcePos(node->getFilename(), node->getStartLineNumber()).printf(
4895                             "removing attribute %s%s%s from <%s>",
4896                             String8(attr.ns).string(),
4897                             (attr.ns.size() == 0 ? "" : ":"),
4898                             String8(attr.name).string(),
4899                             String8(node->getElementName()).string());
4900                 }
4901                 node->removeAttribute(i);
4902                 i--;
4903             }
4904         }
4905 
4906         // Schedule a visit to the children.
4907         const Vector<sp<XMLNode> >& children = node->getChildren();
4908         const size_t childCount = children.size();
4909         for (size_t i = 0; i < childCount; i++) {
4910             nodesToVisit.push(children[i]);
4911         }
4912     }
4913 
4914     if (newRoot == NULL) {
4915         return NO_ERROR;
4916     }
4917 
4918     // Look to see if we already have an overriding v21 configuration.
4919     sp<ConfigList> cl = getConfigList(String16(mAssets->getPackage()),
4920             String16(target->getResourceType()), resourceName);
4921     if (shouldGenerateVersionedResource(cl, config, sdkVersionToGenerate)) {
4922         // We don't have an overriding entry for v21, so we must duplicate this one.
4923         ConfigDescription newConfig(config);
4924         newConfig.sdkVersion = sdkVersionToGenerate;
4925         sp<AaptFile> newFile = new AaptFile(target->getSourceFile(),
4926                 AaptGroupEntry(newConfig), target->getResourceType());
4927         String8 resPath = String8::format("res/%s/%s.xml",
4928                 newFile->getGroupEntry().toDirName(target->getResourceType()).string(),
4929                 String8(resourceName).string());
4930         resPath.convertToResPath();
4931 
4932         // Add a resource table entry.
4933         if (bundle->getVerbose()) {
4934             SourcePos(target->getSourceFile(), -1).printf(
4935                     "using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.",
4936                     newConfig.sdkVersion,
4937                     mAssets->getPackage().string(),
4938                     newFile->getResourceType().string(),
4939                     String8(resourceName).string(),
4940                     newConfig.toString().string());
4941         }
4942 
4943         addEntry(SourcePos(),
4944                 String16(mAssets->getPackage()),
4945                 String16(target->getResourceType()),
4946                 resourceName,
4947                 String16(resPath),
4948                 NULL,
4949                 &newConfig);
4950 
4951         // Schedule this to be compiled.
4952         CompileResourceWorkItem item;
4953         item.resourceName = resourceName;
4954         item.resPath = resPath;
4955         item.file = newFile;
4956         item.xmlRoot = newRoot;
4957         item.needsCompiling = false;    // This step occurs after we parse/assign, so we don't need
4958                                         // to do it again.
4959         mWorkQueue.push(item);
4960     }
4961     return NO_ERROR;
4962 }
4963 
getDensityVaryingResources(KeyedVector<Symbol,Vector<SymbolDefinition>> & resources)4964 void ResourceTable::getDensityVaryingResources(
4965         KeyedVector<Symbol, Vector<SymbolDefinition> >& resources) {
4966     const ConfigDescription nullConfig;
4967 
4968     const size_t packageCount = mOrderedPackages.size();
4969     for (size_t p = 0; p < packageCount; p++) {
4970         const Vector<sp<Type> >& types = mOrderedPackages[p]->getOrderedTypes();
4971         const size_t typeCount = types.size();
4972         for (size_t t = 0; t < typeCount; t++) {
4973             const sp<Type>& type = types[t];
4974             if (type == NULL) {
4975                 continue;
4976             }
4977 
4978             const Vector<sp<ConfigList> >& configs = type->getOrderedConfigs();
4979             const size_t configCount = configs.size();
4980             for (size_t c = 0; c < configCount; c++) {
4981                 const sp<ConfigList>& configList = configs[c];
4982                 if (configList == NULL) {
4983                     continue;
4984                 }
4985 
4986                 const DefaultKeyedVector<ConfigDescription, sp<Entry> >& configEntries
4987                         = configList->getEntries();
4988                 const size_t configEntryCount = configEntries.size();
4989                 for (size_t ce = 0; ce < configEntryCount; ce++) {
4990                     const sp<Entry>& entry = configEntries.valueAt(ce);
4991                     if (entry == NULL) {
4992                         continue;
4993                     }
4994 
4995                     const ConfigDescription& config = configEntries.keyAt(ce);
4996                     if (AaptConfig::isDensityOnly(config)) {
4997                         // This configuration only varies with regards to density.
4998                         const Symbol symbol(
4999                                 mOrderedPackages[p]->getName(),
5000                                 type->getName(),
5001                                 configList->getName(),
5002                                 getResId(mOrderedPackages[p], types[t],
5003                                          configList->getEntryIndex()));
5004 
5005 
5006                         AaptUtil::appendValue(resources, symbol,
5007                                               SymbolDefinition(symbol, config, entry->getPos()));
5008                     }
5009                 }
5010             }
5011         }
5012     }
5013 }
5014 
buildNamespace(const String16 & package)5015 static String16 buildNamespace(const String16& package) {
5016     return String16("http://schemas.android.com/apk/res/") + package;
5017 }
5018 
findOnlyChildElement(const sp<XMLNode> & parent)5019 static sp<XMLNode> findOnlyChildElement(const sp<XMLNode>& parent) {
5020     const Vector<sp<XMLNode> >& children = parent->getChildren();
5021     sp<XMLNode> onlyChild;
5022     for (size_t i = 0; i < children.size(); i++) {
5023         if (children[i]->getType() != XMLNode::TYPE_CDATA) {
5024             if (onlyChild != NULL) {
5025                 return NULL;
5026             }
5027             onlyChild = children[i];
5028         }
5029     }
5030     return onlyChild;
5031 }
5032 
5033 /**
5034  * Detects use of the `bundle' format and extracts nested resources into their own top level
5035  * resources. The bundle format looks like this:
5036  *
5037  * <!-- res/drawable/bundle.xml -->
5038  * <animated-vector xmlns:aapt="http://schemas.android.com/aapt">
5039  *   <aapt:attr name="android:drawable">
5040  *     <vector android:width="60dp"
5041  *             android:height="60dp">
5042  *       <path android:name="v"
5043  *             android:fillColor="#000000"
5044  *             android:pathData="M300,70 l 0,-70 70,..." />
5045  *     </vector>
5046  *   </aapt:attr>
5047  * </animated-vector>
5048  *
5049  * When AAPT sees the <aapt:attr> tag, it will extract its single element and its children
5050  * into a new high-level resource, assigning it a name and ID. Then value of the `name`
5051  * attribute must be a resource attribute. That resource attribute is inserted into the parent
5052  * with the reference to the extracted resource as the value.
5053  *
5054  * <!-- res/drawable/bundle.xml -->
5055  * <animated-vector android:drawable="@drawable/bundle_1.xml">
5056  * </animated-vector>
5057  *
5058  * <!-- res/drawable/bundle_1.xml -->
5059  * <vector android:width="60dp"
5060  *         android:height="60dp">
5061  *   <path android:name="v"
5062  *         android:fillColor="#000000"
5063  *         android:pathData="M300,70 l 0,-70 70,..." />
5064  * </vector>
5065  */
processBundleFormat(const Bundle * bundle,const String16 & resourceName,const sp<AaptFile> & target,const sp<XMLNode> & root)5066 status_t ResourceTable::processBundleFormat(const Bundle* bundle,
5067                                             const String16& resourceName,
5068                                             const sp<AaptFile>& target,
5069                                             const sp<XMLNode>& root) {
5070     Vector<sp<XMLNode> > namespaces;
5071     if (root->getType() == XMLNode::TYPE_NAMESPACE) {
5072         namespaces.push(root);
5073     }
5074     return processBundleFormatImpl(bundle, resourceName, target, root, &namespaces);
5075 }
5076 
processBundleFormatImpl(const Bundle * bundle,const String16 & resourceName,const sp<AaptFile> & target,const sp<XMLNode> & parent,Vector<sp<XMLNode>> * namespaces)5077 status_t ResourceTable::processBundleFormatImpl(const Bundle* bundle,
5078                                                 const String16& resourceName,
5079                                                 const sp<AaptFile>& target,
5080                                                 const sp<XMLNode>& parent,
5081                                                 Vector<sp<XMLNode> >* namespaces) {
5082     const String16 kAaptNamespaceUri16("http://schemas.android.com/aapt");
5083     const String16 kName16("name");
5084     const String16 kAttr16("attr");
5085     const String16 kAssetPackage16(mAssets->getPackage());
5086 
5087     Vector<sp<XMLNode> >& children = parent->getChildren();
5088     for (size_t i = 0; i < children.size(); i++) {
5089         const sp<XMLNode>& child = children[i];
5090 
5091         if (child->getType() == XMLNode::TYPE_CDATA) {
5092             continue;
5093         } else if (child->getType() == XMLNode::TYPE_NAMESPACE) {
5094             namespaces->push(child);
5095         }
5096 
5097         if (child->getElementNamespace() != kAaptNamespaceUri16 ||
5098                 child->getElementName() != kAttr16) {
5099             status_t result = processBundleFormatImpl(bundle, resourceName, target, child,
5100                                                       namespaces);
5101             if (result != NO_ERROR) {
5102                 return result;
5103             }
5104 
5105             if (child->getType() == XMLNode::TYPE_NAMESPACE) {
5106                 namespaces->pop();
5107             }
5108             continue;
5109         }
5110 
5111         // This is the <aapt:attr> tag. Look for the 'name' attribute.
5112         SourcePos source(child->getFilename(), child->getStartLineNumber());
5113 
5114         sp<XMLNode> nestedRoot = findOnlyChildElement(child);
5115         if (nestedRoot == NULL) {
5116             source.error("<%s:%s> must have exactly one child element",
5117                          String8(child->getElementNamespace()).string(),
5118                          String8(child->getElementName()).string());
5119             return UNKNOWN_ERROR;
5120         }
5121 
5122         // Find the special attribute 'parent-attr'. This attribute's value contains
5123         // the resource attribute for which this element should be assigned in the parent.
5124         const XMLNode::attribute_entry* attr = child->getAttribute(String16(), kName16);
5125         if (attr == NULL) {
5126             source.error("inline resource definition must specify an attribute via 'name'");
5127             return UNKNOWN_ERROR;
5128         }
5129 
5130         // Parse the attribute name.
5131         const char* errorMsg = NULL;
5132         String16 attrPackage, attrType, attrName;
5133         bool result = ResTable::expandResourceRef(attr->string.string(),
5134                                                   attr->string.size(),
5135                                                   &attrPackage, &attrType, &attrName,
5136                                                   &kAttr16, &kAssetPackage16,
5137                                                   &errorMsg, NULL);
5138         if (!result) {
5139             source.error("invalid attribute name for 'name': %s", errorMsg);
5140             return UNKNOWN_ERROR;
5141         }
5142 
5143         if (attrType != kAttr16) {
5144             // The value of the 'name' attribute must be an attribute reference.
5145             source.error("value of 'name' must be an attribute reference.");
5146             return UNKNOWN_ERROR;
5147         }
5148 
5149         // Generate a name for this nested resource and try to add it to the table.
5150         // We do this in a loop because the name may be taken, in which case we will
5151         // increment a suffix until we succeed.
5152         String8 nestedResourceName;
5153         String8 nestedResourcePath;
5154         int suffix = 1;
5155         while (true) {
5156             // This child element will be extracted into its own resource file.
5157             // Generate a name and path for it from its parent.
5158             nestedResourceName = String8::format("%s_%d",
5159                         String8(resourceName).string(), suffix++);
5160             nestedResourcePath = String8::format("res/%s/%s.xml",
5161                         target->getGroupEntry().toDirName(target->getResourceType())
5162                                                .string(),
5163                         nestedResourceName.string());
5164 
5165             // Lookup or create the entry for this name.
5166             sp<Entry> entry = getEntry(kAssetPackage16,
5167                                        String16(target->getResourceType()),
5168                                        String16(nestedResourceName),
5169                                        source,
5170                                        false,
5171                                        &target->getGroupEntry().toParams(),
5172                                        true);
5173             if (entry == NULL) {
5174                 return UNKNOWN_ERROR;
5175             }
5176 
5177             if (entry->getType() == Entry::TYPE_UNKNOWN) {
5178                 // The value for this resource has never been set,
5179                 // meaning we're good!
5180                 entry->setItem(source, String16(nestedResourcePath));
5181                 break;
5182             }
5183 
5184             // We failed (name already exists), so try with a different name
5185             // (increment the suffix).
5186         }
5187 
5188         if (bundle->getVerbose()) {
5189             source.printf("generating nested resource %s:%s/%s",
5190                     mAssets->getPackage().string(), target->getResourceType().string(),
5191                     nestedResourceName.string());
5192         }
5193 
5194         // Build the attribute reference and assign it to the parent.
5195         String16 nestedResourceRef = String16(String8::format("@%s:%s/%s",
5196                     mAssets->getPackage().string(), target->getResourceType().string(),
5197                     nestedResourceName.string()));
5198 
5199         String16 attrNs = buildNamespace(attrPackage);
5200         if (parent->getAttribute(attrNs, attrName) != NULL) {
5201             SourcePos(parent->getFilename(), parent->getStartLineNumber())
5202                     .error("parent of nested resource already defines attribute '%s:%s'",
5203                            String8(attrPackage).string(), String8(attrName).string());
5204             return UNKNOWN_ERROR;
5205         }
5206 
5207         // Add the reference to the inline resource.
5208         parent->addAttribute(attrNs, attrName, nestedResourceRef);
5209 
5210         // Remove the <aapt:attr> child element from here.
5211         children.removeAt(i);
5212         i--;
5213 
5214         // Append all namespace declarations that we've seen on this branch in the XML tree
5215         // to this resource.
5216         // We do this because the order of namespace declarations and prefix usage is determined
5217         // by the developer and we do not want to override any decisions. Be conservative.
5218         for (size_t nsIndex = namespaces->size(); nsIndex > 0; nsIndex--) {
5219             const sp<XMLNode>& ns = namespaces->itemAt(nsIndex - 1);
5220             sp<XMLNode> newNs = XMLNode::newNamespace(ns->getFilename(), ns->getNamespacePrefix(),
5221                                                       ns->getNamespaceUri());
5222             newNs->addChild(nestedRoot);
5223             nestedRoot = newNs;
5224         }
5225 
5226         // Schedule compilation of the nested resource.
5227         CompileResourceWorkItem workItem;
5228         workItem.resPath = nestedResourcePath;
5229         workItem.resourceName = String16(nestedResourceName);
5230         workItem.xmlRoot = nestedRoot;
5231         workItem.file = new AaptFile(target->getSourceFile(), target->getGroupEntry(),
5232                                      target->getResourceType());
5233         mWorkQueue.push(workItem);
5234     }
5235     return NO_ERROR;
5236 }
5237