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