• 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 #include "Main.h"
7 #include "AaptAssets.h"
8 #include "StringPool.h"
9 #include "XMLNode.h"
10 #include "ResourceTable.h"
11 #include "Images.h"
12 
13 #include "CrunchCache.h"
14 #include "FileFinder.h"
15 #include "CacheUpdater.h"
16 
17 #if HAVE_PRINTF_ZD
18 #  define ZD "%zd"
19 #  define ZD_TYPE ssize_t
20 #else
21 #  define ZD "%ld"
22 #  define ZD_TYPE long
23 #endif
24 
25 #define NOISY(x) // x
26 
27 // ==========================================================================
28 // ==========================================================================
29 // ==========================================================================
30 
31 class PackageInfo
32 {
33 public:
PackageInfo()34     PackageInfo()
35     {
36     }
~PackageInfo()37     ~PackageInfo()
38     {
39     }
40 
41     status_t parsePackage(const sp<AaptGroup>& grp);
42 };
43 
44 // ==========================================================================
45 // ==========================================================================
46 // ==========================================================================
47 
parseResourceName(const String8 & leaf)48 static String8 parseResourceName(const String8& leaf)
49 {
50     const char* firstDot = strchr(leaf.string(), '.');
51     const char* str = leaf.string();
52 
53     if (firstDot) {
54         return String8(str, firstDot-str);
55     } else {
56         return String8(str);
57     }
58 }
59 
ResourceTypeSet()60 ResourceTypeSet::ResourceTypeSet()
61     :RefBase(),
62      KeyedVector<String8,sp<AaptGroup> >()
63 {
64 }
65 
FilePathStore()66 FilePathStore::FilePathStore()
67     :RefBase(),
68      Vector<String8>()
69 {
70 }
71 
72 class ResourceDirIterator
73 {
74 public:
ResourceDirIterator(const sp<ResourceTypeSet> & set,const String8 & resType)75     ResourceDirIterator(const sp<ResourceTypeSet>& set, const String8& resType)
76         : mResType(resType), mSet(set), mSetPos(0), mGroupPos(0)
77     {
78     }
79 
getGroup() const80     inline const sp<AaptGroup>& getGroup() const { return mGroup; }
getFile() const81     inline const sp<AaptFile>& getFile() const { return mFile; }
82 
getBaseName() const83     inline const String8& getBaseName() const { return mBaseName; }
getLeafName() const84     inline const String8& getLeafName() const { return mLeafName; }
getPath() const85     inline String8 getPath() const { return mPath; }
getParams() const86     inline const ResTable_config& getParams() const { return mParams; }
87 
88     enum {
89         EOD = 1
90     };
91 
next()92     ssize_t next()
93     {
94         while (true) {
95             sp<AaptGroup> group;
96             sp<AaptFile> file;
97 
98             // Try to get next file in this current group.
99             if (mGroup != NULL && mGroupPos < mGroup->getFiles().size()) {
100                 group = mGroup;
101                 file = group->getFiles().valueAt(mGroupPos++);
102 
103             // Try to get the next group/file in this directory
104             } else if (mSetPos < mSet->size()) {
105                 mGroup = group = mSet->valueAt(mSetPos++);
106                 if (group->getFiles().size() < 1) {
107                     continue;
108                 }
109                 file = group->getFiles().valueAt(0);
110                 mGroupPos = 1;
111 
112             // All done!
113             } else {
114                 return EOD;
115             }
116 
117             mFile = file;
118 
119             String8 leaf(group->getLeaf());
120             mLeafName = String8(leaf);
121             mParams = file->getGroupEntry().toParams();
122             NOISY(printf("Dir %s: mcc=%d mnc=%d lang=%c%c cnt=%c%c orient=%d ui=%d density=%d touch=%d key=%d inp=%d nav=%d\n",
123                    group->getPath().string(), mParams.mcc, mParams.mnc,
124                    mParams.language[0] ? mParams.language[0] : '-',
125                    mParams.language[1] ? mParams.language[1] : '-',
126                    mParams.country[0] ? mParams.country[0] : '-',
127                    mParams.country[1] ? mParams.country[1] : '-',
128                    mParams.orientation, mParams.uiMode,
129                    mParams.density, mParams.touchscreen, mParams.keyboard,
130                    mParams.inputFlags, mParams.navigation));
131             mPath = "res";
132             mPath.appendPath(file->getGroupEntry().toDirName(mResType));
133             mPath.appendPath(leaf);
134             mBaseName = parseResourceName(leaf);
135             if (mBaseName == "") {
136                 fprintf(stderr, "Error: malformed resource filename %s\n",
137                         file->getPrintableSource().string());
138                 return UNKNOWN_ERROR;
139             }
140 
141             NOISY(printf("file name=%s\n", mBaseName.string()));
142 
143             return NO_ERROR;
144         }
145     }
146 
147 private:
148     String8 mResType;
149 
150     const sp<ResourceTypeSet> mSet;
151     size_t mSetPos;
152 
153     sp<AaptGroup> mGroup;
154     size_t mGroupPos;
155 
156     sp<AaptFile> mFile;
157     String8 mBaseName;
158     String8 mLeafName;
159     String8 mPath;
160     ResTable_config mParams;
161 };
162 
163 // ==========================================================================
164 // ==========================================================================
165 // ==========================================================================
166 
isValidResourceType(const String8 & type)167 bool isValidResourceType(const String8& type)
168 {
169     return type == "anim" || type == "animator" || type == "interpolator"
170         || type == "drawable" || type == "layout"
171         || type == "values" || type == "xml" || type == "raw"
172         || type == "color" || type == "menu" || type == "mipmap";
173 }
174 
getResourceFile(const sp<AaptAssets> & assets,bool makeIfNecessary=true)175 static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true)
176 {
177     sp<AaptGroup> group = assets->getFiles().valueFor(String8("resources.arsc"));
178     sp<AaptFile> file;
179     if (group != NULL) {
180         file = group->getFiles().valueFor(AaptGroupEntry());
181         if (file != NULL) {
182             return file;
183         }
184     }
185 
186     if (!makeIfNecessary) {
187         return NULL;
188     }
189     return assets->addFile(String8("resources.arsc"), AaptGroupEntry(), String8(),
190                             NULL, String8());
191 }
192 
parsePackage(Bundle * bundle,const sp<AaptAssets> & assets,const sp<AaptGroup> & grp)193 static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets,
194     const sp<AaptGroup>& grp)
195 {
196     if (grp->getFiles().size() != 1) {
197         fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
198                 grp->getFiles().valueAt(0)->getPrintableSource().string());
199     }
200 
201     sp<AaptFile> file = grp->getFiles().valueAt(0);
202 
203     ResXMLTree block;
204     status_t err = parseXMLResource(file, &block);
205     if (err != NO_ERROR) {
206         return err;
207     }
208     //printXMLBlock(&block);
209 
210     ResXMLTree::event_code_t code;
211     while ((code=block.next()) != ResXMLTree::START_TAG
212            && code != ResXMLTree::END_DOCUMENT
213            && code != ResXMLTree::BAD_DOCUMENT) {
214     }
215 
216     size_t len;
217     if (code != ResXMLTree::START_TAG) {
218         fprintf(stderr, "%s:%d: No start tag found\n",
219                 file->getPrintableSource().string(), block.getLineNumber());
220         return UNKNOWN_ERROR;
221     }
222     if (strcmp16(block.getElementName(&len), String16("manifest").string()) != 0) {
223         fprintf(stderr, "%s:%d: Invalid start tag %s, expected <manifest>\n",
224                 file->getPrintableSource().string(), block.getLineNumber(),
225                 String8(block.getElementName(&len)).string());
226         return UNKNOWN_ERROR;
227     }
228 
229     ssize_t nameIndex = block.indexOfAttribute(NULL, "package");
230     if (nameIndex < 0) {
231         fprintf(stderr, "%s:%d: <manifest> does not have package attribute.\n",
232                 file->getPrintableSource().string(), block.getLineNumber());
233         return UNKNOWN_ERROR;
234     }
235 
236     assets->setPackage(String8(block.getAttributeStringValue(nameIndex, &len)));
237 
238     String16 uses_sdk16("uses-sdk");
239     while ((code=block.next()) != ResXMLTree::END_DOCUMENT
240            && code != ResXMLTree::BAD_DOCUMENT) {
241         if (code == ResXMLTree::START_TAG) {
242             if (strcmp16(block.getElementName(&len), uses_sdk16.string()) == 0) {
243                 ssize_t minSdkIndex = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
244                                                              "minSdkVersion");
245                 if (minSdkIndex >= 0) {
246                     const uint16_t* minSdk16 = block.getAttributeStringValue(minSdkIndex, &len);
247                     const char* minSdk8 = strdup(String8(minSdk16).string());
248                     bundle->setManifestMinSdkVersion(minSdk8);
249                 }
250             }
251         }
252     }
253 
254     return NO_ERROR;
255 }
256 
257 // ==========================================================================
258 // ==========================================================================
259 // ==========================================================================
260 
makeFileResources(Bundle * bundle,const sp<AaptAssets> & assets,ResourceTable * table,const sp<ResourceTypeSet> & set,const char * resType)261 static status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets,
262                                   ResourceTable* table,
263                                   const sp<ResourceTypeSet>& set,
264                                   const char* resType)
265 {
266     String8 type8(resType);
267     String16 type16(resType);
268 
269     bool hasErrors = false;
270 
271     ResourceDirIterator it(set, String8(resType));
272     ssize_t res;
273     while ((res=it.next()) == NO_ERROR) {
274         if (bundle->getVerbose()) {
275             printf("    (new resource id %s from %s)\n",
276                    it.getBaseName().string(), it.getFile()->getPrintableSource().string());
277         }
278         String16 baseName(it.getBaseName());
279         const char16_t* str = baseName.string();
280         const char16_t* const end = str + baseName.size();
281         while (str < end) {
282             if (!((*str >= 'a' && *str <= 'z')
283                     || (*str >= '0' && *str <= '9')
284                     || *str == '_' || *str == '.')) {
285                 fprintf(stderr, "%s: Invalid file name: must contain only [a-z0-9_.]\n",
286                         it.getPath().string());
287                 hasErrors = true;
288             }
289             str++;
290         }
291         String8 resPath = it.getPath();
292         resPath.convertToResPath();
293         table->addEntry(SourcePos(it.getPath(), 0), String16(assets->getPackage()),
294                         type16,
295                         baseName,
296                         String16(resPath),
297                         NULL,
298                         &it.getParams());
299         assets->addResource(it.getLeafName(), resPath, it.getFile(), type8);
300     }
301 
302     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
303 }
304 
preProcessImages(Bundle * bundle,const sp<AaptAssets> & assets,const sp<ResourceTypeSet> & set,const char * type)305 static status_t preProcessImages(Bundle* bundle, const sp<AaptAssets>& assets,
306                           const sp<ResourceTypeSet>& set, const char* type)
307 {
308     bool hasErrors = false;
309     ssize_t res = NO_ERROR;
310     if (bundle->getUseCrunchCache() == false) {
311         ResourceDirIterator it(set, String8(type));
312         Vector<sp<AaptFile> > newNameFiles;
313         Vector<String8> newNamePaths;
314         while ((res=it.next()) == NO_ERROR) {
315             res = preProcessImage(bundle, assets, it.getFile(), NULL);
316             if (res < NO_ERROR) {
317                 hasErrors = true;
318             }
319         }
320     }
321     return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
322 }
323 
postProcessImages(const sp<AaptAssets> & assets,ResourceTable * table,const sp<ResourceTypeSet> & set)324 status_t postProcessImages(const sp<AaptAssets>& assets,
325                            ResourceTable* table,
326                            const sp<ResourceTypeSet>& set)
327 {
328     ResourceDirIterator it(set, String8("drawable"));
329     bool hasErrors = false;
330     ssize_t res;
331     while ((res=it.next()) == NO_ERROR) {
332         res = postProcessImage(assets, table, it.getFile());
333         if (res < NO_ERROR) {
334             hasErrors = true;
335         }
336     }
337 
338     return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
339 }
340 
collect_files(const sp<AaptDir> & dir,KeyedVector<String8,sp<ResourceTypeSet>> * resources)341 static void collect_files(const sp<AaptDir>& dir,
342         KeyedVector<String8, sp<ResourceTypeSet> >* resources)
343 {
344     const DefaultKeyedVector<String8, sp<AaptGroup> >& groups = dir->getFiles();
345     int N = groups.size();
346     for (int i=0; i<N; i++) {
347         String8 leafName = groups.keyAt(i);
348         const sp<AaptGroup>& group = groups.valueAt(i);
349 
350         const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files
351                 = group->getFiles();
352 
353         if (files.size() == 0) {
354             continue;
355         }
356 
357         String8 resType = files.valueAt(0)->getResourceType();
358 
359         ssize_t index = resources->indexOfKey(resType);
360 
361         if (index < 0) {
362             sp<ResourceTypeSet> set = new ResourceTypeSet();
363             NOISY(printf("Creating new resource type set for leaf %s with group %s (%p)\n",
364                     leafName.string(), group->getPath().string(), group.get()));
365             set->add(leafName, group);
366             resources->add(resType, set);
367         } else {
368             sp<ResourceTypeSet> set = resources->valueAt(index);
369             index = set->indexOfKey(leafName);
370             if (index < 0) {
371                 NOISY(printf("Adding to resource type set for leaf %s group %s (%p)\n",
372                         leafName.string(), group->getPath().string(), group.get()));
373                 set->add(leafName, group);
374             } else {
375                 sp<AaptGroup> existingGroup = set->valueAt(index);
376                 NOISY(printf("Extending to resource type set for leaf %s group %s (%p)\n",
377                         leafName.string(), group->getPath().string(), group.get()));
378                 for (size_t j=0; j<files.size(); j++) {
379                     NOISY(printf("Adding file %s in group %s resType %s\n",
380                         files.valueAt(j)->getSourceFile().string(),
381                         files.keyAt(j).toDirName(String8()).string(),
382                         resType.string()));
383                     status_t err = existingGroup->addFile(files.valueAt(j));
384                 }
385             }
386         }
387     }
388 }
389 
collect_files(const sp<AaptAssets> & ass,KeyedVector<String8,sp<ResourceTypeSet>> * resources)390 static void collect_files(const sp<AaptAssets>& ass,
391         KeyedVector<String8, sp<ResourceTypeSet> >* resources)
392 {
393     const Vector<sp<AaptDir> >& dirs = ass->resDirs();
394     int N = dirs.size();
395 
396     for (int i=0; i<N; i++) {
397         sp<AaptDir> d = dirs.itemAt(i);
398         NOISY(printf("Collecting dir #%d %p: %s, leaf %s\n", i, d.get(), d->getPath().string(),
399                 d->getLeaf().string()));
400         collect_files(d, resources);
401 
402         // don't try to include the res dir
403         NOISY(printf("Removing dir leaf %s\n", d->getLeaf().string()));
404         ass->removeDir(d->getLeaf());
405     }
406 }
407 
408 enum {
409     ATTR_OKAY = -1,
410     ATTR_NOT_FOUND = -2,
411     ATTR_LEADING_SPACES = -3,
412     ATTR_TRAILING_SPACES = -4
413 };
validateAttr(const String8 & path,const ResTable & table,const ResXMLParser & parser,const char * ns,const char * attr,const char * validChars,bool required)414 static int validateAttr(const String8& path, const ResTable& table,
415         const ResXMLParser& parser,
416         const char* ns, const char* attr, const char* validChars, bool required)
417 {
418     size_t len;
419 
420     ssize_t index = parser.indexOfAttribute(ns, attr);
421     const uint16_t* str;
422     Res_value value;
423     if (index >= 0 && parser.getAttributeValue(index, &value) >= 0) {
424         const ResStringPool* pool = &parser.getStrings();
425         if (value.dataType == Res_value::TYPE_REFERENCE) {
426             uint32_t specFlags = 0;
427             int strIdx;
428             if ((strIdx=table.resolveReference(&value, 0x10000000, NULL, &specFlags)) < 0) {
429                 fprintf(stderr, "%s:%d: Tag <%s> attribute %s references unknown resid 0x%08x.\n",
430                         path.string(), parser.getLineNumber(),
431                         String8(parser.getElementName(&len)).string(), attr,
432                         value.data);
433                 return ATTR_NOT_FOUND;
434             }
435 
436             pool = table.getTableStringBlock(strIdx);
437             #if 0
438             if (pool != NULL) {
439                 str = pool->stringAt(value.data, &len);
440             }
441             printf("***** RES ATTR: %s specFlags=0x%x strIdx=%d: %s\n", attr,
442                     specFlags, strIdx, str != NULL ? String8(str).string() : "???");
443             #endif
444             if ((specFlags&~ResTable_typeSpec::SPEC_PUBLIC) != 0 && false) {
445                 fprintf(stderr, "%s:%d: Tag <%s> attribute %s varies by configurations 0x%x.\n",
446                         path.string(), parser.getLineNumber(),
447                         String8(parser.getElementName(&len)).string(), attr,
448                         specFlags);
449                 return ATTR_NOT_FOUND;
450             }
451         }
452         if (value.dataType == Res_value::TYPE_STRING) {
453             if (pool == NULL) {
454                 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has no string block.\n",
455                         path.string(), parser.getLineNumber(),
456                         String8(parser.getElementName(&len)).string(), attr);
457                 return ATTR_NOT_FOUND;
458             }
459             if ((str=pool->stringAt(value.data, &len)) == NULL) {
460                 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has corrupt string value.\n",
461                         path.string(), parser.getLineNumber(),
462                         String8(parser.getElementName(&len)).string(), attr);
463                 return ATTR_NOT_FOUND;
464             }
465         } else {
466             fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid type %d.\n",
467                     path.string(), parser.getLineNumber(),
468                     String8(parser.getElementName(&len)).string(), attr,
469                     value.dataType);
470             return ATTR_NOT_FOUND;
471         }
472         if (validChars) {
473             for (size_t i=0; i<len; i++) {
474                 uint16_t c = str[i];
475                 const char* p = validChars;
476                 bool okay = false;
477                 while (*p) {
478                     if (c == *p) {
479                         okay = true;
480                         break;
481                     }
482                     p++;
483                 }
484                 if (!okay) {
485                     fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid character '%c'.\n",
486                             path.string(), parser.getLineNumber(),
487                             String8(parser.getElementName(&len)).string(), attr, (char)str[i]);
488                     return (int)i;
489                 }
490             }
491         }
492         if (*str == ' ') {
493             fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not start with a space.\n",
494                     path.string(), parser.getLineNumber(),
495                     String8(parser.getElementName(&len)).string(), attr);
496             return ATTR_LEADING_SPACES;
497         }
498         if (str[len-1] == ' ') {
499             fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not end with a space.\n",
500                     path.string(), parser.getLineNumber(),
501                     String8(parser.getElementName(&len)).string(), attr);
502             return ATTR_TRAILING_SPACES;
503         }
504         return ATTR_OKAY;
505     }
506     if (required) {
507         fprintf(stderr, "%s:%d: Tag <%s> missing required attribute %s.\n",
508                 path.string(), parser.getLineNumber(),
509                 String8(parser.getElementName(&len)).string(), attr);
510         return ATTR_NOT_FOUND;
511     }
512     return ATTR_OKAY;
513 }
514 
checkForIds(const String8 & path,ResXMLParser & parser)515 static void checkForIds(const String8& path, ResXMLParser& parser)
516 {
517     ResXMLTree::event_code_t code;
518     while ((code=parser.next()) != ResXMLTree::END_DOCUMENT
519            && code > ResXMLTree::BAD_DOCUMENT) {
520         if (code == ResXMLTree::START_TAG) {
521             ssize_t index = parser.indexOfAttribute(NULL, "id");
522             if (index >= 0) {
523                 fprintf(stderr, "%s:%d: warning: found plain 'id' attribute; did you mean the new 'android:id' name?\n",
524                         path.string(), parser.getLineNumber());
525             }
526         }
527     }
528 }
529 
applyFileOverlay(Bundle * bundle,const sp<AaptAssets> & assets,sp<ResourceTypeSet> * baseSet,const char * resType)530 static bool applyFileOverlay(Bundle *bundle,
531                              const sp<AaptAssets>& assets,
532                              sp<ResourceTypeSet> *baseSet,
533                              const char *resType)
534 {
535     if (bundle->getVerbose()) {
536         printf("applyFileOverlay for %s\n", resType);
537     }
538 
539     // Replace any base level files in this category with any found from the overlay
540     // Also add any found only in the overlay.
541     sp<AaptAssets> overlay = assets->getOverlay();
542     String8 resTypeString(resType);
543 
544     // work through the linked list of overlays
545     while (overlay.get()) {
546         KeyedVector<String8, sp<ResourceTypeSet> >* overlayRes = overlay->getResources();
547 
548         // get the overlay resources of the requested type
549         ssize_t index = overlayRes->indexOfKey(resTypeString);
550         if (index >= 0) {
551             sp<ResourceTypeSet> overlaySet = overlayRes->valueAt(index);
552 
553             // for each of the resources, check for a match in the previously built
554             // non-overlay "baseset".
555             size_t overlayCount = overlaySet->size();
556             for (size_t overlayIndex=0; overlayIndex<overlayCount; overlayIndex++) {
557                 if (bundle->getVerbose()) {
558                     printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string());
559                 }
560                 size_t baseIndex = UNKNOWN_ERROR;
561                 if (baseSet->get() != NULL) {
562                     baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex));
563                 }
564                 if (baseIndex < UNKNOWN_ERROR) {
565                     // look for same flavor.  For a given file (strings.xml, for example)
566                     // there may be a locale specific or other flavors - we want to match
567                     // the same flavor.
568                     sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
569                     sp<AaptGroup> baseGroup = (*baseSet)->valueAt(baseIndex);
570 
571                     DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
572                             overlayGroup->getFiles();
573                     if (bundle->getVerbose()) {
574                         DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles =
575                                 baseGroup->getFiles();
576                         for (size_t i=0; i < baseFiles.size(); i++) {
577                             printf("baseFile " ZD " has flavor %s\n", (ZD_TYPE) i,
578                                     baseFiles.keyAt(i).toString().string());
579                         }
580                         for (size_t i=0; i < overlayFiles.size(); i++) {
581                             printf("overlayFile " ZD " has flavor %s\n", (ZD_TYPE) i,
582                                     overlayFiles.keyAt(i).toString().string());
583                         }
584                     }
585 
586                     size_t overlayGroupSize = overlayFiles.size();
587                     for (size_t overlayGroupIndex = 0;
588                             overlayGroupIndex<overlayGroupSize;
589                             overlayGroupIndex++) {
590                         size_t baseFileIndex =
591                                 baseGroup->getFiles().indexOfKey(overlayFiles.
592                                 keyAt(overlayGroupIndex));
593                         if (baseFileIndex < UNKNOWN_ERROR) {
594                             if (bundle->getVerbose()) {
595                                 printf("found a match (" ZD ") for overlay file %s, for flavor %s\n",
596                                         (ZD_TYPE) baseFileIndex,
597                                         overlayGroup->getLeaf().string(),
598                                         overlayFiles.keyAt(overlayGroupIndex).toString().string());
599                             }
600                             baseGroup->removeFile(baseFileIndex);
601                         } else {
602                             // didn't find a match fall through and add it..
603                             if (true || bundle->getVerbose()) {
604                                 printf("nothing matches overlay file %s, for flavor %s\n",
605                                         overlayGroup->getLeaf().string(),
606                                         overlayFiles.keyAt(overlayGroupIndex).toString().string());
607                             }
608                         }
609                         baseGroup->addFile(overlayFiles.valueAt(overlayGroupIndex));
610                         assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
611                     }
612                 } else {
613                     if (baseSet->get() == NULL) {
614                         *baseSet = new ResourceTypeSet();
615                         assets->getResources()->add(String8(resType), *baseSet);
616                     }
617                     // this group doesn't exist (a file that's only in the overlay)
618                     (*baseSet)->add(overlaySet->keyAt(overlayIndex),
619                             overlaySet->valueAt(overlayIndex));
620                     // make sure all flavors are defined in the resources.
621                     sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
622                     DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
623                             overlayGroup->getFiles();
624                     size_t overlayGroupSize = overlayFiles.size();
625                     for (size_t overlayGroupIndex = 0;
626                             overlayGroupIndex<overlayGroupSize;
627                             overlayGroupIndex++) {
628                         assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
629                     }
630                 }
631             }
632             // this overlay didn't have resources for this type
633         }
634         // try next overlay
635         overlay = overlay->getOverlay();
636     }
637     return true;
638 }
639 
addTagAttribute(const sp<XMLNode> & node,const char * ns8,const char * attr8,const char * value)640 void addTagAttribute(const sp<XMLNode>& node, const char* ns8,
641         const char* attr8, const char* value)
642 {
643     if (value == NULL) {
644         return;
645     }
646 
647     const String16 ns(ns8);
648     const String16 attr(attr8);
649 
650     if (node->getAttribute(ns, attr) != NULL) {
651         fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s);"
652                         " using existing value in manifest.\n",
653                 String8(attr).string(), String8(ns).string());
654         return;
655     }
656 
657     node->addAttribute(ns, attr, String16(value));
658 }
659 
fullyQualifyClassName(const String8 & package,sp<XMLNode> node,const String16 & attrName)660 static void fullyQualifyClassName(const String8& package, sp<XMLNode> node,
661         const String16& attrName) {
662     XMLNode::attribute_entry* attr = node->editAttribute(
663             String16("http://schemas.android.com/apk/res/android"), attrName);
664     if (attr != NULL) {
665         String8 name(attr->string);
666 
667         // asdf     --> package.asdf
668         // .asdf  .a.b  --> package.asdf package.a.b
669         // asdf.adsf --> asdf.asdf
670         String8 className;
671         const char* p = name.string();
672         const char* q = strchr(p, '.');
673         if (p == q) {
674             className += package;
675             className += name;
676         } else if (q == NULL) {
677             className += package;
678             className += ".";
679             className += name;
680         } else {
681             className += name;
682         }
683         NOISY(printf("Qualifying class '%s' to '%s'", name.string(), className.string()));
684         attr->string.setTo(String16(className));
685     }
686 }
687 
massageManifest(Bundle * bundle,sp<XMLNode> root)688 status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
689 {
690     root = root->searchElement(String16(), String16("manifest"));
691     if (root == NULL) {
692         fprintf(stderr, "No <manifest> tag.\n");
693         return UNKNOWN_ERROR;
694     }
695 
696     addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode",
697             bundle->getVersionCode());
698     addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName",
699             bundle->getVersionName());
700 
701     if (bundle->getMinSdkVersion() != NULL
702             || bundle->getTargetSdkVersion() != NULL
703             || bundle->getMaxSdkVersion() != NULL) {
704         sp<XMLNode> vers = root->getChildElement(String16(), String16("uses-sdk"));
705         if (vers == NULL) {
706             vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk"));
707             root->insertChildAt(vers, 0);
708         }
709 
710         addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion",
711                 bundle->getMinSdkVersion());
712         addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion",
713                 bundle->getTargetSdkVersion());
714         addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion",
715                 bundle->getMaxSdkVersion());
716     }
717 
718     if (bundle->getDebugMode()) {
719         sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
720         if (application != NULL) {
721             addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true");
722         }
723     }
724 
725     // Deal with manifest package name overrides
726     const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride();
727     if (manifestPackageNameOverride != NULL) {
728         // Update the actual package name
729         XMLNode::attribute_entry* attr = root->editAttribute(String16(), String16("package"));
730         if (attr == NULL) {
731             fprintf(stderr, "package name is required with --rename-manifest-package.\n");
732             return UNKNOWN_ERROR;
733         }
734         String8 origPackage(attr->string);
735         attr->string.setTo(String16(manifestPackageNameOverride));
736         NOISY(printf("Overriding package '%s' to be '%s'\n", origPackage.string(), manifestPackageNameOverride));
737 
738         // Make class names fully qualified
739         sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
740         if (application != NULL) {
741             fullyQualifyClassName(origPackage, application, String16("name"));
742             fullyQualifyClassName(origPackage, application, String16("backupAgent"));
743 
744             Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(application->getChildren());
745             for (size_t i = 0; i < children.size(); i++) {
746                 sp<XMLNode> child = children.editItemAt(i);
747                 String8 tag(child->getElementName());
748                 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
749                     fullyQualifyClassName(origPackage, child, String16("name"));
750                 } else if (tag == "activity-alias") {
751                     fullyQualifyClassName(origPackage, child, String16("name"));
752                     fullyQualifyClassName(origPackage, child, String16("targetActivity"));
753                 }
754             }
755         }
756     }
757 
758     // Deal with manifest package name overrides
759     const char* instrumentationPackageNameOverride = bundle->getInstrumentationPackageNameOverride();
760     if (instrumentationPackageNameOverride != NULL) {
761         // Fix up instrumentation targets.
762         Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(root->getChildren());
763         for (size_t i = 0; i < children.size(); i++) {
764             sp<XMLNode> child = children.editItemAt(i);
765             String8 tag(child->getElementName());
766             if (tag == "instrumentation") {
767                 XMLNode::attribute_entry* attr = child->editAttribute(
768                         String16("http://schemas.android.com/apk/res/android"), String16("targetPackage"));
769                 if (attr != NULL) {
770                     attr->string.setTo(String16(instrumentationPackageNameOverride));
771                 }
772             }
773         }
774     }
775 
776     return NO_ERROR;
777 }
778 
779 #define ASSIGN_IT(n) \
780         do { \
781             ssize_t index = resources->indexOfKey(String8(#n)); \
782             if (index >= 0) { \
783                 n ## s = resources->valueAt(index); \
784             } \
785         } while (0)
786 
updatePreProcessedCache(Bundle * bundle)787 status_t updatePreProcessedCache(Bundle* bundle)
788 {
789     #if BENCHMARK
790     fprintf(stdout, "BENCHMARK: Starting PNG PreProcessing \n");
791     long startPNGTime = clock();
792     #endif /* BENCHMARK */
793 
794     String8 source(bundle->getResourceSourceDirs()[0]);
795     String8 dest(bundle->getCrunchedOutputDir());
796 
797     FileFinder* ff = new SystemFileFinder();
798     CrunchCache cc(source,dest,ff);
799 
800     CacheUpdater* cu = new SystemCacheUpdater(bundle);
801     size_t numFiles = cc.crunch(cu);
802 
803     if (bundle->getVerbose())
804         fprintf(stdout, "Crunched %d PNG files to update cache\n", (int)numFiles);
805 
806     delete ff;
807     delete cu;
808 
809     #if BENCHMARK
810     fprintf(stdout, "BENCHMARK: End PNG PreProcessing. Time Elapsed: %f ms \n"
811             ,(clock() - startPNGTime)/1000.0);
812     #endif /* BENCHMARK */
813     return 0;
814 }
815 
buildResources(Bundle * bundle,const sp<AaptAssets> & assets)816 status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
817 {
818     // First, look for a package file to parse.  This is required to
819     // be able to generate the resource information.
820     sp<AaptGroup> androidManifestFile =
821             assets->getFiles().valueFor(String8("AndroidManifest.xml"));
822     if (androidManifestFile == NULL) {
823         fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n");
824         return UNKNOWN_ERROR;
825     }
826 
827     status_t err = parsePackage(bundle, assets, androidManifestFile);
828     if (err != NO_ERROR) {
829         return err;
830     }
831 
832     NOISY(printf("Creating resources for package %s\n",
833                  assets->getPackage().string()));
834 
835     ResourceTable table(bundle, String16(assets->getPackage()));
836     err = table.addIncludedResources(bundle, assets);
837     if (err != NO_ERROR) {
838         return err;
839     }
840 
841     NOISY(printf("Found %d included resource packages\n", (int)table.size()));
842 
843     // Standard flags for compiled XML and optional UTF-8 encoding
844     int xmlFlags = XML_COMPILE_STANDARD_RESOURCE;
845 
846     /* Only enable UTF-8 if the caller of aapt didn't specifically
847      * request UTF-16 encoding and the parameters of this package
848      * allow UTF-8 to be used.
849      */
850     if (!bundle->getWantUTF16()
851             && bundle->isMinSdkAtLeast(SDK_FROYO)) {
852         xmlFlags |= XML_COMPILE_UTF8;
853     }
854 
855     // --------------------------------------------------------------
856     // First, gather all resource information.
857     // --------------------------------------------------------------
858 
859     // resType -> leafName -> group
860     KeyedVector<String8, sp<ResourceTypeSet> > *resources =
861             new KeyedVector<String8, sp<ResourceTypeSet> >;
862     collect_files(assets, resources);
863 
864     sp<ResourceTypeSet> drawables;
865     sp<ResourceTypeSet> layouts;
866     sp<ResourceTypeSet> anims;
867     sp<ResourceTypeSet> animators;
868     sp<ResourceTypeSet> interpolators;
869     sp<ResourceTypeSet> xmls;
870     sp<ResourceTypeSet> raws;
871     sp<ResourceTypeSet> colors;
872     sp<ResourceTypeSet> menus;
873     sp<ResourceTypeSet> mipmaps;
874 
875     ASSIGN_IT(drawable);
876     ASSIGN_IT(layout);
877     ASSIGN_IT(anim);
878     ASSIGN_IT(animator);
879     ASSIGN_IT(interpolator);
880     ASSIGN_IT(xml);
881     ASSIGN_IT(raw);
882     ASSIGN_IT(color);
883     ASSIGN_IT(menu);
884     ASSIGN_IT(mipmap);
885 
886     assets->setResources(resources);
887     // now go through any resource overlays and collect their files
888     sp<AaptAssets> current = assets->getOverlay();
889     while(current.get()) {
890         KeyedVector<String8, sp<ResourceTypeSet> > *resources =
891                 new KeyedVector<String8, sp<ResourceTypeSet> >;
892         current->setResources(resources);
893         collect_files(current, resources);
894         current = current->getOverlay();
895     }
896     // apply the overlay files to the base set
897     if (!applyFileOverlay(bundle, assets, &drawables, "drawable") ||
898             !applyFileOverlay(bundle, assets, &layouts, "layout") ||
899             !applyFileOverlay(bundle, assets, &anims, "anim") ||
900             !applyFileOverlay(bundle, assets, &animators, "animator") ||
901             !applyFileOverlay(bundle, assets, &interpolators, "interpolator") ||
902             !applyFileOverlay(bundle, assets, &xmls, "xml") ||
903             !applyFileOverlay(bundle, assets, &raws, "raw") ||
904             !applyFileOverlay(bundle, assets, &colors, "color") ||
905             !applyFileOverlay(bundle, assets, &menus, "menu") ||
906             !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) {
907         return UNKNOWN_ERROR;
908     }
909 
910     bool hasErrors = false;
911 
912     if (drawables != NULL) {
913         if (bundle->getOutputAPKFile() != NULL) {
914             err = preProcessImages(bundle, assets, drawables, "drawable");
915         }
916         if (err == NO_ERROR) {
917             err = makeFileResources(bundle, assets, &table, drawables, "drawable");
918             if (err != NO_ERROR) {
919                 hasErrors = true;
920             }
921         } else {
922             hasErrors = true;
923         }
924     }
925 
926     if (mipmaps != NULL) {
927         if (bundle->getOutputAPKFile() != NULL) {
928             err = preProcessImages(bundle, assets, mipmaps, "mipmap");
929         }
930         if (err == NO_ERROR) {
931             err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap");
932             if (err != NO_ERROR) {
933                 hasErrors = true;
934             }
935         } else {
936             hasErrors = true;
937         }
938     }
939 
940     if (layouts != NULL) {
941         err = makeFileResources(bundle, assets, &table, layouts, "layout");
942         if (err != NO_ERROR) {
943             hasErrors = true;
944         }
945     }
946 
947     if (anims != NULL) {
948         err = makeFileResources(bundle, assets, &table, anims, "anim");
949         if (err != NO_ERROR) {
950             hasErrors = true;
951         }
952     }
953 
954     if (animators != NULL) {
955         err = makeFileResources(bundle, assets, &table, animators, "animator");
956         if (err != NO_ERROR) {
957             hasErrors = true;
958         }
959     }
960 
961     if (interpolators != NULL) {
962         err = makeFileResources(bundle, assets, &table, interpolators, "interpolator");
963         if (err != NO_ERROR) {
964             hasErrors = true;
965         }
966     }
967 
968     if (xmls != NULL) {
969         err = makeFileResources(bundle, assets, &table, xmls, "xml");
970         if (err != NO_ERROR) {
971             hasErrors = true;
972         }
973     }
974 
975     if (raws != NULL) {
976         err = makeFileResources(bundle, assets, &table, raws, "raw");
977         if (err != NO_ERROR) {
978             hasErrors = true;
979         }
980     }
981 
982     // compile resources
983     current = assets;
984     while(current.get()) {
985         KeyedVector<String8, sp<ResourceTypeSet> > *resources =
986                 current->getResources();
987 
988         ssize_t index = resources->indexOfKey(String8("values"));
989         if (index >= 0) {
990             ResourceDirIterator it(resources->valueAt(index), String8("values"));
991             ssize_t res;
992             while ((res=it.next()) == NO_ERROR) {
993                 sp<AaptFile> file = it.getFile();
994                 res = compileResourceFile(bundle, assets, file, it.getParams(),
995                                           (current!=assets), &table);
996                 if (res != NO_ERROR) {
997                     hasErrors = true;
998                 }
999             }
1000         }
1001         current = current->getOverlay();
1002     }
1003 
1004     if (colors != NULL) {
1005         err = makeFileResources(bundle, assets, &table, colors, "color");
1006         if (err != NO_ERROR) {
1007             hasErrors = true;
1008         }
1009     }
1010 
1011     if (menus != NULL) {
1012         err = makeFileResources(bundle, assets, &table, menus, "menu");
1013         if (err != NO_ERROR) {
1014             hasErrors = true;
1015         }
1016     }
1017 
1018     // --------------------------------------------------------------------
1019     // Assignment of resource IDs and initial generation of resource table.
1020     // --------------------------------------------------------------------
1021 
1022     if (table.hasResources()) {
1023         sp<AaptFile> resFile(getResourceFile(assets));
1024         if (resFile == NULL) {
1025             fprintf(stderr, "Error: unable to generate entry for resource data\n");
1026             return UNKNOWN_ERROR;
1027         }
1028 
1029         err = table.assignResourceIds();
1030         if (err < NO_ERROR) {
1031             return err;
1032         }
1033     }
1034 
1035     // --------------------------------------------------------------
1036     // Finally, we can now we can compile XML files, which may reference
1037     // resources.
1038     // --------------------------------------------------------------
1039 
1040     if (layouts != NULL) {
1041         ResourceDirIterator it(layouts, String8("layout"));
1042         while ((err=it.next()) == NO_ERROR) {
1043             String8 src = it.getFile()->getPrintableSource();
1044             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1045             if (err == NO_ERROR) {
1046                 ResXMLTree block;
1047                 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
1048                 checkForIds(src, block);
1049             } else {
1050                 hasErrors = true;
1051             }
1052         }
1053 
1054         if (err < NO_ERROR) {
1055             hasErrors = true;
1056         }
1057         err = NO_ERROR;
1058     }
1059 
1060     if (anims != NULL) {
1061         ResourceDirIterator it(anims, String8("anim"));
1062         while ((err=it.next()) == NO_ERROR) {
1063             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1064             if (err != NO_ERROR) {
1065                 hasErrors = true;
1066             }
1067         }
1068 
1069         if (err < NO_ERROR) {
1070             hasErrors = true;
1071         }
1072         err = NO_ERROR;
1073     }
1074 
1075     if (animators != NULL) {
1076         ResourceDirIterator it(animators, String8("animator"));
1077         while ((err=it.next()) == NO_ERROR) {
1078             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1079             if (err != NO_ERROR) {
1080                 hasErrors = true;
1081             }
1082         }
1083 
1084         if (err < NO_ERROR) {
1085             hasErrors = true;
1086         }
1087         err = NO_ERROR;
1088     }
1089 
1090     if (interpolators != NULL) {
1091         ResourceDirIterator it(interpolators, String8("interpolator"));
1092         while ((err=it.next()) == NO_ERROR) {
1093             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1094             if (err != NO_ERROR) {
1095                 hasErrors = true;
1096             }
1097         }
1098 
1099         if (err < NO_ERROR) {
1100             hasErrors = true;
1101         }
1102         err = NO_ERROR;
1103     }
1104 
1105     if (xmls != NULL) {
1106         ResourceDirIterator it(xmls, String8("xml"));
1107         while ((err=it.next()) == NO_ERROR) {
1108             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1109             if (err != NO_ERROR) {
1110                 hasErrors = true;
1111             }
1112         }
1113 
1114         if (err < NO_ERROR) {
1115             hasErrors = true;
1116         }
1117         err = NO_ERROR;
1118     }
1119 
1120     if (drawables != NULL) {
1121         err = postProcessImages(assets, &table, drawables);
1122         if (err != NO_ERROR) {
1123             hasErrors = true;
1124         }
1125     }
1126 
1127     if (colors != NULL) {
1128         ResourceDirIterator it(colors, String8("color"));
1129         while ((err=it.next()) == NO_ERROR) {
1130           err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1131             if (err != NO_ERROR) {
1132                 hasErrors = true;
1133             }
1134         }
1135 
1136         if (err < NO_ERROR) {
1137             hasErrors = true;
1138         }
1139         err = NO_ERROR;
1140     }
1141 
1142     if (menus != NULL) {
1143         ResourceDirIterator it(menus, String8("menu"));
1144         while ((err=it.next()) == NO_ERROR) {
1145             String8 src = it.getFile()->getPrintableSource();
1146             err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
1147             if (err != NO_ERROR) {
1148                 hasErrors = true;
1149             }
1150             ResXMLTree block;
1151             block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
1152             checkForIds(src, block);
1153         }
1154 
1155         if (err < NO_ERROR) {
1156             hasErrors = true;
1157         }
1158         err = NO_ERROR;
1159     }
1160 
1161     if (table.validateLocalizations()) {
1162         hasErrors = true;
1163     }
1164 
1165     if (hasErrors) {
1166         return UNKNOWN_ERROR;
1167     }
1168 
1169     const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0));
1170     String8 manifestPath(manifestFile->getPrintableSource());
1171 
1172     // Generate final compiled manifest file.
1173     manifestFile->clearData();
1174     sp<XMLNode> manifestTree = XMLNode::parse(manifestFile);
1175     if (manifestTree == NULL) {
1176         return UNKNOWN_ERROR;
1177     }
1178     err = massageManifest(bundle, manifestTree);
1179     if (err < NO_ERROR) {
1180         return err;
1181     }
1182     err = compileXmlFile(assets, manifestTree, manifestFile, &table);
1183     if (err < NO_ERROR) {
1184         return err;
1185     }
1186 
1187     //block.restart();
1188     //printXMLBlock(&block);
1189 
1190     // --------------------------------------------------------------
1191     // Generate the final resource table.
1192     // Re-flatten because we may have added new resource IDs
1193     // --------------------------------------------------------------
1194 
1195     ResTable finalResTable;
1196     sp<AaptFile> resFile;
1197 
1198     if (table.hasResources()) {
1199         sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
1200         err = table.addSymbols(symbols);
1201         if (err < NO_ERROR) {
1202             return err;
1203         }
1204 
1205         resFile = getResourceFile(assets);
1206         if (resFile == NULL) {
1207             fprintf(stderr, "Error: unable to generate entry for resource data\n");
1208             return UNKNOWN_ERROR;
1209         }
1210 
1211         err = table.flatten(bundle, resFile);
1212         if (err < NO_ERROR) {
1213             return err;
1214         }
1215 
1216         if (bundle->getPublicOutputFile()) {
1217             FILE* fp = fopen(bundle->getPublicOutputFile(), "w+");
1218             if (fp == NULL) {
1219                 fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n",
1220                         (const char*)bundle->getPublicOutputFile(), strerror(errno));
1221                 return UNKNOWN_ERROR;
1222             }
1223             if (bundle->getVerbose()) {
1224                 printf("  Writing public definitions to %s.\n", bundle->getPublicOutputFile());
1225             }
1226             table.writePublicDefinitions(String16(assets->getPackage()), fp);
1227             fclose(fp);
1228         }
1229 
1230         // Read resources back in,
1231         finalResTable.add(resFile->getData(), resFile->getSize(), NULL);
1232 
1233 #if 0
1234         NOISY(
1235               printf("Generated resources:\n");
1236               finalResTable.print();
1237         )
1238 #endif
1239     }
1240 
1241     // Perform a basic validation of the manifest file.  This time we
1242     // parse it with the comments intact, so that we can use them to
1243     // generate java docs...  so we are not going to write this one
1244     // back out to the final manifest data.
1245     sp<AaptFile> outManifestFile = new AaptFile(manifestFile->getSourceFile(),
1246             manifestFile->getGroupEntry(),
1247             manifestFile->getResourceType());
1248     err = compileXmlFile(assets, manifestFile,
1249             outManifestFile, &table,
1250             XML_COMPILE_ASSIGN_ATTRIBUTE_IDS
1251             | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES);
1252     if (err < NO_ERROR) {
1253         return err;
1254     }
1255     ResXMLTree block;
1256     block.setTo(outManifestFile->getData(), outManifestFile->getSize(), true);
1257     String16 manifest16("manifest");
1258     String16 permission16("permission");
1259     String16 permission_group16("permission-group");
1260     String16 uses_permission16("uses-permission");
1261     String16 instrumentation16("instrumentation");
1262     String16 application16("application");
1263     String16 provider16("provider");
1264     String16 service16("service");
1265     String16 receiver16("receiver");
1266     String16 activity16("activity");
1267     String16 action16("action");
1268     String16 category16("category");
1269     String16 data16("scheme");
1270     const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz"
1271         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789";
1272     const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz"
1273         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-";
1274     const char* classIdentChars = "abcdefghijklmnopqrstuvwxyz"
1275         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789$";
1276     const char* processIdentChars = "abcdefghijklmnopqrstuvwxyz"
1277         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:";
1278     const char* authoritiesIdentChars = "abcdefghijklmnopqrstuvwxyz"
1279         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-:;";
1280     const char* typeIdentChars = "abcdefghijklmnopqrstuvwxyz"
1281         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:-/*+";
1282     const char* schemeIdentChars = "abcdefghijklmnopqrstuvwxyz"
1283         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-";
1284     ResXMLTree::event_code_t code;
1285     sp<AaptSymbols> permissionSymbols;
1286     sp<AaptSymbols> permissionGroupSymbols;
1287     while ((code=block.next()) != ResXMLTree::END_DOCUMENT
1288            && code > ResXMLTree::BAD_DOCUMENT) {
1289         if (code == ResXMLTree::START_TAG) {
1290             size_t len;
1291             if (block.getElementNamespace(&len) != NULL) {
1292                 continue;
1293             }
1294             if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) {
1295                 if (validateAttr(manifestPath, finalResTable, block, NULL, "package",
1296                                  packageIdentChars, true) != ATTR_OKAY) {
1297                     hasErrors = true;
1298                 }
1299                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1300                                  "sharedUserId", packageIdentChars, false) != ATTR_OKAY) {
1301                     hasErrors = true;
1302                 }
1303             } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0
1304                     || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) {
1305                 const bool isGroup = strcmp16(block.getElementName(&len),
1306                         permission_group16.string()) == 0;
1307                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1308                                  "name", isGroup ? packageIdentCharsWithTheStupid
1309                                  : packageIdentChars, true) != ATTR_OKAY) {
1310                     hasErrors = true;
1311                 }
1312                 SourcePos srcPos(manifestPath, block.getLineNumber());
1313                 sp<AaptSymbols> syms;
1314                 if (!isGroup) {
1315                     syms = permissionSymbols;
1316                     if (syms == NULL) {
1317                         sp<AaptSymbols> symbols =
1318                                 assets->getSymbolsFor(String8("Manifest"));
1319                         syms = permissionSymbols = symbols->addNestedSymbol(
1320                                 String8("permission"), srcPos);
1321                     }
1322                 } else {
1323                     syms = permissionGroupSymbols;
1324                     if (syms == NULL) {
1325                         sp<AaptSymbols> symbols =
1326                                 assets->getSymbolsFor(String8("Manifest"));
1327                         syms = permissionGroupSymbols = symbols->addNestedSymbol(
1328                                 String8("permission_group"), srcPos);
1329                     }
1330                 }
1331                 size_t len;
1332                 ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name");
1333                 const uint16_t* id = block.getAttributeStringValue(index, &len);
1334                 if (id == NULL) {
1335                     fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n",
1336                             manifestPath.string(), block.getLineNumber(),
1337                             String8(block.getElementName(&len)).string());
1338                     hasErrors = true;
1339                     break;
1340                 }
1341                 String8 idStr(id);
1342                 char* p = idStr.lockBuffer(idStr.size());
1343                 char* e = p + idStr.size();
1344                 bool begins_with_digit = true;  // init to true so an empty string fails
1345                 while (e > p) {
1346                     e--;
1347                     if (*e >= '0' && *e <= '9') {
1348                       begins_with_digit = true;
1349                       continue;
1350                     }
1351                     if ((*e >= 'a' && *e <= 'z') ||
1352                         (*e >= 'A' && *e <= 'Z') ||
1353                         (*e == '_')) {
1354                       begins_with_digit = false;
1355                       continue;
1356                     }
1357                     if (isGroup && (*e == '-')) {
1358                         *e = '_';
1359                         begins_with_digit = false;
1360                         continue;
1361                     }
1362                     e++;
1363                     break;
1364                 }
1365                 idStr.unlockBuffer();
1366                 // verify that we stopped because we hit a period or
1367                 // the beginning of the string, and that the
1368                 // identifier didn't begin with a digit.
1369                 if (begins_with_digit || (e != p && *(e-1) != '.')) {
1370                   fprintf(stderr,
1371                           "%s:%d: Permission name <%s> is not a valid Java symbol\n",
1372                           manifestPath.string(), block.getLineNumber(), idStr.string());
1373                   hasErrors = true;
1374                 }
1375                 syms->addStringSymbol(String8(e), idStr, srcPos);
1376                 const uint16_t* cmt = block.getComment(&len);
1377                 if (cmt != NULL && *cmt != 0) {
1378                     //printf("Comment of %s: %s\n", String8(e).string(),
1379                     //        String8(cmt).string());
1380                     syms->appendComment(String8(e), String16(cmt), srcPos);
1381                 } else {
1382                     //printf("No comment for %s\n", String8(e).string());
1383                 }
1384                 syms->makeSymbolPublic(String8(e), srcPos);
1385             } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) {
1386                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1387                                  "name", packageIdentChars, true) != ATTR_OKAY) {
1388                     hasErrors = true;
1389                 }
1390             } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) {
1391                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1392                                  "name", classIdentChars, true) != ATTR_OKAY) {
1393                     hasErrors = true;
1394                 }
1395                 if (validateAttr(manifestPath, finalResTable, block,
1396                                  RESOURCES_ANDROID_NAMESPACE, "targetPackage",
1397                                  packageIdentChars, true) != ATTR_OKAY) {
1398                     hasErrors = true;
1399                 }
1400             } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) {
1401                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1402                                  "name", classIdentChars, false) != ATTR_OKAY) {
1403                     hasErrors = true;
1404                 }
1405                 if (validateAttr(manifestPath, finalResTable, block,
1406                                  RESOURCES_ANDROID_NAMESPACE, "permission",
1407                                  packageIdentChars, false) != ATTR_OKAY) {
1408                     hasErrors = true;
1409                 }
1410                 if (validateAttr(manifestPath, finalResTable, block,
1411                                  RESOURCES_ANDROID_NAMESPACE, "process",
1412                                  processIdentChars, false) != ATTR_OKAY) {
1413                     hasErrors = true;
1414                 }
1415                 if (validateAttr(manifestPath, finalResTable, block,
1416                                  RESOURCES_ANDROID_NAMESPACE, "taskAffinity",
1417                                  processIdentChars, false) != ATTR_OKAY) {
1418                     hasErrors = true;
1419                 }
1420             } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) {
1421                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1422                                  "name", classIdentChars, true) != ATTR_OKAY) {
1423                     hasErrors = true;
1424                 }
1425                 if (validateAttr(manifestPath, finalResTable, block,
1426                                  RESOURCES_ANDROID_NAMESPACE, "authorities",
1427                                  authoritiesIdentChars, true) != ATTR_OKAY) {
1428                     hasErrors = true;
1429                 }
1430                 if (validateAttr(manifestPath, finalResTable, block,
1431                                  RESOURCES_ANDROID_NAMESPACE, "permission",
1432                                  packageIdentChars, false) != ATTR_OKAY) {
1433                     hasErrors = true;
1434                 }
1435                 if (validateAttr(manifestPath, finalResTable, block,
1436                                  RESOURCES_ANDROID_NAMESPACE, "process",
1437                                  processIdentChars, false) != ATTR_OKAY) {
1438                     hasErrors = true;
1439                 }
1440             } else if (strcmp16(block.getElementName(&len), service16.string()) == 0
1441                        || strcmp16(block.getElementName(&len), receiver16.string()) == 0
1442                        || strcmp16(block.getElementName(&len), activity16.string()) == 0) {
1443                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1444                                  "name", classIdentChars, true) != ATTR_OKAY) {
1445                     hasErrors = true;
1446                 }
1447                 if (validateAttr(manifestPath, finalResTable, block,
1448                                  RESOURCES_ANDROID_NAMESPACE, "permission",
1449                                  packageIdentChars, false) != ATTR_OKAY) {
1450                     hasErrors = true;
1451                 }
1452                 if (validateAttr(manifestPath, finalResTable, block,
1453                                  RESOURCES_ANDROID_NAMESPACE, "process",
1454                                  processIdentChars, false) != ATTR_OKAY) {
1455                     hasErrors = true;
1456                 }
1457                 if (validateAttr(manifestPath, finalResTable, block,
1458                                  RESOURCES_ANDROID_NAMESPACE, "taskAffinity",
1459                                  processIdentChars, false) != ATTR_OKAY) {
1460                     hasErrors = true;
1461                 }
1462             } else if (strcmp16(block.getElementName(&len), action16.string()) == 0
1463                        || strcmp16(block.getElementName(&len), category16.string()) == 0) {
1464                 if (validateAttr(manifestPath, finalResTable, block,
1465                                  RESOURCES_ANDROID_NAMESPACE, "name",
1466                                  packageIdentChars, true) != ATTR_OKAY) {
1467                     hasErrors = true;
1468                 }
1469             } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) {
1470                 if (validateAttr(manifestPath, finalResTable, block,
1471                                  RESOURCES_ANDROID_NAMESPACE, "mimeType",
1472                                  typeIdentChars, true) != ATTR_OKAY) {
1473                     hasErrors = true;
1474                 }
1475                 if (validateAttr(manifestPath, finalResTable, block,
1476                                  RESOURCES_ANDROID_NAMESPACE, "scheme",
1477                                  schemeIdentChars, true) != ATTR_OKAY) {
1478                     hasErrors = true;
1479                 }
1480             }
1481         }
1482     }
1483 
1484     if (resFile != NULL) {
1485         // These resources are now considered to be a part of the included
1486         // resources, for others to reference.
1487         err = assets->addIncludedResources(resFile);
1488         if (err < NO_ERROR) {
1489             fprintf(stderr, "ERROR: Unable to parse generated resources, aborting.\n");
1490             return err;
1491         }
1492     }
1493 
1494     return err;
1495 }
1496 
getIndentSpace(int indent)1497 static const char* getIndentSpace(int indent)
1498 {
1499 static const char whitespace[] =
1500 "                                                                                       ";
1501 
1502     return whitespace + sizeof(whitespace) - 1 - indent*4;
1503 }
1504 
fixupSymbol(String16 * inoutSymbol)1505 static status_t fixupSymbol(String16* inoutSymbol)
1506 {
1507     inoutSymbol->replaceAll('.', '_');
1508     inoutSymbol->replaceAll(':', '_');
1509     return NO_ERROR;
1510 }
1511 
getAttributeComment(const sp<AaptAssets> & assets,const String8 & name,String16 * outTypeComment=NULL)1512 static String16 getAttributeComment(const sp<AaptAssets>& assets,
1513                                     const String8& name,
1514                                     String16* outTypeComment = NULL)
1515 {
1516     sp<AaptSymbols> asym = assets->getSymbolsFor(String8("R"));
1517     if (asym != NULL) {
1518         //printf("Got R symbols!\n");
1519         asym = asym->getNestedSymbols().valueFor(String8("attr"));
1520         if (asym != NULL) {
1521             //printf("Got attrs symbols! comment %s=%s\n",
1522             //     name.string(), String8(asym->getComment(name)).string());
1523             if (outTypeComment != NULL) {
1524                 *outTypeComment = asym->getTypeComment(name);
1525             }
1526             return asym->getComment(name);
1527         }
1528     }
1529     return String16();
1530 }
1531 
writeLayoutClasses(FILE * fp,const sp<AaptAssets> & assets,const sp<AaptSymbols> & symbols,int indent,bool includePrivate)1532 static status_t writeLayoutClasses(
1533     FILE* fp, const sp<AaptAssets>& assets,
1534     const sp<AaptSymbols>& symbols, int indent, bool includePrivate)
1535 {
1536     const char* indentStr = getIndentSpace(indent);
1537     if (!includePrivate) {
1538         fprintf(fp, "%s/** @doconly */\n", indentStr);
1539     }
1540     fprintf(fp, "%spublic static final class styleable {\n", indentStr);
1541     indent++;
1542 
1543     String16 attr16("attr");
1544     String16 package16(assets->getPackage());
1545 
1546     indentStr = getIndentSpace(indent);
1547     bool hasErrors = false;
1548 
1549     size_t i;
1550     size_t N = symbols->getNestedSymbols().size();
1551     for (i=0; i<N; i++) {
1552         sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
1553         String16 nclassName16(symbols->getNestedSymbols().keyAt(i));
1554         String8 realClassName(nclassName16);
1555         if (fixupSymbol(&nclassName16) != NO_ERROR) {
1556             hasErrors = true;
1557         }
1558         String8 nclassName(nclassName16);
1559 
1560         SortedVector<uint32_t> idents;
1561         Vector<uint32_t> origOrder;
1562         Vector<bool> publicFlags;
1563 
1564         size_t a;
1565         size_t NA = nsymbols->getSymbols().size();
1566         for (a=0; a<NA; a++) {
1567             const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a));
1568             int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32
1569                     ? sym.int32Val : 0;
1570             bool isPublic = true;
1571             if (code == 0) {
1572                 String16 name16(sym.name);
1573                 uint32_t typeSpecFlags;
1574                 code = assets->getIncludedResources().identifierForName(
1575                     name16.string(), name16.size(),
1576                     attr16.string(), attr16.size(),
1577                     package16.string(), package16.size(), &typeSpecFlags);
1578                 if (code == 0) {
1579                     fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n",
1580                             nclassName.string(), sym.name.string());
1581                     hasErrors = true;
1582                 }
1583                 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
1584             }
1585             idents.add(code);
1586             origOrder.add(code);
1587             publicFlags.add(isPublic);
1588         }
1589 
1590         NA = idents.size();
1591 
1592         bool deprecated = false;
1593 
1594         String16 comment = symbols->getComment(realClassName);
1595         fprintf(fp, "%s/** ", indentStr);
1596         if (comment.size() > 0) {
1597             String8 cmt(comment);
1598             fprintf(fp, "%s\n", cmt.string());
1599             if (strstr(cmt.string(), "@deprecated") != NULL) {
1600                 deprecated = true;
1601             }
1602         } else {
1603             fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string());
1604         }
1605         bool hasTable = false;
1606         for (a=0; a<NA; a++) {
1607             ssize_t pos = idents.indexOf(origOrder.itemAt(a));
1608             if (pos >= 0) {
1609                 if (!hasTable) {
1610                     hasTable = true;
1611                     fprintf(fp,
1612                             "%s   <p>Includes the following attributes:</p>\n"
1613                             "%s   <table>\n"
1614                             "%s   <colgroup align=\"left\" />\n"
1615                             "%s   <colgroup align=\"left\" />\n"
1616                             "%s   <tr><th>Attribute</th><th>Description</th></tr>\n",
1617                             indentStr,
1618                             indentStr,
1619                             indentStr,
1620                             indentStr,
1621                             indentStr);
1622                 }
1623                 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
1624                 if (!publicFlags.itemAt(a) && !includePrivate) {
1625                     continue;
1626                 }
1627                 String8 name8(sym.name);
1628                 String16 comment(sym.comment);
1629                 if (comment.size() <= 0) {
1630                     comment = getAttributeComment(assets, name8);
1631                 }
1632                 if (comment.size() > 0) {
1633                     const char16_t* p = comment.string();
1634                     while (*p != 0 && *p != '.') {
1635                         if (*p == '{') {
1636                             while (*p != 0 && *p != '}') {
1637                                 p++;
1638                             }
1639                         } else {
1640                             p++;
1641                         }
1642                     }
1643                     if (*p == '.') {
1644                         p++;
1645                     }
1646                     comment = String16(comment.string(), p-comment.string());
1647                 }
1648                 String16 name(name8);
1649                 fixupSymbol(&name);
1650                 fprintf(fp, "%s   <tr><td><code>{@link #%s_%s %s:%s}</code></td><td>%s</td></tr>\n",
1651                         indentStr, nclassName.string(),
1652                         String8(name).string(),
1653                         assets->getPackage().string(),
1654                         String8(name).string(),
1655                         String8(comment).string());
1656             }
1657         }
1658         if (hasTable) {
1659             fprintf(fp, "%s   </table>\n", indentStr);
1660         }
1661         for (a=0; a<NA; a++) {
1662             ssize_t pos = idents.indexOf(origOrder.itemAt(a));
1663             if (pos >= 0) {
1664                 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
1665                 if (!publicFlags.itemAt(a) && !includePrivate) {
1666                     continue;
1667                 }
1668                 String16 name(sym.name);
1669                 fixupSymbol(&name);
1670                 fprintf(fp, "%s   @see #%s_%s\n",
1671                         indentStr, nclassName.string(),
1672                         String8(name).string());
1673             }
1674         }
1675         fprintf(fp, "%s */\n", getIndentSpace(indent));
1676 
1677         if (deprecated) {
1678             fprintf(fp, "%s@Deprecated\n", indentStr);
1679         }
1680 
1681         fprintf(fp,
1682                 "%spublic static final int[] %s = {\n"
1683                 "%s",
1684                 indentStr, nclassName.string(),
1685                 getIndentSpace(indent+1));
1686 
1687         for (a=0; a<NA; a++) {
1688             if (a != 0) {
1689                 if ((a&3) == 0) {
1690                     fprintf(fp, ",\n%s", getIndentSpace(indent+1));
1691                 } else {
1692                     fprintf(fp, ", ");
1693                 }
1694             }
1695             fprintf(fp, "0x%08x", idents[a]);
1696         }
1697 
1698         fprintf(fp, "\n%s};\n", indentStr);
1699 
1700         for (a=0; a<NA; a++) {
1701             ssize_t pos = idents.indexOf(origOrder.itemAt(a));
1702             if (pos >= 0) {
1703                 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
1704                 if (!publicFlags.itemAt(a) && !includePrivate) {
1705                     continue;
1706                 }
1707                 String8 name8(sym.name);
1708                 String16 comment(sym.comment);
1709                 String16 typeComment;
1710                 if (comment.size() <= 0) {
1711                     comment = getAttributeComment(assets, name8, &typeComment);
1712                 } else {
1713                     getAttributeComment(assets, name8, &typeComment);
1714                 }
1715                 String16 name(name8);
1716                 if (fixupSymbol(&name) != NO_ERROR) {
1717                     hasErrors = true;
1718                 }
1719 
1720                 uint32_t typeSpecFlags = 0;
1721                 String16 name16(sym.name);
1722                 assets->getIncludedResources().identifierForName(
1723                     name16.string(), name16.size(),
1724                     attr16.string(), attr16.size(),
1725                     package16.string(), package16.size(), &typeSpecFlags);
1726                 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
1727                 //    String8(attr16).string(), String8(name16).string(), typeSpecFlags);
1728                 const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
1729 
1730                 bool deprecated = false;
1731 
1732                 fprintf(fp, "%s/**\n", indentStr);
1733                 if (comment.size() > 0) {
1734                     String8 cmt(comment);
1735                     fprintf(fp, "%s  <p>\n%s  @attr description\n", indentStr, indentStr);
1736                     fprintf(fp, "%s  %s\n", indentStr, cmt.string());
1737                     if (strstr(cmt.string(), "@deprecated") != NULL) {
1738                         deprecated = true;
1739                     }
1740                 } else {
1741                     fprintf(fp,
1742                             "%s  <p>This symbol is the offset where the {@link %s.R.attr#%s}\n"
1743                             "%s  attribute's value can be found in the {@link #%s} array.\n",
1744                             indentStr,
1745                             pub ? assets->getPackage().string()
1746                                 : assets->getSymbolsPrivatePackage().string(),
1747                             String8(name).string(),
1748                             indentStr, nclassName.string());
1749                 }
1750                 if (typeComment.size() > 0) {
1751                     String8 cmt(typeComment);
1752                     fprintf(fp, "\n\n%s  %s\n", indentStr, cmt.string());
1753                     if (strstr(cmt.string(), "@deprecated") != NULL) {
1754                         deprecated = true;
1755                     }
1756                 }
1757                 if (comment.size() > 0) {
1758                     if (pub) {
1759                         fprintf(fp,
1760                                 "%s  <p>This corresponds to the global attribute"
1761                                 "%s  resource symbol {@link %s.R.attr#%s}.\n",
1762                                 indentStr, indentStr,
1763                                 assets->getPackage().string(),
1764                                 String8(name).string());
1765                     } else {
1766                         fprintf(fp,
1767                                 "%s  <p>This is a private symbol.\n", indentStr);
1768                     }
1769                 }
1770                 fprintf(fp, "%s  @attr name %s:%s\n", indentStr,
1771                         "android", String8(name).string());
1772                 fprintf(fp, "%s*/\n", indentStr);
1773                 if (deprecated) {
1774                     fprintf(fp, "%s@Deprecated\n", indentStr);
1775                 }
1776                 fprintf(fp,
1777                         "%spublic static final int %s_%s = %d;\n",
1778                         indentStr, nclassName.string(),
1779                         String8(name).string(), (int)pos);
1780             }
1781         }
1782     }
1783 
1784     indent--;
1785     fprintf(fp, "%s};\n", getIndentSpace(indent));
1786     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
1787 }
1788 
writeSymbolClass(FILE * fp,const sp<AaptAssets> & assets,bool includePrivate,const sp<AaptSymbols> & symbols,const String8 & className,int indent,bool nonConstantId)1789 static status_t writeSymbolClass(
1790     FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
1791     const sp<AaptSymbols>& symbols, const String8& className, int indent,
1792     bool nonConstantId)
1793 {
1794     fprintf(fp, "%spublic %sfinal class %s {\n",
1795             getIndentSpace(indent),
1796             indent != 0 ? "static " : "", className.string());
1797     indent++;
1798 
1799     size_t i;
1800     status_t err = NO_ERROR;
1801 
1802     const char * id_format = nonConstantId ?
1803             "%spublic static int %s=0x%08x;\n" :
1804             "%spublic static final int %s=0x%08x;\n";
1805 
1806     size_t N = symbols->getSymbols().size();
1807     for (i=0; i<N; i++) {
1808         const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
1809         if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) {
1810             continue;
1811         }
1812         if (!includePrivate && !sym.isPublic) {
1813             continue;
1814         }
1815         String16 name(sym.name);
1816         String8 realName(name);
1817         if (fixupSymbol(&name) != NO_ERROR) {
1818             return UNKNOWN_ERROR;
1819         }
1820         String16 comment(sym.comment);
1821         bool haveComment = false;
1822         bool deprecated = false;
1823         if (comment.size() > 0) {
1824             haveComment = true;
1825             String8 cmt(comment);
1826             fprintf(fp,
1827                     "%s/** %s\n",
1828                     getIndentSpace(indent), cmt.string());
1829             if (strstr(cmt.string(), "@deprecated") != NULL) {
1830                 deprecated = true;
1831             }
1832         } else if (sym.isPublic && !includePrivate) {
1833             sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
1834                 assets->getPackage().string(), className.string(),
1835                 String8(sym.name).string());
1836         }
1837         String16 typeComment(sym.typeComment);
1838         if (typeComment.size() > 0) {
1839             String8 cmt(typeComment);
1840             if (!haveComment) {
1841                 haveComment = true;
1842                 fprintf(fp,
1843                         "%s/** %s\n", getIndentSpace(indent), cmt.string());
1844             } else {
1845                 fprintf(fp,
1846                         "%s %s\n", getIndentSpace(indent), cmt.string());
1847             }
1848             if (strstr(cmt.string(), "@deprecated") != NULL) {
1849                 deprecated = true;
1850             }
1851         }
1852         if (haveComment) {
1853             fprintf(fp,"%s */\n", getIndentSpace(indent));
1854         }
1855         if (deprecated) {
1856             fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
1857         }
1858         fprintf(fp, id_format,
1859                 getIndentSpace(indent),
1860                 String8(name).string(), (int)sym.int32Val);
1861     }
1862 
1863     for (i=0; i<N; i++) {
1864         const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
1865         if (sym.typeCode != AaptSymbolEntry::TYPE_STRING) {
1866             continue;
1867         }
1868         if (!includePrivate && !sym.isPublic) {
1869             continue;
1870         }
1871         String16 name(sym.name);
1872         if (fixupSymbol(&name) != NO_ERROR) {
1873             return UNKNOWN_ERROR;
1874         }
1875         String16 comment(sym.comment);
1876         bool deprecated = false;
1877         if (comment.size() > 0) {
1878             String8 cmt(comment);
1879             fprintf(fp,
1880                     "%s/** %s\n"
1881                      "%s */\n",
1882                     getIndentSpace(indent), cmt.string(),
1883                     getIndentSpace(indent));
1884             if (strstr(cmt.string(), "@deprecated") != NULL) {
1885                 deprecated = true;
1886             }
1887         } else if (sym.isPublic && !includePrivate) {
1888             sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
1889                 assets->getPackage().string(), className.string(),
1890                 String8(sym.name).string());
1891         }
1892         if (deprecated) {
1893             fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
1894         }
1895         fprintf(fp, "%spublic static final String %s=\"%s\";\n",
1896                 getIndentSpace(indent),
1897                 String8(name).string(), sym.stringVal.string());
1898     }
1899 
1900     sp<AaptSymbols> styleableSymbols;
1901 
1902     N = symbols->getNestedSymbols().size();
1903     for (i=0; i<N; i++) {
1904         sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
1905         String8 nclassName(symbols->getNestedSymbols().keyAt(i));
1906         if (nclassName == "styleable") {
1907             styleableSymbols = nsymbols;
1908         } else {
1909             err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent, nonConstantId);
1910         }
1911         if (err != NO_ERROR) {
1912             return err;
1913         }
1914     }
1915 
1916     if (styleableSymbols != NULL) {
1917         err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate);
1918         if (err != NO_ERROR) {
1919             return err;
1920         }
1921     }
1922 
1923     indent--;
1924     fprintf(fp, "%s}\n", getIndentSpace(indent));
1925     return NO_ERROR;
1926 }
1927 
writeResourceSymbols(Bundle * bundle,const sp<AaptAssets> & assets,const String8 & package,bool includePrivate)1928 status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
1929     const String8& package, bool includePrivate)
1930 {
1931     if (!bundle->getRClassDir()) {
1932         return NO_ERROR;
1933     }
1934 
1935     const size_t N = assets->getSymbols().size();
1936     for (size_t i=0; i<N; i++) {
1937         sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i);
1938         String8 className(assets->getSymbols().keyAt(i));
1939         String8 dest(bundle->getRClassDir());
1940         if (bundle->getMakePackageDirs()) {
1941             String8 pkg(package);
1942             const char* last = pkg.string();
1943             const char* s = last-1;
1944             do {
1945                 s++;
1946                 if (s > last && (*s == '.' || *s == 0)) {
1947                     String8 part(last, s-last);
1948                     dest.appendPath(part);
1949 #ifdef HAVE_MS_C_RUNTIME
1950                     _mkdir(dest.string());
1951 #else
1952                     mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
1953 #endif
1954                     last = s+1;
1955                 }
1956             } while (*s);
1957         }
1958         dest.appendPath(className);
1959         dest.append(".java");
1960         FILE* fp = fopen(dest.string(), "w+");
1961         if (fp == NULL) {
1962             fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
1963                     dest.string(), strerror(errno));
1964             return UNKNOWN_ERROR;
1965         }
1966         if (bundle->getVerbose()) {
1967             printf("  Writing symbols for class %s.\n", className.string());
1968         }
1969 
1970         fprintf(fp,
1971         "/* AUTO-GENERATED FILE.  DO NOT MODIFY.\n"
1972         " *\n"
1973         " * This class was automatically generated by the\n"
1974         " * aapt tool from the resource data it found.  It\n"
1975         " * should not be modified by hand.\n"
1976         " */\n"
1977         "\n"
1978         "package %s;\n\n", package.string());
1979 
1980         status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, className, 0, bundle->getNonConstantId());
1981         if (err != NO_ERROR) {
1982             return err;
1983         }
1984         fclose(fp);
1985 
1986         // If we were asked to generate a dependency file, we'll go ahead and add this R.java
1987         // as a target in the dependency file right next to it.
1988         if (bundle->getGenDependencies()) {
1989             // Add this R.java to the dependency file
1990             String8 dependencyFile(bundle->getRClassDir());
1991             dependencyFile.appendPath("R.java.d");
1992 
1993             fp = fopen(dependencyFile.string(), "a");
1994             fprintf(fp,"%s \\\n", dest.string());
1995             fclose(fp);
1996         }
1997     }
1998 
1999     return NO_ERROR;
2000 }
2001 
2002 
2003 
2004 class ProguardKeepSet
2005 {
2006 public:
2007     // { rule --> { file locations } }
2008     KeyedVector<String8, SortedVector<String8> > rules;
2009 
2010     void add(const String8& rule, const String8& where);
2011 };
2012 
add(const String8 & rule,const String8 & where)2013 void ProguardKeepSet::add(const String8& rule, const String8& where)
2014 {
2015     ssize_t index = rules.indexOfKey(rule);
2016     if (index < 0) {
2017         index = rules.add(rule, SortedVector<String8>());
2018     }
2019     rules.editValueAt(index).add(where);
2020 }
2021 
2022 void
addProguardKeepRule(ProguardKeepSet * keep,const String8 & inClassName,const char * pkg,const String8 & srcName,int line)2023 addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName,
2024         const char* pkg, const String8& srcName, int line)
2025 {
2026     String8 className(inClassName);
2027     if (pkg != NULL) {
2028         // asdf     --> package.asdf
2029         // .asdf  .a.b  --> package.asdf package.a.b
2030         // asdf.adsf --> asdf.asdf
2031         const char* p = className.string();
2032         const char* q = strchr(p, '.');
2033         if (p == q) {
2034             className = pkg;
2035             className.append(inClassName);
2036         } else if (q == NULL) {
2037             className = pkg;
2038             className.append(".");
2039             className.append(inClassName);
2040         }
2041     }
2042 
2043     String8 rule("-keep class ");
2044     rule += className;
2045     rule += " { <init>(...); }";
2046 
2047     String8 location("view ");
2048     location += srcName;
2049     char lineno[20];
2050     sprintf(lineno, ":%d", line);
2051     location += lineno;
2052 
2053     keep->add(rule, location);
2054 }
2055 
2056 status_t
writeProguardForAndroidManifest(ProguardKeepSet * keep,const sp<AaptAssets> & assets)2057 writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
2058 {
2059     status_t err;
2060     ResXMLTree tree;
2061     size_t len;
2062     ResXMLTree::event_code_t code;
2063     int depth = 0;
2064     bool inApplication = false;
2065     String8 error;
2066     sp<AaptGroup> assGroup;
2067     sp<AaptFile> assFile;
2068     String8 pkg;
2069 
2070     // First, look for a package file to parse.  This is required to
2071     // be able to generate the resource information.
2072     assGroup = assets->getFiles().valueFor(String8("AndroidManifest.xml"));
2073     if (assGroup == NULL) {
2074         fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n");
2075         return -1;
2076     }
2077 
2078     if (assGroup->getFiles().size() != 1) {
2079         fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
2080                 assGroup->getFiles().valueAt(0)->getPrintableSource().string());
2081     }
2082 
2083     assFile = assGroup->getFiles().valueAt(0);
2084 
2085     err = parseXMLResource(assFile, &tree);
2086     if (err != NO_ERROR) {
2087         return err;
2088     }
2089 
2090     tree.restart();
2091 
2092     while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
2093         if (code == ResXMLTree::END_TAG) {
2094             if (/* name == "Application" && */ depth == 2) {
2095                 inApplication = false;
2096             }
2097             depth--;
2098             continue;
2099         }
2100         if (code != ResXMLTree::START_TAG) {
2101             continue;
2102         }
2103         depth++;
2104         String8 tag(tree.getElementName(&len));
2105         // printf("Depth %d tag %s\n", depth, tag.string());
2106         bool keepTag = false;
2107         if (depth == 1) {
2108             if (tag != "manifest") {
2109                 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
2110                 return -1;
2111             }
2112             pkg = getAttribute(tree, NULL, "package", NULL);
2113         } else if (depth == 2) {
2114             if (tag == "application") {
2115                 inApplication = true;
2116                 keepTag = true;
2117 
2118                 String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android",
2119                         "backupAgent", &error);
2120                 if (agent.length() > 0) {
2121                     addProguardKeepRule(keep, agent, pkg.string(),
2122                             assFile->getPrintableSource(), tree.getLineNumber());
2123                 }
2124             } else if (tag == "instrumentation") {
2125                 keepTag = true;
2126             }
2127         }
2128         if (!keepTag && inApplication && depth == 3) {
2129             if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
2130                 keepTag = true;
2131             }
2132         }
2133         if (keepTag) {
2134             String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android",
2135                     "name", &error);
2136             if (error != "") {
2137                 fprintf(stderr, "ERROR: %s\n", error.string());
2138                 return -1;
2139             }
2140             if (name.length() > 0) {
2141                 addProguardKeepRule(keep, name, pkg.string(),
2142                         assFile->getPrintableSource(), tree.getLineNumber());
2143             }
2144         }
2145     }
2146 
2147     return NO_ERROR;
2148 }
2149 
2150 struct NamespaceAttributePair {
2151     const char* ns;
2152     const char* attr;
2153 
NamespaceAttributePairNamespaceAttributePair2154     NamespaceAttributePair(const char* n, const char* a) : ns(n), attr(a) {}
NamespaceAttributePairNamespaceAttributePair2155     NamespaceAttributePair() : ns(NULL), attr(NULL) {}
2156 };
2157 
2158 status_t
writeProguardForXml(ProguardKeepSet * keep,const sp<AaptFile> & layoutFile,const char * startTag,const KeyedVector<String8,NamespaceAttributePair> * tagAttrPairs)2159 writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
2160         const char* startTag, const KeyedVector<String8, NamespaceAttributePair>* tagAttrPairs)
2161 {
2162     status_t err;
2163     ResXMLTree tree;
2164     size_t len;
2165     ResXMLTree::event_code_t code;
2166 
2167     err = parseXMLResource(layoutFile, &tree);
2168     if (err != NO_ERROR) {
2169         return err;
2170     }
2171 
2172     tree.restart();
2173 
2174     if (startTag != NULL) {
2175         bool haveStart = false;
2176         while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
2177             if (code != ResXMLTree::START_TAG) {
2178                 continue;
2179             }
2180             String8 tag(tree.getElementName(&len));
2181             if (tag == startTag) {
2182                 haveStart = true;
2183             }
2184             break;
2185         }
2186         if (!haveStart) {
2187             return NO_ERROR;
2188         }
2189     }
2190 
2191     while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
2192         if (code != ResXMLTree::START_TAG) {
2193             continue;
2194         }
2195         String8 tag(tree.getElementName(&len));
2196 
2197         // If there is no '.', we'll assume that it's one of the built in names.
2198         if (strchr(tag.string(), '.')) {
2199             addProguardKeepRule(keep, tag, NULL,
2200                     layoutFile->getPrintableSource(), tree.getLineNumber());
2201         } else if (tagAttrPairs != NULL) {
2202             ssize_t tagIndex = tagAttrPairs->indexOfKey(tag);
2203             if (tagIndex >= 0) {
2204                 const NamespaceAttributePair& nsAttr = tagAttrPairs->valueAt(tagIndex);
2205                 ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr);
2206                 if (attrIndex < 0) {
2207                     // fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n",
2208                     //        layoutFile->getPrintableSource().string(), tree.getLineNumber(),
2209                     //        tag.string(), nsAttr.ns, nsAttr.attr);
2210                 } else {
2211                     size_t len;
2212                     addProguardKeepRule(keep,
2213                                         String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
2214                                         layoutFile->getPrintableSource(), tree.getLineNumber());
2215                 }
2216             }
2217         }
2218     }
2219 
2220     return NO_ERROR;
2221 }
2222 
addTagAttrPair(KeyedVector<String8,NamespaceAttributePair> * dest,const char * tag,const char * ns,const char * attr)2223 static void addTagAttrPair(KeyedVector<String8, NamespaceAttributePair>* dest,
2224         const char* tag, const char* ns, const char* attr) {
2225     dest->add(String8(tag), NamespaceAttributePair(ns, attr));
2226 }
2227 
2228 status_t
writeProguardForLayouts(ProguardKeepSet * keep,const sp<AaptAssets> & assets)2229 writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
2230 {
2231     status_t err;
2232 
2233     // tag:attribute pairs that should be checked in layout files.
2234     KeyedVector<String8, NamespaceAttributePair> kLayoutTagAttrPairs;
2235     addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class");
2236     addTagAttrPair(&kLayoutTagAttrPairs, "fragment", NULL, "class");
2237     addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name");
2238 
2239     // tag:attribute pairs that should be checked in xml files.
2240     KeyedVector<String8, NamespaceAttributePair> kXmlTagAttrPairs;
2241     addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment");
2242     addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment");
2243 
2244     const Vector<sp<AaptDir> >& dirs = assets->resDirs();
2245     const size_t K = dirs.size();
2246     for (size_t k=0; k<K; k++) {
2247         const sp<AaptDir>& d = dirs.itemAt(k);
2248         const String8& dirName = d->getLeaf();
2249         const char* startTag = NULL;
2250         const KeyedVector<String8, NamespaceAttributePair>* tagAttrPairs = NULL;
2251         if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) {
2252             tagAttrPairs = &kLayoutTagAttrPairs;
2253         } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
2254             startTag = "PreferenceScreen";
2255             tagAttrPairs = &kXmlTagAttrPairs;
2256         } else {
2257             continue;
2258         }
2259 
2260         const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles();
2261         const size_t N = groups.size();
2262         for (size_t i=0; i<N; i++) {
2263             const sp<AaptGroup>& group = groups.valueAt(i);
2264             const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles();
2265             const size_t M = files.size();
2266             for (size_t j=0; j<M; j++) {
2267                 err = writeProguardForXml(keep, files.valueAt(j), startTag, tagAttrPairs);
2268                 if (err < 0) {
2269                     return err;
2270                 }
2271             }
2272         }
2273     }
2274     // Handle the overlays
2275     sp<AaptAssets> overlay = assets->getOverlay();
2276     if (overlay.get()) {
2277         return writeProguardForLayouts(keep, overlay);
2278     }
2279     return NO_ERROR;
2280 }
2281 
2282 status_t
writeProguardFile(Bundle * bundle,const sp<AaptAssets> & assets)2283 writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets)
2284 {
2285     status_t err = -1;
2286 
2287     if (!bundle->getProguardFile()) {
2288         return NO_ERROR;
2289     }
2290 
2291     ProguardKeepSet keep;
2292 
2293     err = writeProguardForAndroidManifest(&keep, assets);
2294     if (err < 0) {
2295         return err;
2296     }
2297 
2298     err = writeProguardForLayouts(&keep, assets);
2299     if (err < 0) {
2300         return err;
2301     }
2302 
2303     FILE* fp = fopen(bundle->getProguardFile(), "w+");
2304     if (fp == NULL) {
2305         fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
2306                 bundle->getProguardFile(), strerror(errno));
2307         return UNKNOWN_ERROR;
2308     }
2309 
2310     const KeyedVector<String8, SortedVector<String8> >& rules = keep.rules;
2311     const size_t N = rules.size();
2312     for (size_t i=0; i<N; i++) {
2313         const SortedVector<String8>& locations = rules.valueAt(i);
2314         const size_t M = locations.size();
2315         for (size_t j=0; j<M; j++) {
2316             fprintf(fp, "# %s\n", locations.itemAt(j).string());
2317         }
2318         fprintf(fp, "%s\n\n", rules.keyAt(i).string());
2319     }
2320     fclose(fp);
2321 
2322     return err;
2323 }
2324 
2325 // Loops through the string paths and writes them to the file pointer
2326 // Each file path is written on its own line with a terminating backslash.
writePathsToFile(const sp<FilePathStore> & files,FILE * fp)2327 status_t writePathsToFile(const sp<FilePathStore>& files, FILE* fp)
2328 {
2329     status_t deps = -1;
2330     for (size_t file_i = 0; file_i < files->size(); ++file_i) {
2331         // Add the full file path to the dependency file
2332         fprintf(fp, "%s \\\n", files->itemAt(file_i).string());
2333         deps++;
2334     }
2335     return deps;
2336 }
2337 
2338 status_t
writeDependencyPreReqs(Bundle * bundle,const sp<AaptAssets> & assets,FILE * fp,bool includeRaw)2339 writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw)
2340 {
2341     status_t deps = -1;
2342     deps += writePathsToFile(assets->getFullResPaths(), fp);
2343     if (includeRaw) {
2344         deps += writePathsToFile(assets->getFullAssetPaths(), fp);
2345     }
2346     return deps;
2347 }
2348