• 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 "AaptAssets.h"
7 #include "AaptXml.h"
8 #include "CacheUpdater.h"
9 #include "CrunchCache.h"
10 #include "FileFinder.h"
11 #include "Images.h"
12 #include "IndentPrinter.h"
13 #include "Main.h"
14 #include "ResourceTable.h"
15 #include "StringPool.h"
16 #include "WorkQueue.h"
17 #include "XMLNode.h"
18 
19 #if HAVE_PRINTF_ZD
20 #  define ZD "%zd"
21 #  define ZD_TYPE ssize_t
22 #else
23 #  define ZD "%ld"
24 #  define ZD_TYPE long
25 #endif
26 
27 #define NOISY(x) // x
28 
29 // Number of threads to use for preprocessing images.
30 static const size_t MAX_THREADS = 4;
31 
32 // ==========================================================================
33 // ==========================================================================
34 // ==========================================================================
35 
36 class PackageInfo
37 {
38 public:
PackageInfo()39     PackageInfo()
40     {
41     }
~PackageInfo()42     ~PackageInfo()
43     {
44     }
45 
46     status_t parsePackage(const sp<AaptGroup>& grp);
47 };
48 
49 // ==========================================================================
50 // ==========================================================================
51 // ==========================================================================
52 
parseResourceName(const String8 & leaf)53 String8 parseResourceName(const String8& leaf)
54 {
55     const char* firstDot = strchr(leaf.string(), '.');
56     const char* str = leaf.string();
57 
58     if (firstDot) {
59         return String8(str, firstDot-str);
60     } else {
61         return String8(str);
62     }
63 }
64 
ResourceTypeSet()65 ResourceTypeSet::ResourceTypeSet()
66     :RefBase(),
67      KeyedVector<String8,sp<AaptGroup> >()
68 {
69 }
70 
FilePathStore()71 FilePathStore::FilePathStore()
72     :RefBase(),
73      Vector<String8>()
74 {
75 }
76 
77 class ResourceDirIterator
78 {
79 public:
ResourceDirIterator(const sp<ResourceTypeSet> & set,const String8 & resType)80     ResourceDirIterator(const sp<ResourceTypeSet>& set, const String8& resType)
81         : mResType(resType), mSet(set), mSetPos(0), mGroupPos(0)
82     {
83         memset(&mParams, 0, sizeof(ResTable_config));
84     }
85 
getGroup() const86     inline const sp<AaptGroup>& getGroup() const { return mGroup; }
getFile() const87     inline const sp<AaptFile>& getFile() const { return mFile; }
88 
getBaseName() const89     inline const String8& getBaseName() const { return mBaseName; }
getLeafName() const90     inline const String8& getLeafName() const { return mLeafName; }
getPath() const91     inline String8 getPath() const { return mPath; }
getParams() const92     inline const ResTable_config& getParams() const { return mParams; }
93 
94     enum {
95         EOD = 1
96     };
97 
next()98     ssize_t next()
99     {
100         while (true) {
101             sp<AaptGroup> group;
102             sp<AaptFile> file;
103 
104             // Try to get next file in this current group.
105             if (mGroup != NULL && mGroupPos < mGroup->getFiles().size()) {
106                 group = mGroup;
107                 file = group->getFiles().valueAt(mGroupPos++);
108 
109             // Try to get the next group/file in this directory
110             } else if (mSetPos < mSet->size()) {
111                 mGroup = group = mSet->valueAt(mSetPos++);
112                 if (group->getFiles().size() < 1) {
113                     continue;
114                 }
115                 file = group->getFiles().valueAt(0);
116                 mGroupPos = 1;
117 
118             // All done!
119             } else {
120                 return EOD;
121             }
122 
123             mFile = file;
124 
125             String8 leaf(group->getLeaf());
126             mLeafName = String8(leaf);
127             mParams = file->getGroupEntry().toParams();
128             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",
129                    group->getPath().string(), mParams.mcc, mParams.mnc,
130                    mParams.language[0] ? mParams.language[0] : '-',
131                    mParams.language[1] ? mParams.language[1] : '-',
132                    mParams.country[0] ? mParams.country[0] : '-',
133                    mParams.country[1] ? mParams.country[1] : '-',
134                    mParams.orientation, mParams.uiMode,
135                    mParams.density, mParams.touchscreen, mParams.keyboard,
136                    mParams.inputFlags, mParams.navigation));
137             mPath = "res";
138             mPath.appendPath(file->getGroupEntry().toDirName(mResType));
139             mPath.appendPath(leaf);
140             mBaseName = parseResourceName(leaf);
141             if (mBaseName == "") {
142                 fprintf(stderr, "Error: malformed resource filename %s\n",
143                         file->getPrintableSource().string());
144                 return UNKNOWN_ERROR;
145             }
146 
147             NOISY(printf("file name=%s\n", mBaseName.string()));
148 
149             return NO_ERROR;
150         }
151     }
152 
153 private:
154     String8 mResType;
155 
156     const sp<ResourceTypeSet> mSet;
157     size_t mSetPos;
158 
159     sp<AaptGroup> mGroup;
160     size_t mGroupPos;
161 
162     sp<AaptFile> mFile;
163     String8 mBaseName;
164     String8 mLeafName;
165     String8 mPath;
166     ResTable_config mParams;
167 };
168 
169 class AnnotationProcessor {
170 public:
AnnotationProcessor()171     AnnotationProcessor() : mDeprecated(false), mSystemApi(false) { }
172 
preprocessComment(String8 & comment)173     void preprocessComment(String8& comment) {
174         if (comment.size() > 0) {
175             if (comment.contains("@deprecated")) {
176                 mDeprecated = true;
177             }
178             if (comment.removeAll("@SystemApi")) {
179                 mSystemApi = true;
180             }
181         }
182     }
183 
printAnnotations(FILE * fp,const char * indentStr)184     void printAnnotations(FILE* fp, const char* indentStr) {
185         if (mDeprecated) {
186             fprintf(fp, "%s@Deprecated\n", indentStr);
187         }
188         if (mSystemApi) {
189             fprintf(fp, "%s@android.annotation.SystemApi\n", indentStr);
190         }
191     }
192 
193 private:
194     bool mDeprecated;
195     bool mSystemApi;
196 };
197 
198 // ==========================================================================
199 // ==========================================================================
200 // ==========================================================================
201 
isValidResourceType(const String8 & type)202 bool isValidResourceType(const String8& type)
203 {
204     return type == "anim" || type == "animator" || type == "interpolator"
205         || type == "transition"
206         || type == "drawable" || type == "layout"
207         || type == "values" || type == "xml" || type == "raw"
208         || type == "color" || type == "menu" || type == "mipmap";
209 }
210 
parsePackage(Bundle * bundle,const sp<AaptAssets> & assets,const sp<AaptGroup> & grp)211 static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets,
212     const sp<AaptGroup>& grp)
213 {
214     if (grp->getFiles().size() != 1) {
215         fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
216                 grp->getFiles().valueAt(0)->getPrintableSource().string());
217     }
218 
219     sp<AaptFile> file = grp->getFiles().valueAt(0);
220 
221     ResXMLTree block;
222     status_t err = parseXMLResource(file, &block);
223     if (err != NO_ERROR) {
224         return err;
225     }
226     //printXMLBlock(&block);
227 
228     ResXMLTree::event_code_t code;
229     while ((code=block.next()) != ResXMLTree::START_TAG
230            && code != ResXMLTree::END_DOCUMENT
231            && code != ResXMLTree::BAD_DOCUMENT) {
232     }
233 
234     size_t len;
235     if (code != ResXMLTree::START_TAG) {
236         fprintf(stderr, "%s:%d: No start tag found\n",
237                 file->getPrintableSource().string(), block.getLineNumber());
238         return UNKNOWN_ERROR;
239     }
240     if (strcmp16(block.getElementName(&len), String16("manifest").string()) != 0) {
241         fprintf(stderr, "%s:%d: Invalid start tag %s, expected <manifest>\n",
242                 file->getPrintableSource().string(), block.getLineNumber(),
243                 String8(block.getElementName(&len)).string());
244         return UNKNOWN_ERROR;
245     }
246 
247     ssize_t nameIndex = block.indexOfAttribute(NULL, "package");
248     if (nameIndex < 0) {
249         fprintf(stderr, "%s:%d: <manifest> does not have package attribute.\n",
250                 file->getPrintableSource().string(), block.getLineNumber());
251         return UNKNOWN_ERROR;
252     }
253 
254     assets->setPackage(String8(block.getAttributeStringValue(nameIndex, &len)));
255 
256     String16 uses_sdk16("uses-sdk");
257     while ((code=block.next()) != ResXMLTree::END_DOCUMENT
258            && code != ResXMLTree::BAD_DOCUMENT) {
259         if (code == ResXMLTree::START_TAG) {
260             if (strcmp16(block.getElementName(&len), uses_sdk16.string()) == 0) {
261                 ssize_t minSdkIndex = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
262                                                              "minSdkVersion");
263                 if (minSdkIndex >= 0) {
264                     const uint16_t* minSdk16 = block.getAttributeStringValue(minSdkIndex, &len);
265                     const char* minSdk8 = strdup(String8(minSdk16).string());
266                     bundle->setManifestMinSdkVersion(minSdk8);
267                 }
268             }
269         }
270     }
271 
272     return NO_ERROR;
273 }
274 
275 // ==========================================================================
276 // ==========================================================================
277 // ==========================================================================
278 
makeFileResources(Bundle * bundle,const sp<AaptAssets> & assets,ResourceTable * table,const sp<ResourceTypeSet> & set,const char * resType)279 static status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets,
280                                   ResourceTable* table,
281                                   const sp<ResourceTypeSet>& set,
282                                   const char* resType)
283 {
284     String8 type8(resType);
285     String16 type16(resType);
286 
287     bool hasErrors = false;
288 
289     ResourceDirIterator it(set, String8(resType));
290     ssize_t res;
291     while ((res=it.next()) == NO_ERROR) {
292         if (bundle->getVerbose()) {
293             printf("    (new resource id %s from %s)\n",
294                    it.getBaseName().string(), it.getFile()->getPrintableSource().string());
295         }
296         String16 baseName(it.getBaseName());
297         const char16_t* str = baseName.string();
298         const char16_t* const end = str + baseName.size();
299         while (str < end) {
300             if (!((*str >= 'a' && *str <= 'z')
301                     || (*str >= '0' && *str <= '9')
302                     || *str == '_' || *str == '.')) {
303                 fprintf(stderr, "%s: Invalid file name: must contain only [a-z0-9_.]\n",
304                         it.getPath().string());
305                 hasErrors = true;
306             }
307             str++;
308         }
309         String8 resPath = it.getPath();
310         resPath.convertToResPath();
311         table->addEntry(SourcePos(it.getPath(), 0), String16(assets->getPackage()),
312                         type16,
313                         baseName,
314                         String16(resPath),
315                         NULL,
316                         &it.getParams());
317         assets->addResource(it.getLeafName(), resPath, it.getFile(), type8);
318     }
319 
320     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
321 }
322 
323 class PreProcessImageWorkUnit : public WorkQueue::WorkUnit {
324 public:
PreProcessImageWorkUnit(const Bundle * bundle,const sp<AaptAssets> & assets,const sp<AaptFile> & file,volatile bool * hasErrors)325     PreProcessImageWorkUnit(const Bundle* bundle, const sp<AaptAssets>& assets,
326             const sp<AaptFile>& file, volatile bool* hasErrors) :
327             mBundle(bundle), mAssets(assets), mFile(file), mHasErrors(hasErrors) {
328     }
329 
run()330     virtual bool run() {
331         status_t status = preProcessImage(mBundle, mAssets, mFile, NULL);
332         if (status) {
333             *mHasErrors = true;
334         }
335         return true; // continue even if there are errors
336     }
337 
338 private:
339     const Bundle* mBundle;
340     sp<AaptAssets> mAssets;
341     sp<AaptFile> mFile;
342     volatile bool* mHasErrors;
343 };
344 
preProcessImages(const Bundle * bundle,const sp<AaptAssets> & assets,const sp<ResourceTypeSet> & set,const char * type)345 static status_t preProcessImages(const Bundle* bundle, const sp<AaptAssets>& assets,
346                           const sp<ResourceTypeSet>& set, const char* type)
347 {
348     volatile bool hasErrors = false;
349     ssize_t res = NO_ERROR;
350     if (bundle->getUseCrunchCache() == false) {
351         WorkQueue wq(MAX_THREADS, false);
352         ResourceDirIterator it(set, String8(type));
353         while ((res=it.next()) == NO_ERROR) {
354             PreProcessImageWorkUnit* w = new PreProcessImageWorkUnit(
355                     bundle, assets, it.getFile(), &hasErrors);
356             status_t status = wq.schedule(w);
357             if (status) {
358                 fprintf(stderr, "preProcessImages failed: schedule() returned %d\n", status);
359                 hasErrors = true;
360                 delete w;
361                 break;
362             }
363         }
364         status_t status = wq.finish();
365         if (status) {
366             fprintf(stderr, "preProcessImages failed: finish() returned %d\n", status);
367             hasErrors = true;
368         }
369     }
370     return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
371 }
372 
collect_files(const sp<AaptDir> & dir,KeyedVector<String8,sp<ResourceTypeSet>> * resources)373 static void collect_files(const sp<AaptDir>& dir,
374         KeyedVector<String8, sp<ResourceTypeSet> >* resources)
375 {
376     const DefaultKeyedVector<String8, sp<AaptGroup> >& groups = dir->getFiles();
377     int N = groups.size();
378     for (int i=0; i<N; i++) {
379         String8 leafName = groups.keyAt(i);
380         const sp<AaptGroup>& group = groups.valueAt(i);
381 
382         const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files
383                 = group->getFiles();
384 
385         if (files.size() == 0) {
386             continue;
387         }
388 
389         String8 resType = files.valueAt(0)->getResourceType();
390 
391         ssize_t index = resources->indexOfKey(resType);
392 
393         if (index < 0) {
394             sp<ResourceTypeSet> set = new ResourceTypeSet();
395             NOISY(printf("Creating new resource type set for leaf %s with group %s (%p)\n",
396                     leafName.string(), group->getPath().string(), group.get()));
397             set->add(leafName, group);
398             resources->add(resType, set);
399         } else {
400             sp<ResourceTypeSet> set = resources->valueAt(index);
401             index = set->indexOfKey(leafName);
402             if (index < 0) {
403                 NOISY(printf("Adding to resource type set for leaf %s group %s (%p)\n",
404                         leafName.string(), group->getPath().string(), group.get()));
405                 set->add(leafName, group);
406             } else {
407                 sp<AaptGroup> existingGroup = set->valueAt(index);
408                 NOISY(printf("Extending to resource type set for leaf %s group %s (%p)\n",
409                         leafName.string(), group->getPath().string(), group.get()));
410                 for (size_t j=0; j<files.size(); j++) {
411                     NOISY(printf("Adding file %s in group %s resType %s\n",
412                         files.valueAt(j)->getSourceFile().string(),
413                         files.keyAt(j).toDirName(String8()).string(),
414                         resType.string()));
415                     status_t err = existingGroup->addFile(files.valueAt(j));
416                 }
417             }
418         }
419     }
420 }
421 
collect_files(const sp<AaptAssets> & ass,KeyedVector<String8,sp<ResourceTypeSet>> * resources)422 static void collect_files(const sp<AaptAssets>& ass,
423         KeyedVector<String8, sp<ResourceTypeSet> >* resources)
424 {
425     const Vector<sp<AaptDir> >& dirs = ass->resDirs();
426     int N = dirs.size();
427 
428     for (int i=0; i<N; i++) {
429         sp<AaptDir> d = dirs.itemAt(i);
430         NOISY(printf("Collecting dir #%d %p: %s, leaf %s\n", i, d.get(), d->getPath().string(),
431                 d->getLeaf().string()));
432         collect_files(d, resources);
433 
434         // don't try to include the res dir
435         NOISY(printf("Removing dir leaf %s\n", d->getLeaf().string()));
436         ass->removeDir(d->getLeaf());
437     }
438 }
439 
440 enum {
441     ATTR_OKAY = -1,
442     ATTR_NOT_FOUND = -2,
443     ATTR_LEADING_SPACES = -3,
444     ATTR_TRAILING_SPACES = -4
445 };
validateAttr(const String8 & path,const ResTable & table,const ResXMLParser & parser,const char * ns,const char * attr,const char * validChars,bool required)446 static int validateAttr(const String8& path, const ResTable& table,
447         const ResXMLParser& parser,
448         const char* ns, const char* attr, const char* validChars, bool required)
449 {
450     size_t len;
451 
452     ssize_t index = parser.indexOfAttribute(ns, attr);
453     const uint16_t* str;
454     Res_value value;
455     if (index >= 0 && parser.getAttributeValue(index, &value) >= 0) {
456         const ResStringPool* pool = &parser.getStrings();
457         if (value.dataType == Res_value::TYPE_REFERENCE) {
458             uint32_t specFlags = 0;
459             int strIdx;
460             if ((strIdx=table.resolveReference(&value, 0x10000000, NULL, &specFlags)) < 0) {
461                 fprintf(stderr, "%s:%d: Tag <%s> attribute %s references unknown resid 0x%08x.\n",
462                         path.string(), parser.getLineNumber(),
463                         String8(parser.getElementName(&len)).string(), attr,
464                         value.data);
465                 return ATTR_NOT_FOUND;
466             }
467 
468             pool = table.getTableStringBlock(strIdx);
469             #if 0
470             if (pool != NULL) {
471                 str = pool->stringAt(value.data, &len);
472             }
473             printf("***** RES ATTR: %s specFlags=0x%x strIdx=%d: %s\n", attr,
474                     specFlags, strIdx, str != NULL ? String8(str).string() : "???");
475             #endif
476             if ((specFlags&~ResTable_typeSpec::SPEC_PUBLIC) != 0 && false) {
477                 fprintf(stderr, "%s:%d: Tag <%s> attribute %s varies by configurations 0x%x.\n",
478                         path.string(), parser.getLineNumber(),
479                         String8(parser.getElementName(&len)).string(), attr,
480                         specFlags);
481                 return ATTR_NOT_FOUND;
482             }
483         }
484         if (value.dataType == Res_value::TYPE_STRING) {
485             if (pool == NULL) {
486                 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has no string block.\n",
487                         path.string(), parser.getLineNumber(),
488                         String8(parser.getElementName(&len)).string(), attr);
489                 return ATTR_NOT_FOUND;
490             }
491             if ((str=pool->stringAt(value.data, &len)) == NULL) {
492                 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has corrupt string value.\n",
493                         path.string(), parser.getLineNumber(),
494                         String8(parser.getElementName(&len)).string(), attr);
495                 return ATTR_NOT_FOUND;
496             }
497         } else {
498             fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid type %d.\n",
499                     path.string(), parser.getLineNumber(),
500                     String8(parser.getElementName(&len)).string(), attr,
501                     value.dataType);
502             return ATTR_NOT_FOUND;
503         }
504         if (validChars) {
505             for (size_t i=0; i<len; i++) {
506                 uint16_t c = str[i];
507                 const char* p = validChars;
508                 bool okay = false;
509                 while (*p) {
510                     if (c == *p) {
511                         okay = true;
512                         break;
513                     }
514                     p++;
515                 }
516                 if (!okay) {
517                     fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid character '%c'.\n",
518                             path.string(), parser.getLineNumber(),
519                             String8(parser.getElementName(&len)).string(), attr, (char)str[i]);
520                     return (int)i;
521                 }
522             }
523         }
524         if (*str == ' ') {
525             fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not start with a space.\n",
526                     path.string(), parser.getLineNumber(),
527                     String8(parser.getElementName(&len)).string(), attr);
528             return ATTR_LEADING_SPACES;
529         }
530         if (str[len-1] == ' ') {
531             fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not end with a space.\n",
532                     path.string(), parser.getLineNumber(),
533                     String8(parser.getElementName(&len)).string(), attr);
534             return ATTR_TRAILING_SPACES;
535         }
536         return ATTR_OKAY;
537     }
538     if (required) {
539         fprintf(stderr, "%s:%d: Tag <%s> missing required attribute %s.\n",
540                 path.string(), parser.getLineNumber(),
541                 String8(parser.getElementName(&len)).string(), attr);
542         return ATTR_NOT_FOUND;
543     }
544     return ATTR_OKAY;
545 }
546 
checkForIds(const String8 & path,ResXMLParser & parser)547 static void checkForIds(const String8& path, ResXMLParser& parser)
548 {
549     ResXMLTree::event_code_t code;
550     while ((code=parser.next()) != ResXMLTree::END_DOCUMENT
551            && code > ResXMLTree::BAD_DOCUMENT) {
552         if (code == ResXMLTree::START_TAG) {
553             ssize_t index = parser.indexOfAttribute(NULL, "id");
554             if (index >= 0) {
555                 fprintf(stderr, "%s:%d: warning: found plain 'id' attribute; did you mean the new 'android:id' name?\n",
556                         path.string(), parser.getLineNumber());
557             }
558         }
559     }
560 }
561 
applyFileOverlay(Bundle * bundle,const sp<AaptAssets> & assets,sp<ResourceTypeSet> * baseSet,const char * resType)562 static bool applyFileOverlay(Bundle *bundle,
563                              const sp<AaptAssets>& assets,
564                              sp<ResourceTypeSet> *baseSet,
565                              const char *resType)
566 {
567     if (bundle->getVerbose()) {
568         printf("applyFileOverlay for %s\n", resType);
569     }
570 
571     // Replace any base level files in this category with any found from the overlay
572     // Also add any found only in the overlay.
573     sp<AaptAssets> overlay = assets->getOverlay();
574     String8 resTypeString(resType);
575 
576     // work through the linked list of overlays
577     while (overlay.get()) {
578         KeyedVector<String8, sp<ResourceTypeSet> >* overlayRes = overlay->getResources();
579 
580         // get the overlay resources of the requested type
581         ssize_t index = overlayRes->indexOfKey(resTypeString);
582         if (index >= 0) {
583             sp<ResourceTypeSet> overlaySet = overlayRes->valueAt(index);
584 
585             // for each of the resources, check for a match in the previously built
586             // non-overlay "baseset".
587             size_t overlayCount = overlaySet->size();
588             for (size_t overlayIndex=0; overlayIndex<overlayCount; overlayIndex++) {
589                 if (bundle->getVerbose()) {
590                     printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string());
591                 }
592                 ssize_t baseIndex = -1;
593                 if (baseSet->get() != NULL) {
594                     baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex));
595                 }
596                 if (baseIndex >= 0) {
597                     // look for same flavor.  For a given file (strings.xml, for example)
598                     // there may be a locale specific or other flavors - we want to match
599                     // the same flavor.
600                     sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
601                     sp<AaptGroup> baseGroup = (*baseSet)->valueAt(baseIndex);
602 
603                     DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
604                             overlayGroup->getFiles();
605                     if (bundle->getVerbose()) {
606                         DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles =
607                                 baseGroup->getFiles();
608                         for (size_t i=0; i < baseFiles.size(); i++) {
609                             printf("baseFile " ZD " has flavor %s\n", (ZD_TYPE) i,
610                                     baseFiles.keyAt(i).toString().string());
611                         }
612                         for (size_t i=0; i < overlayFiles.size(); i++) {
613                             printf("overlayFile " ZD " has flavor %s\n", (ZD_TYPE) i,
614                                     overlayFiles.keyAt(i).toString().string());
615                         }
616                     }
617 
618                     size_t overlayGroupSize = overlayFiles.size();
619                     for (size_t overlayGroupIndex = 0;
620                             overlayGroupIndex<overlayGroupSize;
621                             overlayGroupIndex++) {
622                         ssize_t baseFileIndex =
623                                 baseGroup->getFiles().indexOfKey(overlayFiles.
624                                 keyAt(overlayGroupIndex));
625                         if (baseFileIndex >= 0) {
626                             if (bundle->getVerbose()) {
627                                 printf("found a match (" ZD ") for overlay file %s, for flavor %s\n",
628                                         (ZD_TYPE) baseFileIndex,
629                                         overlayGroup->getLeaf().string(),
630                                         overlayFiles.keyAt(overlayGroupIndex).toString().string());
631                             }
632                             baseGroup->removeFile(baseFileIndex);
633                         } else {
634                             // didn't find a match fall through and add it..
635                             if (true || bundle->getVerbose()) {
636                                 printf("nothing matches overlay file %s, for flavor %s\n",
637                                         overlayGroup->getLeaf().string(),
638                                         overlayFiles.keyAt(overlayGroupIndex).toString().string());
639                             }
640                         }
641                         baseGroup->addFile(overlayFiles.valueAt(overlayGroupIndex));
642                         assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
643                     }
644                 } else {
645                     if (baseSet->get() == NULL) {
646                         *baseSet = new ResourceTypeSet();
647                         assets->getResources()->add(String8(resType), *baseSet);
648                     }
649                     // this group doesn't exist (a file that's only in the overlay)
650                     (*baseSet)->add(overlaySet->keyAt(overlayIndex),
651                             overlaySet->valueAt(overlayIndex));
652                     // make sure all flavors are defined in the resources.
653                     sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
654                     DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
655                             overlayGroup->getFiles();
656                     size_t overlayGroupSize = overlayFiles.size();
657                     for (size_t overlayGroupIndex = 0;
658                             overlayGroupIndex<overlayGroupSize;
659                             overlayGroupIndex++) {
660                         assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
661                     }
662                 }
663             }
664             // this overlay didn't have resources for this type
665         }
666         // try next overlay
667         overlay = overlay->getOverlay();
668     }
669     return true;
670 }
671 
672 /*
673  * Inserts an attribute in a given node.
674  * If errorOnFailedInsert is true, and the attribute already exists, returns false.
675  * If replaceExisting is true, the attribute will be updated if it already exists.
676  * Returns true otherwise, even if the attribute already exists, and does not modify
677  * the existing attribute's value.
678  */
addTagAttribute(const sp<XMLNode> & node,const char * ns8,const char * attr8,const char * value,bool errorOnFailedInsert,bool replaceExisting)679 bool addTagAttribute(const sp<XMLNode>& node, const char* ns8,
680         const char* attr8, const char* value, bool errorOnFailedInsert,
681         bool replaceExisting)
682 {
683     if (value == NULL) {
684         return true;
685     }
686 
687     const String16 ns(ns8);
688     const String16 attr(attr8);
689 
690     XMLNode::attribute_entry* existingEntry = node->editAttribute(ns, attr);
691     if (existingEntry != NULL) {
692         if (replaceExisting) {
693             NOISY(printf("Info: AndroidManifest.xml already defines %s (in %s);"
694                          " overwriting existing value from manifest.\n",
695                          String8(attr).string(), String8(ns).string()));
696             existingEntry->string = String16(value);
697             return true;
698         }
699 
700         if (errorOnFailedInsert) {
701             fprintf(stderr, "Error: AndroidManifest.xml already defines %s (in %s);"
702                             " cannot insert new value %s.\n",
703                     String8(attr).string(), String8(ns).string(), value);
704             return false;
705         }
706 
707         fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s);"
708                         " using existing value in manifest.\n",
709                 String8(attr).string(), String8(ns).string());
710 
711         // don't stop the build.
712         return true;
713     }
714 
715     node->addAttribute(ns, attr, String16(value));
716     return true;
717 }
718 
719 /*
720  * Inserts an attribute in a given node, only if the attribute does not
721  * exist.
722  * If errorOnFailedInsert is true, and the attribute already exists, returns false.
723  * Returns true otherwise, even if the attribute already exists.
724  */
addTagAttribute(const sp<XMLNode> & node,const char * ns8,const char * attr8,const char * value,bool errorOnFailedInsert)725 bool addTagAttribute(const sp<XMLNode>& node, const char* ns8,
726         const char* attr8, const char* value, bool errorOnFailedInsert)
727 {
728     return addTagAttribute(node, ns8, attr8, value, errorOnFailedInsert, false);
729 }
730 
fullyQualifyClassName(const String8 & package,sp<XMLNode> node,const String16 & attrName)731 static void fullyQualifyClassName(const String8& package, sp<XMLNode> node,
732         const String16& attrName) {
733     XMLNode::attribute_entry* attr = node->editAttribute(
734             String16("http://schemas.android.com/apk/res/android"), attrName);
735     if (attr != NULL) {
736         String8 name(attr->string);
737 
738         // asdf     --> package.asdf
739         // .asdf  .a.b  --> package.asdf package.a.b
740         // asdf.adsf --> asdf.asdf
741         String8 className;
742         const char* p = name.string();
743         const char* q = strchr(p, '.');
744         if (p == q) {
745             className += package;
746             className += name;
747         } else if (q == NULL) {
748             className += package;
749             className += ".";
750             className += name;
751         } else {
752             className += name;
753         }
754         NOISY(printf("Qualifying class '%s' to '%s'", name.string(), className.string()));
755         attr->string.setTo(String16(className));
756     }
757 }
758 
massageManifest(Bundle * bundle,sp<XMLNode> root)759 status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
760 {
761     root = root->searchElement(String16(), String16("manifest"));
762     if (root == NULL) {
763         fprintf(stderr, "No <manifest> tag.\n");
764         return UNKNOWN_ERROR;
765     }
766 
767     bool errorOnFailedInsert = bundle->getErrorOnFailedInsert();
768     bool replaceVersion = bundle->getReplaceVersion();
769 
770     if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode",
771             bundle->getVersionCode(), errorOnFailedInsert, replaceVersion)) {
772         return UNKNOWN_ERROR;
773     } else {
774         const XMLNode::attribute_entry* attr = root->getAttribute(
775                 String16(RESOURCES_ANDROID_NAMESPACE), String16("versionCode"));
776         if (attr != NULL) {
777             bundle->setVersionCode(strdup(String8(attr->string).string()));
778         }
779     }
780 
781     if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName",
782             bundle->getVersionName(), errorOnFailedInsert, replaceVersion)) {
783         return UNKNOWN_ERROR;
784     } else {
785         const XMLNode::attribute_entry* attr = root->getAttribute(
786                 String16(RESOURCES_ANDROID_NAMESPACE), String16("versionName"));
787         if (attr != NULL) {
788             bundle->setVersionName(strdup(String8(attr->string).string()));
789         }
790     }
791 
792     sp<XMLNode> vers = root->getChildElement(String16(), String16("uses-sdk"));
793     if (bundle->getMinSdkVersion() != NULL
794             || bundle->getTargetSdkVersion() != NULL
795             || bundle->getMaxSdkVersion() != NULL) {
796         if (vers == NULL) {
797             vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk"));
798             root->insertChildAt(vers, 0);
799         }
800 
801         if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion",
802                 bundle->getMinSdkVersion(), errorOnFailedInsert)) {
803             return UNKNOWN_ERROR;
804         }
805         if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion",
806                 bundle->getTargetSdkVersion(), errorOnFailedInsert)) {
807             return UNKNOWN_ERROR;
808         }
809         if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion",
810                 bundle->getMaxSdkVersion(), errorOnFailedInsert)) {
811             return UNKNOWN_ERROR;
812         }
813     }
814 
815     if (vers != NULL) {
816         const XMLNode::attribute_entry* attr = vers->getAttribute(
817                 String16(RESOURCES_ANDROID_NAMESPACE), String16("minSdkVersion"));
818         if (attr != NULL) {
819             bundle->setMinSdkVersion(strdup(String8(attr->string).string()));
820         }
821     }
822 
823     if (bundle->getPlatformBuildVersionCode() != "") {
824         if (!addTagAttribute(root, "", "platformBuildVersionCode",
825                     bundle->getPlatformBuildVersionCode(), errorOnFailedInsert, true)) {
826             return UNKNOWN_ERROR;
827         }
828     }
829 
830     if (bundle->getPlatformBuildVersionName() != "") {
831         if (!addTagAttribute(root, "", "platformBuildVersionName",
832                     bundle->getPlatformBuildVersionName(), errorOnFailedInsert, true)) {
833             return UNKNOWN_ERROR;
834         }
835     }
836 
837     if (bundle->getDebugMode()) {
838         sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
839         if (application != NULL) {
840             if (!addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true",
841                     errorOnFailedInsert)) {
842                 return UNKNOWN_ERROR;
843             }
844         }
845     }
846 
847     // Deal with manifest package name overrides
848     const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride();
849     if (manifestPackageNameOverride != NULL) {
850         // Update the actual package name
851         XMLNode::attribute_entry* attr = root->editAttribute(String16(), String16("package"));
852         if (attr == NULL) {
853             fprintf(stderr, "package name is required with --rename-manifest-package.\n");
854             return UNKNOWN_ERROR;
855         }
856         String8 origPackage(attr->string);
857         attr->string.setTo(String16(manifestPackageNameOverride));
858         NOISY(printf("Overriding package '%s' to be '%s'\n", origPackage.string(), manifestPackageNameOverride));
859 
860         // Make class names fully qualified
861         sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
862         if (application != NULL) {
863             fullyQualifyClassName(origPackage, application, String16("name"));
864             fullyQualifyClassName(origPackage, application, String16("backupAgent"));
865 
866             Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(application->getChildren());
867             for (size_t i = 0; i < children.size(); i++) {
868                 sp<XMLNode> child = children.editItemAt(i);
869                 String8 tag(child->getElementName());
870                 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
871                     fullyQualifyClassName(origPackage, child, String16("name"));
872                 } else if (tag == "activity-alias") {
873                     fullyQualifyClassName(origPackage, child, String16("name"));
874                     fullyQualifyClassName(origPackage, child, String16("targetActivity"));
875                 }
876             }
877         }
878     }
879 
880     // Deal with manifest package name overrides
881     const char* instrumentationPackageNameOverride = bundle->getInstrumentationPackageNameOverride();
882     if (instrumentationPackageNameOverride != NULL) {
883         // Fix up instrumentation targets.
884         Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(root->getChildren());
885         for (size_t i = 0; i < children.size(); i++) {
886             sp<XMLNode> child = children.editItemAt(i);
887             String8 tag(child->getElementName());
888             if (tag == "instrumentation") {
889                 XMLNode::attribute_entry* attr = child->editAttribute(
890                         String16("http://schemas.android.com/apk/res/android"), String16("targetPackage"));
891                 if (attr != NULL) {
892                     attr->string.setTo(String16(instrumentationPackageNameOverride));
893                 }
894             }
895         }
896     }
897 
898     // Generate split name if feature is present.
899     const XMLNode::attribute_entry* attr = root->getAttribute(String16(), String16("featureName"));
900     if (attr != NULL) {
901         String16 splitName("feature_");
902         splitName.append(attr->string);
903         status_t err = root->addAttribute(String16(), String16("split"), splitName);
904         if (err != NO_ERROR) {
905             ALOGE("Failed to insert split name into AndroidManifest.xml");
906             return err;
907         }
908     }
909 
910     return NO_ERROR;
911 }
912 
getPlatformAssetCookie(const AssetManager & assets)913 static int32_t getPlatformAssetCookie(const AssetManager& assets) {
914     // Find the system package (0x01). AAPT always generates attributes
915     // with the type 0x01, so we're looking for the first attribute
916     // resource in the system package.
917     const ResTable& table = assets.getResources(true);
918     Res_value val;
919     ssize_t idx = table.getResource(0x01010000, &val, true);
920     if (idx != NO_ERROR) {
921         // Try as a bag.
922         const ResTable::bag_entry* entry;
923         ssize_t cnt = table.lockBag(0x01010000, &entry);
924         if (cnt >= 0) {
925             idx = entry->stringBlock;
926         }
927         table.unlockBag(entry);
928     }
929 
930     if (idx < 0) {
931         return 0;
932     }
933     return table.getTableCookie(idx);
934 }
935 
936 enum {
937     VERSION_CODE_ATTR = 0x0101021b,
938     VERSION_NAME_ATTR = 0x0101021c,
939 };
940 
extractPlatformBuildVersion(ResXMLTree & tree,Bundle * bundle)941 static ssize_t extractPlatformBuildVersion(ResXMLTree& tree, Bundle* bundle) {
942     size_t len;
943     ResXMLTree::event_code_t code;
944     while ((code = tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
945         if (code != ResXMLTree::START_TAG) {
946             continue;
947         }
948 
949         const char16_t* ctag16 = tree.getElementName(&len);
950         if (ctag16 == NULL) {
951             fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
952             return UNKNOWN_ERROR;
953         }
954 
955         String8 tag(ctag16, len);
956         if (tag != "manifest") {
957             continue;
958         }
959 
960         String8 error;
961         int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
962         if (error != "") {
963             fprintf(stderr, "ERROR: failed to get platform version code\n");
964             return UNKNOWN_ERROR;
965         }
966 
967         if (versionCode >= 0 && bundle->getPlatformBuildVersionCode() == "") {
968             bundle->setPlatformBuildVersionCode(String8::format("%d", versionCode));
969         }
970 
971         String8 versionName = AaptXml::getAttribute(tree, VERSION_NAME_ATTR, &error);
972         if (error != "") {
973             fprintf(stderr, "ERROR: failed to get platform version name\n");
974             return UNKNOWN_ERROR;
975         }
976 
977         if (versionName != "" && bundle->getPlatformBuildVersionName() == "") {
978             bundle->setPlatformBuildVersionName(versionName);
979         }
980         return NO_ERROR;
981     }
982 
983     fprintf(stderr, "ERROR: no <manifest> tag found in platform AndroidManifest.xml\n");
984     return UNKNOWN_ERROR;
985 }
986 
extractPlatformBuildVersion(AssetManager & assets,Bundle * bundle)987 static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) {
988     int32_t cookie = getPlatformAssetCookie(assets);
989     if (cookie == 0) {
990         // No platform was loaded.
991         return NO_ERROR;
992     }
993 
994     ResXMLTree tree;
995     Asset* asset = assets.openNonAsset(cookie, "AndroidManifest.xml", Asset::ACCESS_STREAMING);
996     if (asset == NULL) {
997         fprintf(stderr, "ERROR: Platform AndroidManifest.xml not found\n");
998         return UNKNOWN_ERROR;
999     }
1000 
1001     ssize_t result = NO_ERROR;
1002     if (tree.setTo(asset->getBuffer(true), asset->getLength()) != NO_ERROR) {
1003         fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n");
1004         result = UNKNOWN_ERROR;
1005     } else {
1006         result = extractPlatformBuildVersion(tree, bundle);
1007     }
1008 
1009     delete asset;
1010     return result;
1011 }
1012 
1013 #define ASSIGN_IT(n) \
1014         do { \
1015             ssize_t index = resources->indexOfKey(String8(#n)); \
1016             if (index >= 0) { \
1017                 n ## s = resources->valueAt(index); \
1018             } \
1019         } while (0)
1020 
updatePreProcessedCache(Bundle * bundle)1021 status_t updatePreProcessedCache(Bundle* bundle)
1022 {
1023     #if BENCHMARK
1024     fprintf(stdout, "BENCHMARK: Starting PNG PreProcessing \n");
1025     long startPNGTime = clock();
1026     #endif /* BENCHMARK */
1027 
1028     String8 source(bundle->getResourceSourceDirs()[0]);
1029     String8 dest(bundle->getCrunchedOutputDir());
1030 
1031     FileFinder* ff = new SystemFileFinder();
1032     CrunchCache cc(source,dest,ff);
1033 
1034     CacheUpdater* cu = new SystemCacheUpdater(bundle);
1035     size_t numFiles = cc.crunch(cu);
1036 
1037     if (bundle->getVerbose())
1038         fprintf(stdout, "Crunched %d PNG files to update cache\n", (int)numFiles);
1039 
1040     delete ff;
1041     delete cu;
1042 
1043     #if BENCHMARK
1044     fprintf(stdout, "BENCHMARK: End PNG PreProcessing. Time Elapsed: %f ms \n"
1045             ,(clock() - startPNGTime)/1000.0);
1046     #endif /* BENCHMARK */
1047     return 0;
1048 }
1049 
generateAndroidManifestForSplit(Bundle * bundle,const sp<AaptAssets> & assets,const sp<ApkSplit> & split,sp<AaptFile> & outFile,ResourceTable * table)1050 status_t generateAndroidManifestForSplit(Bundle* bundle, const sp<AaptAssets>& assets,
1051         const sp<ApkSplit>& split, sp<AaptFile>& outFile, ResourceTable* table) {
1052     const String8 filename("AndroidManifest.xml");
1053     const String16 androidPrefix("android");
1054     const String16 androidNSUri("http://schemas.android.com/apk/res/android");
1055     sp<XMLNode> root = XMLNode::newNamespace(filename, androidPrefix, androidNSUri);
1056 
1057     // Build the <manifest> tag
1058     sp<XMLNode> manifest = XMLNode::newElement(filename, String16(), String16("manifest"));
1059 
1060     // Add the 'package' attribute which is set to the package name.
1061     const char* packageName = assets->getPackage();
1062     const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride();
1063     if (manifestPackageNameOverride != NULL) {
1064         packageName = manifestPackageNameOverride;
1065     }
1066     manifest->addAttribute(String16(), String16("package"), String16(packageName));
1067 
1068     // Add the 'versionCode' attribute which is set to the original version code.
1069     if (!addTagAttribute(manifest, RESOURCES_ANDROID_NAMESPACE, "versionCode",
1070             bundle->getVersionCode(), true, true)) {
1071         return UNKNOWN_ERROR;
1072     }
1073 
1074     // Add the 'split' attribute which describes the configurations included.
1075     String8 splitName("config.");
1076     splitName.append(split->getPackageSafeName());
1077     manifest->addAttribute(String16(), String16("split"), String16(splitName));
1078 
1079     // Build an empty <application> tag (required).
1080     sp<XMLNode> app = XMLNode::newElement(filename, String16(), String16("application"));
1081 
1082     // Add the 'hasCode' attribute which is never true for resource splits.
1083     if (!addTagAttribute(app, RESOURCES_ANDROID_NAMESPACE, "hasCode",
1084             "false", true, true)) {
1085         return UNKNOWN_ERROR;
1086     }
1087 
1088     manifest->addChild(app);
1089     root->addChild(manifest);
1090 
1091     int err = compileXmlFile(bundle, assets, String16(), root, outFile, table);
1092     if (err < NO_ERROR) {
1093         return err;
1094     }
1095     outFile->setCompressionMethod(ZipEntry::kCompressDeflated);
1096     return NO_ERROR;
1097 }
1098 
buildResources(Bundle * bundle,const sp<AaptAssets> & assets,sp<ApkBuilder> & builder)1099 status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder)
1100 {
1101     // First, look for a package file to parse.  This is required to
1102     // be able to generate the resource information.
1103     sp<AaptGroup> androidManifestFile =
1104             assets->getFiles().valueFor(String8("AndroidManifest.xml"));
1105     if (androidManifestFile == NULL) {
1106         fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n");
1107         return UNKNOWN_ERROR;
1108     }
1109 
1110     status_t err = parsePackage(bundle, assets, androidManifestFile);
1111     if (err != NO_ERROR) {
1112         return err;
1113     }
1114 
1115     NOISY(printf("Creating resources for package %s\n",
1116                  assets->getPackage().string()));
1117 
1118     ResourceTable::PackageType packageType = ResourceTable::App;
1119     if (bundle->getBuildSharedLibrary()) {
1120         packageType = ResourceTable::SharedLibrary;
1121     } else if (bundle->getExtending()) {
1122         packageType = ResourceTable::System;
1123     } else if (!bundle->getFeatureOfPackage().isEmpty()) {
1124         packageType = ResourceTable::AppFeature;
1125     }
1126 
1127     ResourceTable table(bundle, String16(assets->getPackage()), packageType);
1128     err = table.addIncludedResources(bundle, assets);
1129     if (err != NO_ERROR) {
1130         return err;
1131     }
1132 
1133     NOISY(printf("Found %d included resource packages\n", (int)table.size()));
1134 
1135     // Standard flags for compiled XML and optional UTF-8 encoding
1136     int xmlFlags = XML_COMPILE_STANDARD_RESOURCE;
1137 
1138     /* Only enable UTF-8 if the caller of aapt didn't specifically
1139      * request UTF-16 encoding and the parameters of this package
1140      * allow UTF-8 to be used.
1141      */
1142     if (!bundle->getUTF16StringsOption()) {
1143         xmlFlags |= XML_COMPILE_UTF8;
1144     }
1145 
1146     // --------------------------------------------------------------
1147     // First, gather all resource information.
1148     // --------------------------------------------------------------
1149 
1150     // resType -> leafName -> group
1151     KeyedVector<String8, sp<ResourceTypeSet> > *resources =
1152             new KeyedVector<String8, sp<ResourceTypeSet> >;
1153     collect_files(assets, resources);
1154 
1155     sp<ResourceTypeSet> drawables;
1156     sp<ResourceTypeSet> layouts;
1157     sp<ResourceTypeSet> anims;
1158     sp<ResourceTypeSet> animators;
1159     sp<ResourceTypeSet> interpolators;
1160     sp<ResourceTypeSet> transitions;
1161     sp<ResourceTypeSet> xmls;
1162     sp<ResourceTypeSet> raws;
1163     sp<ResourceTypeSet> colors;
1164     sp<ResourceTypeSet> menus;
1165     sp<ResourceTypeSet> mipmaps;
1166 
1167     ASSIGN_IT(drawable);
1168     ASSIGN_IT(layout);
1169     ASSIGN_IT(anim);
1170     ASSIGN_IT(animator);
1171     ASSIGN_IT(interpolator);
1172     ASSIGN_IT(transition);
1173     ASSIGN_IT(xml);
1174     ASSIGN_IT(raw);
1175     ASSIGN_IT(color);
1176     ASSIGN_IT(menu);
1177     ASSIGN_IT(mipmap);
1178 
1179     assets->setResources(resources);
1180     // now go through any resource overlays and collect their files
1181     sp<AaptAssets> current = assets->getOverlay();
1182     while(current.get()) {
1183         KeyedVector<String8, sp<ResourceTypeSet> > *resources =
1184                 new KeyedVector<String8, sp<ResourceTypeSet> >;
1185         current->setResources(resources);
1186         collect_files(current, resources);
1187         current = current->getOverlay();
1188     }
1189     // apply the overlay files to the base set
1190     if (!applyFileOverlay(bundle, assets, &drawables, "drawable") ||
1191             !applyFileOverlay(bundle, assets, &layouts, "layout") ||
1192             !applyFileOverlay(bundle, assets, &anims, "anim") ||
1193             !applyFileOverlay(bundle, assets, &animators, "animator") ||
1194             !applyFileOverlay(bundle, assets, &interpolators, "interpolator") ||
1195             !applyFileOverlay(bundle, assets, &transitions, "transition") ||
1196             !applyFileOverlay(bundle, assets, &xmls, "xml") ||
1197             !applyFileOverlay(bundle, assets, &raws, "raw") ||
1198             !applyFileOverlay(bundle, assets, &colors, "color") ||
1199             !applyFileOverlay(bundle, assets, &menus, "menu") ||
1200             !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) {
1201         return UNKNOWN_ERROR;
1202     }
1203 
1204     bool hasErrors = false;
1205 
1206     if (drawables != NULL) {
1207         if (bundle->getOutputAPKFile() != NULL) {
1208             err = preProcessImages(bundle, assets, drawables, "drawable");
1209         }
1210         if (err == NO_ERROR) {
1211             err = makeFileResources(bundle, assets, &table, drawables, "drawable");
1212             if (err != NO_ERROR) {
1213                 hasErrors = true;
1214             }
1215         } else {
1216             hasErrors = true;
1217         }
1218     }
1219 
1220     if (mipmaps != NULL) {
1221         if (bundle->getOutputAPKFile() != NULL) {
1222             err = preProcessImages(bundle, assets, mipmaps, "mipmap");
1223         }
1224         if (err == NO_ERROR) {
1225             err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap");
1226             if (err != NO_ERROR) {
1227                 hasErrors = true;
1228             }
1229         } else {
1230             hasErrors = true;
1231         }
1232     }
1233 
1234     if (layouts != NULL) {
1235         err = makeFileResources(bundle, assets, &table, layouts, "layout");
1236         if (err != NO_ERROR) {
1237             hasErrors = true;
1238         }
1239     }
1240 
1241     if (anims != NULL) {
1242         err = makeFileResources(bundle, assets, &table, anims, "anim");
1243         if (err != NO_ERROR) {
1244             hasErrors = true;
1245         }
1246     }
1247 
1248     if (animators != NULL) {
1249         err = makeFileResources(bundle, assets, &table, animators, "animator");
1250         if (err != NO_ERROR) {
1251             hasErrors = true;
1252         }
1253     }
1254 
1255     if (transitions != NULL) {
1256         err = makeFileResources(bundle, assets, &table, transitions, "transition");
1257         if (err != NO_ERROR) {
1258             hasErrors = true;
1259         }
1260     }
1261 
1262     if (interpolators != NULL) {
1263         err = makeFileResources(bundle, assets, &table, interpolators, "interpolator");
1264         if (err != NO_ERROR) {
1265             hasErrors = true;
1266         }
1267     }
1268 
1269     if (xmls != NULL) {
1270         err = makeFileResources(bundle, assets, &table, xmls, "xml");
1271         if (err != NO_ERROR) {
1272             hasErrors = true;
1273         }
1274     }
1275 
1276     if (raws != NULL) {
1277         err = makeFileResources(bundle, assets, &table, raws, "raw");
1278         if (err != NO_ERROR) {
1279             hasErrors = true;
1280         }
1281     }
1282 
1283     // compile resources
1284     current = assets;
1285     while(current.get()) {
1286         KeyedVector<String8, sp<ResourceTypeSet> > *resources =
1287                 current->getResources();
1288 
1289         ssize_t index = resources->indexOfKey(String8("values"));
1290         if (index >= 0) {
1291             ResourceDirIterator it(resources->valueAt(index), String8("values"));
1292             ssize_t res;
1293             while ((res=it.next()) == NO_ERROR) {
1294                 sp<AaptFile> file = it.getFile();
1295                 res = compileResourceFile(bundle, assets, file, it.getParams(),
1296                                           (current!=assets), &table);
1297                 if (res != NO_ERROR) {
1298                     hasErrors = true;
1299                 }
1300             }
1301         }
1302         current = current->getOverlay();
1303     }
1304 
1305     if (colors != NULL) {
1306         err = makeFileResources(bundle, assets, &table, colors, "color");
1307         if (err != NO_ERROR) {
1308             hasErrors = true;
1309         }
1310     }
1311 
1312     if (menus != NULL) {
1313         err = makeFileResources(bundle, assets, &table, menus, "menu");
1314         if (err != NO_ERROR) {
1315             hasErrors = true;
1316         }
1317     }
1318 
1319     // --------------------------------------------------------------------
1320     // Assignment of resource IDs and initial generation of resource table.
1321     // --------------------------------------------------------------------
1322 
1323     if (table.hasResources()) {
1324         err = table.assignResourceIds();
1325         if (err < NO_ERROR) {
1326             return err;
1327         }
1328     }
1329 
1330     // --------------------------------------------------------------
1331     // Finally, we can now we can compile XML files, which may reference
1332     // resources.
1333     // --------------------------------------------------------------
1334 
1335     if (layouts != NULL) {
1336         ResourceDirIterator it(layouts, String8("layout"));
1337         while ((err=it.next()) == NO_ERROR) {
1338             String8 src = it.getFile()->getPrintableSource();
1339             err = compileXmlFile(bundle, assets, String16(it.getBaseName()),
1340                     it.getFile(), &table, xmlFlags);
1341             if (err == NO_ERROR) {
1342                 ResXMLTree block;
1343                 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
1344                 checkForIds(src, block);
1345             } else {
1346                 hasErrors = true;
1347             }
1348         }
1349 
1350         if (err < NO_ERROR) {
1351             hasErrors = true;
1352         }
1353         err = NO_ERROR;
1354     }
1355 
1356     if (anims != NULL) {
1357         ResourceDirIterator it(anims, String8("anim"));
1358         while ((err=it.next()) == NO_ERROR) {
1359             err = compileXmlFile(bundle, assets, String16(it.getBaseName()),
1360                     it.getFile(), &table, xmlFlags);
1361             if (err != NO_ERROR) {
1362                 hasErrors = true;
1363             }
1364         }
1365 
1366         if (err < NO_ERROR) {
1367             hasErrors = true;
1368         }
1369         err = NO_ERROR;
1370     }
1371 
1372     if (animators != NULL) {
1373         ResourceDirIterator it(animators, String8("animator"));
1374         while ((err=it.next()) == NO_ERROR) {
1375             err = compileXmlFile(bundle, assets, String16(it.getBaseName()),
1376                     it.getFile(), &table, xmlFlags);
1377             if (err != NO_ERROR) {
1378                 hasErrors = true;
1379             }
1380         }
1381 
1382         if (err < NO_ERROR) {
1383             hasErrors = true;
1384         }
1385         err = NO_ERROR;
1386     }
1387 
1388     if (interpolators != NULL) {
1389         ResourceDirIterator it(interpolators, String8("interpolator"));
1390         while ((err=it.next()) == NO_ERROR) {
1391             err = compileXmlFile(bundle, assets, String16(it.getBaseName()),
1392                     it.getFile(), &table, xmlFlags);
1393             if (err != NO_ERROR) {
1394                 hasErrors = true;
1395             }
1396         }
1397 
1398         if (err < NO_ERROR) {
1399             hasErrors = true;
1400         }
1401         err = NO_ERROR;
1402     }
1403 
1404     if (transitions != NULL) {
1405         ResourceDirIterator it(transitions, String8("transition"));
1406         while ((err=it.next()) == NO_ERROR) {
1407             err = compileXmlFile(bundle, assets, String16(it.getBaseName()),
1408                     it.getFile(), &table, xmlFlags);
1409             if (err != NO_ERROR) {
1410                 hasErrors = true;
1411             }
1412         }
1413 
1414         if (err < NO_ERROR) {
1415             hasErrors = true;
1416         }
1417         err = NO_ERROR;
1418     }
1419 
1420     if (xmls != NULL) {
1421         ResourceDirIterator it(xmls, String8("xml"));
1422         while ((err=it.next()) == NO_ERROR) {
1423             err = compileXmlFile(bundle, assets, String16(it.getBaseName()),
1424                     it.getFile(), &table, xmlFlags);
1425             if (err != NO_ERROR) {
1426                 hasErrors = true;
1427             }
1428         }
1429 
1430         if (err < NO_ERROR) {
1431             hasErrors = true;
1432         }
1433         err = NO_ERROR;
1434     }
1435 
1436     if (drawables != NULL) {
1437         ResourceDirIterator it(drawables, String8("drawable"));
1438         while ((err=it.next()) == NO_ERROR) {
1439             err = postProcessImage(bundle, assets, &table, it.getFile());
1440             if (err != NO_ERROR) {
1441                 hasErrors = true;
1442             }
1443         }
1444 
1445         if (err < NO_ERROR) {
1446             hasErrors = true;
1447         }
1448         err = NO_ERROR;
1449     }
1450 
1451     if (colors != NULL) {
1452         ResourceDirIterator it(colors, String8("color"));
1453         while ((err=it.next()) == NO_ERROR) {
1454             err = compileXmlFile(bundle, assets, String16(it.getBaseName()),
1455                     it.getFile(), &table, xmlFlags);
1456             if (err != NO_ERROR) {
1457                 hasErrors = true;
1458             }
1459         }
1460 
1461         if (err < NO_ERROR) {
1462             hasErrors = true;
1463         }
1464         err = NO_ERROR;
1465     }
1466 
1467     if (menus != NULL) {
1468         ResourceDirIterator it(menus, String8("menu"));
1469         while ((err=it.next()) == NO_ERROR) {
1470             String8 src = it.getFile()->getPrintableSource();
1471             err = compileXmlFile(bundle, assets, String16(it.getBaseName()),
1472                     it.getFile(), &table, xmlFlags);
1473             if (err == NO_ERROR) {
1474                 ResXMLTree block;
1475                 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
1476                 checkForIds(src, block);
1477             } else {
1478                 hasErrors = true;
1479             }
1480         }
1481 
1482         if (err < NO_ERROR) {
1483             hasErrors = true;
1484         }
1485         err = NO_ERROR;
1486     }
1487 
1488     // Now compile any generated resources.
1489     std::queue<CompileResourceWorkItem>& workQueue = table.getWorkQueue();
1490     while (!workQueue.empty()) {
1491         CompileResourceWorkItem& workItem = workQueue.front();
1492         err = compileXmlFile(bundle, assets, workItem.resourceName, workItem.file, &table, xmlFlags);
1493         if (err == NO_ERROR) {
1494             assets->addResource(workItem.resPath.getPathLeaf(),
1495                     workItem.resPath,
1496                     workItem.file,
1497                     workItem.file->getResourceType());
1498         } else {
1499             hasErrors = true;
1500         }
1501         workQueue.pop();
1502     }
1503 
1504     if (table.validateLocalizations()) {
1505         hasErrors = true;
1506     }
1507 
1508     if (hasErrors) {
1509         return UNKNOWN_ERROR;
1510     }
1511 
1512     // If we're not overriding the platform build versions,
1513     // extract them from the platform APK.
1514     if (packageType != ResourceTable::System &&
1515             (bundle->getPlatformBuildVersionCode() == "" ||
1516             bundle->getPlatformBuildVersionName() == "")) {
1517         err = extractPlatformBuildVersion(assets->getAssetManager(), bundle);
1518         if (err != NO_ERROR) {
1519             return UNKNOWN_ERROR;
1520         }
1521     }
1522 
1523     const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0));
1524     String8 manifestPath(manifestFile->getPrintableSource());
1525 
1526     // Generate final compiled manifest file.
1527     manifestFile->clearData();
1528     sp<XMLNode> manifestTree = XMLNode::parse(manifestFile);
1529     if (manifestTree == NULL) {
1530         return UNKNOWN_ERROR;
1531     }
1532     err = massageManifest(bundle, manifestTree);
1533     if (err < NO_ERROR) {
1534         return err;
1535     }
1536     err = compileXmlFile(bundle, assets, String16(), manifestTree, manifestFile, &table);
1537     if (err < NO_ERROR) {
1538         return err;
1539     }
1540 
1541     if (table.modifyForCompat(bundle) != NO_ERROR) {
1542         return UNKNOWN_ERROR;
1543     }
1544 
1545     //block.restart();
1546     //printXMLBlock(&block);
1547 
1548     // --------------------------------------------------------------
1549     // Generate the final resource table.
1550     // Re-flatten because we may have added new resource IDs
1551     // --------------------------------------------------------------
1552 
1553     ResTable finalResTable;
1554     sp<AaptFile> resFile;
1555 
1556     if (table.hasResources()) {
1557         sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
1558         err = table.addSymbols(symbols);
1559         if (err < NO_ERROR) {
1560             return err;
1561         }
1562 
1563         Vector<sp<ApkSplit> >& splits = builder->getSplits();
1564         const size_t numSplits = splits.size();
1565         for (size_t i = 0; i < numSplits; i++) {
1566             sp<ApkSplit>& split = splits.editItemAt(i);
1567             sp<AaptFile> flattenedTable = new AaptFile(String8("resources.arsc"),
1568                     AaptGroupEntry(), String8());
1569             err = table.flatten(bundle, split->getResourceFilter(),
1570                     flattenedTable, split->isBase());
1571             if (err != NO_ERROR) {
1572                 fprintf(stderr, "Failed to generate resource table for split '%s'\n",
1573                         split->getPrintableName().string());
1574                 return err;
1575             }
1576             split->addEntry(String8("resources.arsc"), flattenedTable);
1577 
1578             if (split->isBase()) {
1579                 resFile = flattenedTable;
1580                 err = finalResTable.add(flattenedTable->getData(), flattenedTable->getSize());
1581                 if (err != NO_ERROR) {
1582                     fprintf(stderr, "Generated resource table is corrupt.\n");
1583                     return err;
1584                 }
1585             } else {
1586                 sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"),
1587                         AaptGroupEntry(), String8());
1588                 err = generateAndroidManifestForSplit(bundle, assets, split,
1589                         generatedManifest, &table);
1590                 if (err != NO_ERROR) {
1591                     fprintf(stderr, "Failed to generate AndroidManifest.xml for split '%s'\n",
1592                             split->getPrintableName().string());
1593                     return err;
1594                 }
1595                 split->addEntry(String8("AndroidManifest.xml"), generatedManifest);
1596             }
1597         }
1598 
1599         if (bundle->getPublicOutputFile()) {
1600             FILE* fp = fopen(bundle->getPublicOutputFile(), "w+");
1601             if (fp == NULL) {
1602                 fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n",
1603                         (const char*)bundle->getPublicOutputFile(), strerror(errno));
1604                 return UNKNOWN_ERROR;
1605             }
1606             if (bundle->getVerbose()) {
1607                 printf("  Writing public definitions to %s.\n", bundle->getPublicOutputFile());
1608             }
1609             table.writePublicDefinitions(String16(assets->getPackage()), fp);
1610             fclose(fp);
1611         }
1612 
1613         if (finalResTable.getTableCount() == 0 || resFile == NULL) {
1614             fprintf(stderr, "No resource table was generated.\n");
1615             return UNKNOWN_ERROR;
1616         }
1617     }
1618 
1619     // Perform a basic validation of the manifest file.  This time we
1620     // parse it with the comments intact, so that we can use them to
1621     // generate java docs...  so we are not going to write this one
1622     // back out to the final manifest data.
1623     sp<AaptFile> outManifestFile = new AaptFile(manifestFile->getSourceFile(),
1624             manifestFile->getGroupEntry(),
1625             manifestFile->getResourceType());
1626     err = compileXmlFile(bundle, assets, String16(), manifestFile,
1627             outManifestFile, &table,
1628             XML_COMPILE_ASSIGN_ATTRIBUTE_IDS
1629             | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES);
1630     if (err < NO_ERROR) {
1631         return err;
1632     }
1633     ResXMLTree block;
1634     block.setTo(outManifestFile->getData(), outManifestFile->getSize(), true);
1635     String16 manifest16("manifest");
1636     String16 permission16("permission");
1637     String16 permission_group16("permission-group");
1638     String16 uses_permission16("uses-permission");
1639     String16 instrumentation16("instrumentation");
1640     String16 application16("application");
1641     String16 provider16("provider");
1642     String16 service16("service");
1643     String16 receiver16("receiver");
1644     String16 activity16("activity");
1645     String16 action16("action");
1646     String16 category16("category");
1647     String16 data16("scheme");
1648     String16 feature_group16("feature-group");
1649     String16 uses_feature16("uses-feature");
1650     const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz"
1651         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789";
1652     const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz"
1653         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-";
1654     const char* classIdentChars = "abcdefghijklmnopqrstuvwxyz"
1655         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789$";
1656     const char* processIdentChars = "abcdefghijklmnopqrstuvwxyz"
1657         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:";
1658     const char* authoritiesIdentChars = "abcdefghijklmnopqrstuvwxyz"
1659         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-:;";
1660     const char* typeIdentChars = "abcdefghijklmnopqrstuvwxyz"
1661         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:-/*+";
1662     const char* schemeIdentChars = "abcdefghijklmnopqrstuvwxyz"
1663         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-";
1664     ResXMLTree::event_code_t code;
1665     sp<AaptSymbols> permissionSymbols;
1666     sp<AaptSymbols> permissionGroupSymbols;
1667     while ((code=block.next()) != ResXMLTree::END_DOCUMENT
1668            && code > ResXMLTree::BAD_DOCUMENT) {
1669         if (code == ResXMLTree::START_TAG) {
1670             size_t len;
1671             if (block.getElementNamespace(&len) != NULL) {
1672                 continue;
1673             }
1674             if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) {
1675                 if (validateAttr(manifestPath, finalResTable, block, NULL, "package",
1676                                  packageIdentChars, true) != ATTR_OKAY) {
1677                     hasErrors = true;
1678                 }
1679                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1680                                  "sharedUserId", packageIdentChars, false) != ATTR_OKAY) {
1681                     hasErrors = true;
1682                 }
1683             } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0
1684                     || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) {
1685                 const bool isGroup = strcmp16(block.getElementName(&len),
1686                         permission_group16.string()) == 0;
1687                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1688                                  "name", isGroup ? packageIdentCharsWithTheStupid
1689                                  : packageIdentChars, true) != ATTR_OKAY) {
1690                     hasErrors = true;
1691                 }
1692                 SourcePos srcPos(manifestPath, block.getLineNumber());
1693                 sp<AaptSymbols> syms;
1694                 if (!isGroup) {
1695                     syms = permissionSymbols;
1696                     if (syms == NULL) {
1697                         sp<AaptSymbols> symbols =
1698                                 assets->getSymbolsFor(String8("Manifest"));
1699                         syms = permissionSymbols = symbols->addNestedSymbol(
1700                                 String8("permission"), srcPos);
1701                     }
1702                 } else {
1703                     syms = permissionGroupSymbols;
1704                     if (syms == NULL) {
1705                         sp<AaptSymbols> symbols =
1706                                 assets->getSymbolsFor(String8("Manifest"));
1707                         syms = permissionGroupSymbols = symbols->addNestedSymbol(
1708                                 String8("permission_group"), srcPos);
1709                     }
1710                 }
1711                 size_t len;
1712                 ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name");
1713                 const uint16_t* id = block.getAttributeStringValue(index, &len);
1714                 if (id == NULL) {
1715                     fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n",
1716                             manifestPath.string(), block.getLineNumber(),
1717                             String8(block.getElementName(&len)).string());
1718                     hasErrors = true;
1719                     break;
1720                 }
1721                 String8 idStr(id);
1722                 char* p = idStr.lockBuffer(idStr.size());
1723                 char* e = p + idStr.size();
1724                 bool begins_with_digit = true;  // init to true so an empty string fails
1725                 while (e > p) {
1726                     e--;
1727                     if (*e >= '0' && *e <= '9') {
1728                       begins_with_digit = true;
1729                       continue;
1730                     }
1731                     if ((*e >= 'a' && *e <= 'z') ||
1732                         (*e >= 'A' && *e <= 'Z') ||
1733                         (*e == '_')) {
1734                       begins_with_digit = false;
1735                       continue;
1736                     }
1737                     if (isGroup && (*e == '-')) {
1738                         *e = '_';
1739                         begins_with_digit = false;
1740                         continue;
1741                     }
1742                     e++;
1743                     break;
1744                 }
1745                 idStr.unlockBuffer();
1746                 // verify that we stopped because we hit a period or
1747                 // the beginning of the string, and that the
1748                 // identifier didn't begin with a digit.
1749                 if (begins_with_digit || (e != p && *(e-1) != '.')) {
1750                   fprintf(stderr,
1751                           "%s:%d: Permission name <%s> is not a valid Java symbol\n",
1752                           manifestPath.string(), block.getLineNumber(), idStr.string());
1753                   hasErrors = true;
1754                 }
1755                 syms->addStringSymbol(String8(e), idStr, srcPos);
1756                 const uint16_t* cmt = block.getComment(&len);
1757                 if (cmt != NULL && *cmt != 0) {
1758                     //printf("Comment of %s: %s\n", String8(e).string(),
1759                     //        String8(cmt).string());
1760                     syms->appendComment(String8(e), String16(cmt), srcPos);
1761                 } else {
1762                     //printf("No comment for %s\n", String8(e).string());
1763                 }
1764                 syms->makeSymbolPublic(String8(e), srcPos);
1765             } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) {
1766                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1767                                  "name", packageIdentChars, true) != ATTR_OKAY) {
1768                     hasErrors = true;
1769                 }
1770             } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) {
1771                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1772                                  "name", classIdentChars, true) != ATTR_OKAY) {
1773                     hasErrors = true;
1774                 }
1775                 if (validateAttr(manifestPath, finalResTable, block,
1776                                  RESOURCES_ANDROID_NAMESPACE, "targetPackage",
1777                                  packageIdentChars, true) != ATTR_OKAY) {
1778                     hasErrors = true;
1779                 }
1780             } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) {
1781                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1782                                  "name", classIdentChars, false) != ATTR_OKAY) {
1783                     hasErrors = true;
1784                 }
1785                 if (validateAttr(manifestPath, finalResTable, block,
1786                                  RESOURCES_ANDROID_NAMESPACE, "permission",
1787                                  packageIdentChars, false) != ATTR_OKAY) {
1788                     hasErrors = true;
1789                 }
1790                 if (validateAttr(manifestPath, finalResTable, block,
1791                                  RESOURCES_ANDROID_NAMESPACE, "process",
1792                                  processIdentChars, false) != ATTR_OKAY) {
1793                     hasErrors = true;
1794                 }
1795                 if (validateAttr(manifestPath, finalResTable, block,
1796                                  RESOURCES_ANDROID_NAMESPACE, "taskAffinity",
1797                                  processIdentChars, false) != ATTR_OKAY) {
1798                     hasErrors = true;
1799                 }
1800             } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) {
1801                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1802                                  "name", classIdentChars, true) != ATTR_OKAY) {
1803                     hasErrors = true;
1804                 }
1805                 if (validateAttr(manifestPath, finalResTable, block,
1806                                  RESOURCES_ANDROID_NAMESPACE, "authorities",
1807                                  authoritiesIdentChars, true) != ATTR_OKAY) {
1808                     hasErrors = true;
1809                 }
1810                 if (validateAttr(manifestPath, finalResTable, block,
1811                                  RESOURCES_ANDROID_NAMESPACE, "permission",
1812                                  packageIdentChars, false) != ATTR_OKAY) {
1813                     hasErrors = true;
1814                 }
1815                 if (validateAttr(manifestPath, finalResTable, block,
1816                                  RESOURCES_ANDROID_NAMESPACE, "process",
1817                                  processIdentChars, false) != ATTR_OKAY) {
1818                     hasErrors = true;
1819                 }
1820             } else if (strcmp16(block.getElementName(&len), service16.string()) == 0
1821                        || strcmp16(block.getElementName(&len), receiver16.string()) == 0
1822                        || strcmp16(block.getElementName(&len), activity16.string()) == 0) {
1823                 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
1824                                  "name", classIdentChars, true) != ATTR_OKAY) {
1825                     hasErrors = true;
1826                 }
1827                 if (validateAttr(manifestPath, finalResTable, block,
1828                                  RESOURCES_ANDROID_NAMESPACE, "permission",
1829                                  packageIdentChars, false) != ATTR_OKAY) {
1830                     hasErrors = true;
1831                 }
1832                 if (validateAttr(manifestPath, finalResTable, block,
1833                                  RESOURCES_ANDROID_NAMESPACE, "process",
1834                                  processIdentChars, false) != ATTR_OKAY) {
1835                     hasErrors = true;
1836                 }
1837                 if (validateAttr(manifestPath, finalResTable, block,
1838                                  RESOURCES_ANDROID_NAMESPACE, "taskAffinity",
1839                                  processIdentChars, false) != ATTR_OKAY) {
1840                     hasErrors = true;
1841                 }
1842             } else if (strcmp16(block.getElementName(&len), action16.string()) == 0
1843                        || strcmp16(block.getElementName(&len), category16.string()) == 0) {
1844                 if (validateAttr(manifestPath, finalResTable, block,
1845                                  RESOURCES_ANDROID_NAMESPACE, "name",
1846                                  packageIdentChars, true) != ATTR_OKAY) {
1847                     hasErrors = true;
1848                 }
1849             } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) {
1850                 if (validateAttr(manifestPath, finalResTable, block,
1851                                  RESOURCES_ANDROID_NAMESPACE, "mimeType",
1852                                  typeIdentChars, true) != ATTR_OKAY) {
1853                     hasErrors = true;
1854                 }
1855                 if (validateAttr(manifestPath, finalResTable, block,
1856                                  RESOURCES_ANDROID_NAMESPACE, "scheme",
1857                                  schemeIdentChars, true) != ATTR_OKAY) {
1858                     hasErrors = true;
1859                 }
1860             } else if (strcmp16(block.getElementName(&len), feature_group16.string()) == 0) {
1861                 int depth = 1;
1862                 while ((code=block.next()) != ResXMLTree::END_DOCUMENT
1863                        && code > ResXMLTree::BAD_DOCUMENT) {
1864                     if (code == ResXMLTree::START_TAG) {
1865                         depth++;
1866                         if (strcmp16(block.getElementName(&len), uses_feature16.string()) == 0) {
1867                             ssize_t idx = block.indexOfAttribute(
1868                                     RESOURCES_ANDROID_NAMESPACE, "required");
1869                             if (idx < 0) {
1870                                 continue;
1871                             }
1872 
1873                             int32_t data = block.getAttributeData(idx);
1874                             if (data == 0) {
1875                                 fprintf(stderr, "%s:%d: Tag <uses-feature> can not have "
1876                                         "android:required=\"false\" when inside a "
1877                                         "<feature-group> tag.\n",
1878                                         manifestPath.string(), block.getLineNumber());
1879                                 hasErrors = true;
1880                             }
1881                         }
1882                     } else if (code == ResXMLTree::END_TAG) {
1883                         depth--;
1884                         if (depth == 0) {
1885                             break;
1886                         }
1887                     }
1888                 }
1889             }
1890         }
1891     }
1892 
1893     if (hasErrors) {
1894         return UNKNOWN_ERROR;
1895     }
1896 
1897     if (resFile != NULL) {
1898         // These resources are now considered to be a part of the included
1899         // resources, for others to reference.
1900         err = assets->addIncludedResources(resFile);
1901         if (err < NO_ERROR) {
1902             fprintf(stderr, "ERROR: Unable to parse generated resources, aborting.\n");
1903             return err;
1904         }
1905     }
1906 
1907     return err;
1908 }
1909 
getIndentSpace(int indent)1910 static const char* getIndentSpace(int indent)
1911 {
1912 static const char whitespace[] =
1913 "                                                                                       ";
1914 
1915     return whitespace + sizeof(whitespace) - 1 - indent*4;
1916 }
1917 
flattenSymbol(const String8 & symbol)1918 static String8 flattenSymbol(const String8& symbol) {
1919     String8 result(symbol);
1920     ssize_t first;
1921     if ((first = symbol.find(":", 0)) >= 0
1922             || (first = symbol.find(".", 0)) >= 0) {
1923         size_t size = symbol.size();
1924         char* buf = result.lockBuffer(size);
1925         for (size_t i = first; i < size; i++) {
1926             if (buf[i] == ':' || buf[i] == '.') {
1927                 buf[i] = '_';
1928             }
1929         }
1930         result.unlockBuffer(size);
1931     }
1932     return result;
1933 }
1934 
getSymbolPackage(const String8 & symbol,const sp<AaptAssets> & assets,bool pub)1935 static String8 getSymbolPackage(const String8& symbol, const sp<AaptAssets>& assets, bool pub) {
1936     ssize_t colon = symbol.find(":", 0);
1937     if (colon >= 0) {
1938         return String8(symbol.string(), colon);
1939     }
1940     return pub ? assets->getPackage() : assets->getSymbolsPrivatePackage();
1941 }
1942 
getSymbolName(const String8 & symbol)1943 static String8 getSymbolName(const String8& symbol) {
1944     ssize_t colon = symbol.find(":", 0);
1945     if (colon >= 0) {
1946         return String8(symbol.string() + colon + 1);
1947     }
1948     return symbol;
1949 }
1950 
getAttributeComment(const sp<AaptAssets> & assets,const String8 & name,String16 * outTypeComment=NULL)1951 static String16 getAttributeComment(const sp<AaptAssets>& assets,
1952                                     const String8& name,
1953                                     String16* outTypeComment = NULL)
1954 {
1955     sp<AaptSymbols> asym = assets->getSymbolsFor(String8("R"));
1956     if (asym != NULL) {
1957         //printf("Got R symbols!\n");
1958         asym = asym->getNestedSymbols().valueFor(String8("attr"));
1959         if (asym != NULL) {
1960             //printf("Got attrs symbols! comment %s=%s\n",
1961             //     name.string(), String8(asym->getComment(name)).string());
1962             if (outTypeComment != NULL) {
1963                 *outTypeComment = asym->getTypeComment(name);
1964             }
1965             return asym->getComment(name);
1966         }
1967     }
1968     return String16();
1969 }
1970 
writeResourceLoadedCallbackForLayoutClasses(FILE * fp,const sp<AaptAssets> & assets,const sp<AaptSymbols> & symbols,int indent,bool includePrivate)1971 static status_t writeResourceLoadedCallbackForLayoutClasses(
1972     FILE* fp, const sp<AaptAssets>& assets,
1973     const sp<AaptSymbols>& symbols, int indent, bool includePrivate)
1974 {
1975     String16 attr16("attr");
1976     String16 package16(assets->getPackage());
1977 
1978     const char* indentStr = getIndentSpace(indent);
1979     bool hasErrors = false;
1980 
1981     size_t i;
1982     size_t N = symbols->getNestedSymbols().size();
1983     for (i=0; i<N; i++) {
1984         sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
1985         String8 realClassName(symbols->getNestedSymbols().keyAt(i));
1986         String8 nclassName(flattenSymbol(realClassName));
1987 
1988         fprintf(fp,
1989                 "%sfor(int i = 0; i < styleable.%s.length; ++i) {\n"
1990                 "%sstyleable.%s[i] = (styleable.%s[i] & 0x00ffffff) | (packageId << 24);\n"
1991                 "%s}\n",
1992                 indentStr, nclassName.string(),
1993                 getIndentSpace(indent+1), nclassName.string(), nclassName.string(),
1994                 indentStr);
1995     }
1996 
1997     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
1998 }
1999 
writeResourceLoadedCallback(FILE * fp,const sp<AaptAssets> & assets,bool includePrivate,const sp<AaptSymbols> & symbols,const String8 & className,int indent)2000 static status_t writeResourceLoadedCallback(
2001     FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
2002     const sp<AaptSymbols>& symbols, const String8& className, int indent)
2003 {
2004     size_t i;
2005     status_t err = NO_ERROR;
2006 
2007     size_t N = symbols->getSymbols().size();
2008     for (i=0; i<N; i++) {
2009         const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
2010         if (sym.typeCode == AaptSymbolEntry::TYPE_UNKNOWN) {
2011             continue;
2012         }
2013         if (!assets->isJavaSymbol(sym, includePrivate)) {
2014             continue;
2015         }
2016         String8 flat_name(flattenSymbol(sym.name));
2017         fprintf(fp,
2018                 "%s%s.%s = (%s.%s & 0x00ffffff) | (packageId << 24);\n",
2019                 getIndentSpace(indent), className.string(), flat_name.string(),
2020                 className.string(), flat_name.string());
2021     }
2022 
2023     N = symbols->getNestedSymbols().size();
2024     for (i=0; i<N; i++) {
2025         sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
2026         String8 nclassName(symbols->getNestedSymbols().keyAt(i));
2027         if (nclassName == "styleable") {
2028             err = writeResourceLoadedCallbackForLayoutClasses(
2029                     fp, assets, nsymbols, indent, includePrivate);
2030         } else {
2031             err = writeResourceLoadedCallback(fp, assets, includePrivate, nsymbols,
2032                     nclassName, indent);
2033         }
2034         if (err != NO_ERROR) {
2035             return err;
2036         }
2037     }
2038 
2039     return NO_ERROR;
2040 }
2041 
writeLayoutClasses(FILE * fp,const sp<AaptAssets> & assets,const sp<AaptSymbols> & symbols,int indent,bool includePrivate,bool nonConstantId)2042 static status_t writeLayoutClasses(
2043     FILE* fp, const sp<AaptAssets>& assets,
2044     const sp<AaptSymbols>& symbols, int indent, bool includePrivate, bool nonConstantId)
2045 {
2046     const char* indentStr = getIndentSpace(indent);
2047     if (!includePrivate) {
2048         fprintf(fp, "%s/** @doconly */\n", indentStr);
2049     }
2050     fprintf(fp, "%spublic static final class styleable {\n", indentStr);
2051     indent++;
2052 
2053     String16 attr16("attr");
2054     String16 package16(assets->getPackage());
2055 
2056     indentStr = getIndentSpace(indent);
2057     bool hasErrors = false;
2058 
2059     size_t i;
2060     size_t N = symbols->getNestedSymbols().size();
2061     for (i=0; i<N; i++) {
2062         sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
2063         String8 realClassName(symbols->getNestedSymbols().keyAt(i));
2064         String8 nclassName(flattenSymbol(realClassName));
2065 
2066         SortedVector<uint32_t> idents;
2067         Vector<uint32_t> origOrder;
2068         Vector<bool> publicFlags;
2069 
2070         size_t a;
2071         size_t NA = nsymbols->getSymbols().size();
2072         for (a=0; a<NA; a++) {
2073             const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a));
2074             int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32
2075                     ? sym.int32Val : 0;
2076             bool isPublic = true;
2077             if (code == 0) {
2078                 String16 name16(sym.name);
2079                 uint32_t typeSpecFlags;
2080                 code = assets->getIncludedResources().identifierForName(
2081                     name16.string(), name16.size(),
2082                     attr16.string(), attr16.size(),
2083                     package16.string(), package16.size(), &typeSpecFlags);
2084                 if (code == 0) {
2085                     fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n",
2086                             nclassName.string(), sym.name.string());
2087                     hasErrors = true;
2088                 }
2089                 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
2090             }
2091             idents.add(code);
2092             origOrder.add(code);
2093             publicFlags.add(isPublic);
2094         }
2095 
2096         NA = idents.size();
2097 
2098         String16 comment = symbols->getComment(realClassName);
2099         AnnotationProcessor ann;
2100         fprintf(fp, "%s/** ", indentStr);
2101         if (comment.size() > 0) {
2102             String8 cmt(comment);
2103             ann.preprocessComment(cmt);
2104             fprintf(fp, "%s\n", cmt.string());
2105         } else {
2106             fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string());
2107         }
2108         bool hasTable = false;
2109         for (a=0; a<NA; a++) {
2110             ssize_t pos = idents.indexOf(origOrder.itemAt(a));
2111             if (pos >= 0) {
2112                 if (!hasTable) {
2113                     hasTable = true;
2114                     fprintf(fp,
2115                             "%s   <p>Includes the following attributes:</p>\n"
2116                             "%s   <table>\n"
2117                             "%s   <colgroup align=\"left\" />\n"
2118                             "%s   <colgroup align=\"left\" />\n"
2119                             "%s   <tr><th>Attribute</th><th>Description</th></tr>\n",
2120                             indentStr,
2121                             indentStr,
2122                             indentStr,
2123                             indentStr,
2124                             indentStr);
2125                 }
2126                 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
2127                 if (!publicFlags.itemAt(a) && !includePrivate) {
2128                     continue;
2129                 }
2130                 String8 name8(sym.name);
2131                 String16 comment(sym.comment);
2132                 if (comment.size() <= 0) {
2133                     comment = getAttributeComment(assets, name8);
2134                 }
2135                 if (comment.size() > 0) {
2136                     const char16_t* p = comment.string();
2137                     while (*p != 0 && *p != '.') {
2138                         if (*p == '{') {
2139                             while (*p != 0 && *p != '}') {
2140                                 p++;
2141                             }
2142                         } else {
2143                             p++;
2144                         }
2145                     }
2146                     if (*p == '.') {
2147                         p++;
2148                     }
2149                     comment = String16(comment.string(), p-comment.string());
2150                 }
2151                 fprintf(fp, "%s   <tr><td><code>{@link #%s_%s %s:%s}</code></td><td>%s</td></tr>\n",
2152                         indentStr, nclassName.string(),
2153                         flattenSymbol(name8).string(),
2154                         getSymbolPackage(name8, assets, true).string(),
2155                         getSymbolName(name8).string(),
2156                         String8(comment).string());
2157             }
2158         }
2159         if (hasTable) {
2160             fprintf(fp, "%s   </table>\n", indentStr);
2161         }
2162         for (a=0; a<NA; a++) {
2163             ssize_t pos = idents.indexOf(origOrder.itemAt(a));
2164             if (pos >= 0) {
2165                 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
2166                 if (!publicFlags.itemAt(a) && !includePrivate) {
2167                     continue;
2168                 }
2169                 fprintf(fp, "%s   @see #%s_%s\n",
2170                         indentStr, nclassName.string(),
2171                         flattenSymbol(sym.name).string());
2172             }
2173         }
2174         fprintf(fp, "%s */\n", getIndentSpace(indent));
2175 
2176         ann.printAnnotations(fp, indentStr);
2177 
2178         fprintf(fp,
2179                 "%spublic static final int[] %s = {\n"
2180                 "%s",
2181                 indentStr, nclassName.string(),
2182                 getIndentSpace(indent+1));
2183 
2184         for (a=0; a<NA; a++) {
2185             if (a != 0) {
2186                 if ((a&3) == 0) {
2187                     fprintf(fp, ",\n%s", getIndentSpace(indent+1));
2188                 } else {
2189                     fprintf(fp, ", ");
2190                 }
2191             }
2192             fprintf(fp, "0x%08x", idents[a]);
2193         }
2194 
2195         fprintf(fp, "\n%s};\n", indentStr);
2196 
2197         for (a=0; a<NA; a++) {
2198             ssize_t pos = idents.indexOf(origOrder.itemAt(a));
2199             if (pos >= 0) {
2200                 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
2201                 if (!publicFlags.itemAt(a) && !includePrivate) {
2202                     continue;
2203                 }
2204                 String8 name8(sym.name);
2205                 String16 comment(sym.comment);
2206                 String16 typeComment;
2207                 if (comment.size() <= 0) {
2208                     comment = getAttributeComment(assets, name8, &typeComment);
2209                 } else {
2210                     getAttributeComment(assets, name8, &typeComment);
2211                 }
2212 
2213                 uint32_t typeSpecFlags = 0;
2214                 String16 name16(sym.name);
2215                 assets->getIncludedResources().identifierForName(
2216                     name16.string(), name16.size(),
2217                     attr16.string(), attr16.size(),
2218                     package16.string(), package16.size(), &typeSpecFlags);
2219                 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
2220                 //    String8(attr16).string(), String8(name16).string(), typeSpecFlags);
2221                 const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
2222 
2223                 AnnotationProcessor ann;
2224                 fprintf(fp, "%s/**\n", indentStr);
2225                 if (comment.size() > 0) {
2226                     String8 cmt(comment);
2227                     ann.preprocessComment(cmt);
2228                     fprintf(fp, "%s  <p>\n%s  @attr description\n", indentStr, indentStr);
2229                     fprintf(fp, "%s  %s\n", indentStr, cmt.string());
2230                 } else {
2231                     fprintf(fp,
2232                             "%s  <p>This symbol is the offset where the {@link %s.R.attr#%s}\n"
2233                             "%s  attribute's value can be found in the {@link #%s} array.\n",
2234                             indentStr,
2235                             getSymbolPackage(name8, assets, pub).string(),
2236                             getSymbolName(name8).string(),
2237                             indentStr, nclassName.string());
2238                 }
2239                 if (typeComment.size() > 0) {
2240                     String8 cmt(typeComment);
2241                     ann.preprocessComment(cmt);
2242                     fprintf(fp, "\n\n%s  %s\n", indentStr, cmt.string());
2243                 }
2244                 if (comment.size() > 0) {
2245                     if (pub) {
2246                         fprintf(fp,
2247                                 "%s  <p>This corresponds to the global attribute\n"
2248                                 "%s  resource symbol {@link %s.R.attr#%s}.\n",
2249                                 indentStr, indentStr,
2250                                 getSymbolPackage(name8, assets, true).string(),
2251                                 getSymbolName(name8).string());
2252                     } else {
2253                         fprintf(fp,
2254                                 "%s  <p>This is a private symbol.\n", indentStr);
2255                     }
2256                 }
2257                 fprintf(fp, "%s  @attr name %s:%s\n", indentStr,
2258                         getSymbolPackage(name8, assets, pub).string(),
2259                         getSymbolName(name8).string());
2260                 fprintf(fp, "%s*/\n", indentStr);
2261                 ann.printAnnotations(fp, indentStr);
2262 
2263                 const char * id_format = nonConstantId ?
2264                         "%spublic static int %s_%s = %d;\n" :
2265                         "%spublic static final int %s_%s = %d;\n";
2266 
2267                 fprintf(fp,
2268                         id_format,
2269                         indentStr, nclassName.string(),
2270                         flattenSymbol(name8).string(), (int)pos);
2271             }
2272         }
2273     }
2274 
2275     indent--;
2276     fprintf(fp, "%s};\n", getIndentSpace(indent));
2277     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
2278 }
2279 
writeTextLayoutClasses(FILE * fp,const sp<AaptAssets> & assets,const sp<AaptSymbols> & symbols,bool includePrivate)2280 static status_t writeTextLayoutClasses(
2281     FILE* fp, const sp<AaptAssets>& assets,
2282     const sp<AaptSymbols>& symbols, bool includePrivate)
2283 {
2284     String16 attr16("attr");
2285     String16 package16(assets->getPackage());
2286 
2287     bool hasErrors = false;
2288 
2289     size_t i;
2290     size_t N = symbols->getNestedSymbols().size();
2291     for (i=0; i<N; i++) {
2292         sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
2293         String8 realClassName(symbols->getNestedSymbols().keyAt(i));
2294         String8 nclassName(flattenSymbol(realClassName));
2295 
2296         SortedVector<uint32_t> idents;
2297         Vector<uint32_t> origOrder;
2298         Vector<bool> publicFlags;
2299 
2300         size_t a;
2301         size_t NA = nsymbols->getSymbols().size();
2302         for (a=0; a<NA; a++) {
2303             const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a));
2304             int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32
2305                     ? sym.int32Val : 0;
2306             bool isPublic = true;
2307             if (code == 0) {
2308                 String16 name16(sym.name);
2309                 uint32_t typeSpecFlags;
2310                 code = assets->getIncludedResources().identifierForName(
2311                     name16.string(), name16.size(),
2312                     attr16.string(), attr16.size(),
2313                     package16.string(), package16.size(), &typeSpecFlags);
2314                 if (code == 0) {
2315                     fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n",
2316                             nclassName.string(), sym.name.string());
2317                     hasErrors = true;
2318                 }
2319                 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
2320             }
2321             idents.add(code);
2322             origOrder.add(code);
2323             publicFlags.add(isPublic);
2324         }
2325 
2326         NA = idents.size();
2327 
2328         fprintf(fp, "int[] styleable %s {", nclassName.string());
2329 
2330         for (a=0; a<NA; a++) {
2331             if (a != 0) {
2332                 fprintf(fp, ",");
2333             }
2334             fprintf(fp, " 0x%08x", idents[a]);
2335         }
2336 
2337         fprintf(fp, " }\n");
2338 
2339         for (a=0; a<NA; a++) {
2340             ssize_t pos = idents.indexOf(origOrder.itemAt(a));
2341             if (pos >= 0) {
2342                 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
2343                 if (!publicFlags.itemAt(a) && !includePrivate) {
2344                     continue;
2345                 }
2346                 String8 name8(sym.name);
2347                 String16 comment(sym.comment);
2348                 String16 typeComment;
2349                 if (comment.size() <= 0) {
2350                     comment = getAttributeComment(assets, name8, &typeComment);
2351                 } else {
2352                     getAttributeComment(assets, name8, &typeComment);
2353                 }
2354 
2355                 uint32_t typeSpecFlags = 0;
2356                 String16 name16(sym.name);
2357                 assets->getIncludedResources().identifierForName(
2358                     name16.string(), name16.size(),
2359                     attr16.string(), attr16.size(),
2360                     package16.string(), package16.size(), &typeSpecFlags);
2361                 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
2362                 //    String8(attr16).string(), String8(name16).string(), typeSpecFlags);
2363                 const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
2364 
2365                 fprintf(fp,
2366                         "int styleable %s_%s %d\n",
2367                         nclassName.string(),
2368                         flattenSymbol(name8).string(), (int)pos);
2369             }
2370         }
2371     }
2372 
2373     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
2374 }
2375 
writeSymbolClass(FILE * fp,const sp<AaptAssets> & assets,bool includePrivate,const sp<AaptSymbols> & symbols,const String8 & className,int indent,bool nonConstantId,bool emitCallback)2376 static status_t writeSymbolClass(
2377     FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
2378     const sp<AaptSymbols>& symbols, const String8& className, int indent,
2379     bool nonConstantId, bool emitCallback)
2380 {
2381     fprintf(fp, "%spublic %sfinal class %s {\n",
2382             getIndentSpace(indent),
2383             indent != 0 ? "static " : "", className.string());
2384     indent++;
2385 
2386     size_t i;
2387     status_t err = NO_ERROR;
2388 
2389     const char * id_format = nonConstantId ?
2390             "%spublic static int %s=0x%08x;\n" :
2391             "%spublic static final int %s=0x%08x;\n";
2392 
2393     size_t N = symbols->getSymbols().size();
2394     for (i=0; i<N; i++) {
2395         const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
2396         if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) {
2397             continue;
2398         }
2399         if (!assets->isJavaSymbol(sym, includePrivate)) {
2400             continue;
2401         }
2402         String8 name8(sym.name);
2403         String16 comment(sym.comment);
2404         bool haveComment = false;
2405         AnnotationProcessor ann;
2406         if (comment.size() > 0) {
2407             haveComment = true;
2408             String8 cmt(comment);
2409             ann.preprocessComment(cmt);
2410             fprintf(fp,
2411                     "%s/** %s\n",
2412                     getIndentSpace(indent), cmt.string());
2413         } else if (sym.isPublic && !includePrivate) {
2414             sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
2415                 assets->getPackage().string(), className.string(),
2416                 String8(sym.name).string());
2417         }
2418         String16 typeComment(sym.typeComment);
2419         if (typeComment.size() > 0) {
2420             String8 cmt(typeComment);
2421             ann.preprocessComment(cmt);
2422             if (!haveComment) {
2423                 haveComment = true;
2424                 fprintf(fp,
2425                         "%s/** %s\n", getIndentSpace(indent), cmt.string());
2426             } else {
2427                 fprintf(fp,
2428                         "%s %s\n", getIndentSpace(indent), cmt.string());
2429             }
2430         }
2431         if (haveComment) {
2432             fprintf(fp,"%s */\n", getIndentSpace(indent));
2433         }
2434         ann.printAnnotations(fp, getIndentSpace(indent));
2435         fprintf(fp, id_format,
2436                 getIndentSpace(indent),
2437                 flattenSymbol(name8).string(), (int)sym.int32Val);
2438     }
2439 
2440     for (i=0; i<N; i++) {
2441         const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
2442         if (sym.typeCode != AaptSymbolEntry::TYPE_STRING) {
2443             continue;
2444         }
2445         if (!assets->isJavaSymbol(sym, includePrivate)) {
2446             continue;
2447         }
2448         String8 name8(sym.name);
2449         String16 comment(sym.comment);
2450         AnnotationProcessor ann;
2451         if (comment.size() > 0) {
2452             String8 cmt(comment);
2453             ann.preprocessComment(cmt);
2454             fprintf(fp,
2455                     "%s/** %s\n"
2456                      "%s */\n",
2457                     getIndentSpace(indent), cmt.string(),
2458                     getIndentSpace(indent));
2459         } else if (sym.isPublic && !includePrivate) {
2460             sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
2461                 assets->getPackage().string(), className.string(),
2462                 String8(sym.name).string());
2463         }
2464         ann.printAnnotations(fp, getIndentSpace(indent));
2465         fprintf(fp, "%spublic static final String %s=\"%s\";\n",
2466                 getIndentSpace(indent),
2467                 flattenSymbol(name8).string(), sym.stringVal.string());
2468     }
2469 
2470     sp<AaptSymbols> styleableSymbols;
2471 
2472     N = symbols->getNestedSymbols().size();
2473     for (i=0; i<N; i++) {
2474         sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
2475         String8 nclassName(symbols->getNestedSymbols().keyAt(i));
2476         if (nclassName == "styleable") {
2477             styleableSymbols = nsymbols;
2478         } else {
2479             err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName,
2480                     indent, nonConstantId, false);
2481         }
2482         if (err != NO_ERROR) {
2483             return err;
2484         }
2485     }
2486 
2487     if (styleableSymbols != NULL) {
2488         err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate, nonConstantId);
2489         if (err != NO_ERROR) {
2490             return err;
2491         }
2492     }
2493 
2494     if (emitCallback) {
2495         fprintf(fp, "%spublic static void onResourcesLoaded(int packageId) {\n",
2496                 getIndentSpace(indent));
2497         writeResourceLoadedCallback(fp, assets, includePrivate, symbols, className, indent + 1);
2498         fprintf(fp, "%s}\n", getIndentSpace(indent));
2499     }
2500 
2501     indent--;
2502     fprintf(fp, "%s}\n", getIndentSpace(indent));
2503     return NO_ERROR;
2504 }
2505 
writeTextSymbolClass(FILE * fp,const sp<AaptAssets> & assets,bool includePrivate,const sp<AaptSymbols> & symbols,const String8 & className)2506 static status_t writeTextSymbolClass(
2507     FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
2508     const sp<AaptSymbols>& symbols, const String8& className)
2509 {
2510     size_t i;
2511     status_t err = NO_ERROR;
2512 
2513     size_t N = symbols->getSymbols().size();
2514     for (i=0; i<N; i++) {
2515         const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
2516         if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) {
2517             continue;
2518         }
2519 
2520         if (!assets->isJavaSymbol(sym, includePrivate)) {
2521             continue;
2522         }
2523 
2524         String8 name8(sym.name);
2525         fprintf(fp, "int %s %s 0x%08x\n",
2526                 className.string(),
2527                 flattenSymbol(name8).string(), (int)sym.int32Val);
2528     }
2529 
2530     N = symbols->getNestedSymbols().size();
2531     for (i=0; i<N; i++) {
2532         sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
2533         String8 nclassName(symbols->getNestedSymbols().keyAt(i));
2534         if (nclassName == "styleable") {
2535             err = writeTextLayoutClasses(fp, assets, nsymbols, includePrivate);
2536         } else {
2537             err = writeTextSymbolClass(fp, assets, includePrivate, nsymbols, nclassName);
2538         }
2539         if (err != NO_ERROR) {
2540             return err;
2541         }
2542     }
2543 
2544     return NO_ERROR;
2545 }
2546 
writeResourceSymbols(Bundle * bundle,const sp<AaptAssets> & assets,const String8 & package,bool includePrivate,bool emitCallback)2547 status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
2548     const String8& package, bool includePrivate, bool emitCallback)
2549 {
2550     if (!bundle->getRClassDir()) {
2551         return NO_ERROR;
2552     }
2553 
2554     const char* textSymbolsDest = bundle->getOutputTextSymbols();
2555 
2556     String8 R("R");
2557     const size_t N = assets->getSymbols().size();
2558     for (size_t i=0; i<N; i++) {
2559         sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i);
2560         String8 className(assets->getSymbols().keyAt(i));
2561         String8 dest(bundle->getRClassDir());
2562 
2563         if (bundle->getMakePackageDirs()) {
2564             String8 pkg(package);
2565             const char* last = pkg.string();
2566             const char* s = last-1;
2567             do {
2568                 s++;
2569                 if (s > last && (*s == '.' || *s == 0)) {
2570                     String8 part(last, s-last);
2571                     dest.appendPath(part);
2572 #ifdef HAVE_MS_C_RUNTIME
2573                     _mkdir(dest.string());
2574 #else
2575                     mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
2576 #endif
2577                     last = s+1;
2578                 }
2579             } while (*s);
2580         }
2581         dest.appendPath(className);
2582         dest.append(".java");
2583         FILE* fp = fopen(dest.string(), "w+");
2584         if (fp == NULL) {
2585             fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
2586                     dest.string(), strerror(errno));
2587             return UNKNOWN_ERROR;
2588         }
2589         if (bundle->getVerbose()) {
2590             printf("  Writing symbols for class %s.\n", className.string());
2591         }
2592 
2593         fprintf(fp,
2594             "/* AUTO-GENERATED FILE.  DO NOT MODIFY.\n"
2595             " *\n"
2596             " * This class was automatically generated by the\n"
2597             " * aapt tool from the resource data it found.  It\n"
2598             " * should not be modified by hand.\n"
2599             " */\n"
2600             "\n"
2601             "package %s;\n\n", package.string());
2602 
2603         status_t err = writeSymbolClass(fp, assets, includePrivate, symbols,
2604                 className, 0, bundle->getNonConstantId(), emitCallback);
2605         fclose(fp);
2606         if (err != NO_ERROR) {
2607             return err;
2608         }
2609 
2610         if (textSymbolsDest != NULL && R == className) {
2611             String8 textDest(textSymbolsDest);
2612             textDest.appendPath(className);
2613             textDest.append(".txt");
2614 
2615             FILE* fp = fopen(textDest.string(), "w+");
2616             if (fp == NULL) {
2617                 fprintf(stderr, "ERROR: Unable to open text symbol file %s: %s\n",
2618                         textDest.string(), strerror(errno));
2619                 return UNKNOWN_ERROR;
2620             }
2621             if (bundle->getVerbose()) {
2622                 printf("  Writing text symbols for class %s.\n", className.string());
2623             }
2624 
2625             status_t err = writeTextSymbolClass(fp, assets, includePrivate, symbols,
2626                     className);
2627             fclose(fp);
2628             if (err != NO_ERROR) {
2629                 return err;
2630             }
2631         }
2632 
2633         // If we were asked to generate a dependency file, we'll go ahead and add this R.java
2634         // as a target in the dependency file right next to it.
2635         if (bundle->getGenDependencies() && R == className) {
2636             // Add this R.java to the dependency file
2637             String8 dependencyFile(bundle->getRClassDir());
2638             dependencyFile.appendPath("R.java.d");
2639 
2640             FILE *fp = fopen(dependencyFile.string(), "a");
2641             fprintf(fp,"%s \\\n", dest.string());
2642             fclose(fp);
2643         }
2644     }
2645 
2646     return NO_ERROR;
2647 }
2648 
2649 
2650 class ProguardKeepSet
2651 {
2652 public:
2653     // { rule --> { file locations } }
2654     KeyedVector<String8, SortedVector<String8> > rules;
2655 
2656     void add(const String8& rule, const String8& where);
2657 };
2658 
add(const String8 & rule,const String8 & where)2659 void ProguardKeepSet::add(const String8& rule, const String8& where)
2660 {
2661     ssize_t index = rules.indexOfKey(rule);
2662     if (index < 0) {
2663         index = rules.add(rule, SortedVector<String8>());
2664     }
2665     rules.editValueAt(index).add(where);
2666 }
2667 
2668 void
addProguardKeepRule(ProguardKeepSet * keep,const String8 & inClassName,const char * pkg,const String8 & srcName,int line)2669 addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName,
2670         const char* pkg, const String8& srcName, int line)
2671 {
2672     String8 className(inClassName);
2673     if (pkg != NULL) {
2674         // asdf     --> package.asdf
2675         // .asdf  .a.b  --> package.asdf package.a.b
2676         // asdf.adsf --> asdf.asdf
2677         const char* p = className.string();
2678         const char* q = strchr(p, '.');
2679         if (p == q) {
2680             className = pkg;
2681             className.append(inClassName);
2682         } else if (q == NULL) {
2683             className = pkg;
2684             className.append(".");
2685             className.append(inClassName);
2686         }
2687     }
2688 
2689     String8 rule("-keep class ");
2690     rule += className;
2691     rule += " { <init>(...); }";
2692 
2693     String8 location("view ");
2694     location += srcName;
2695     char lineno[20];
2696     sprintf(lineno, ":%d", line);
2697     location += lineno;
2698 
2699     keep->add(rule, location);
2700 }
2701 
2702 void
addProguardKeepMethodRule(ProguardKeepSet * keep,const String8 & memberName,const char * pkg,const String8 & srcName,int line)2703 addProguardKeepMethodRule(ProguardKeepSet* keep, const String8& memberName,
2704         const char* pkg, const String8& srcName, int line)
2705 {
2706     String8 rule("-keepclassmembers class * { *** ");
2707     rule += memberName;
2708     rule += "(...); }";
2709 
2710     String8 location("onClick ");
2711     location += srcName;
2712     char lineno[20];
2713     sprintf(lineno, ":%d", line);
2714     location += lineno;
2715 
2716     keep->add(rule, location);
2717 }
2718 
2719 status_t
writeProguardForAndroidManifest(ProguardKeepSet * keep,const sp<AaptAssets> & assets)2720 writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
2721 {
2722     status_t err;
2723     ResXMLTree tree;
2724     size_t len;
2725     ResXMLTree::event_code_t code;
2726     int depth = 0;
2727     bool inApplication = false;
2728     String8 error;
2729     sp<AaptGroup> assGroup;
2730     sp<AaptFile> assFile;
2731     String8 pkg;
2732 
2733     // First, look for a package file to parse.  This is required to
2734     // be able to generate the resource information.
2735     assGroup = assets->getFiles().valueFor(String8("AndroidManifest.xml"));
2736     if (assGroup == NULL) {
2737         fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n");
2738         return -1;
2739     }
2740 
2741     if (assGroup->getFiles().size() != 1) {
2742         fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
2743                 assGroup->getFiles().valueAt(0)->getPrintableSource().string());
2744     }
2745 
2746     assFile = assGroup->getFiles().valueAt(0);
2747 
2748     err = parseXMLResource(assFile, &tree);
2749     if (err != NO_ERROR) {
2750         return err;
2751     }
2752 
2753     tree.restart();
2754 
2755     while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
2756         if (code == ResXMLTree::END_TAG) {
2757             if (/* name == "Application" && */ depth == 2) {
2758                 inApplication = false;
2759             }
2760             depth--;
2761             continue;
2762         }
2763         if (code != ResXMLTree::START_TAG) {
2764             continue;
2765         }
2766         depth++;
2767         String8 tag(tree.getElementName(&len));
2768         // printf("Depth %d tag %s\n", depth, tag.string());
2769         bool keepTag = false;
2770         if (depth == 1) {
2771             if (tag != "manifest") {
2772                 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
2773                 return -1;
2774             }
2775             pkg = AaptXml::getAttribute(tree, NULL, "package");
2776         } else if (depth == 2) {
2777             if (tag == "application") {
2778                 inApplication = true;
2779                 keepTag = true;
2780 
2781                 String8 agent = AaptXml::getAttribute(tree,
2782                         "http://schemas.android.com/apk/res/android",
2783                         "backupAgent", &error);
2784                 if (agent.length() > 0) {
2785                     addProguardKeepRule(keep, agent, pkg.string(),
2786                             assFile->getPrintableSource(), tree.getLineNumber());
2787                 }
2788             } else if (tag == "instrumentation") {
2789                 keepTag = true;
2790             }
2791         }
2792         if (!keepTag && inApplication && depth == 3) {
2793             if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
2794                 keepTag = true;
2795             }
2796         }
2797         if (keepTag) {
2798             String8 name = AaptXml::getAttribute(tree,
2799                     "http://schemas.android.com/apk/res/android", "name", &error);
2800             if (error != "") {
2801                 fprintf(stderr, "ERROR: %s\n", error.string());
2802                 return -1;
2803             }
2804             if (name.length() > 0) {
2805                 addProguardKeepRule(keep, name, pkg.string(),
2806                         assFile->getPrintableSource(), tree.getLineNumber());
2807             }
2808         }
2809     }
2810 
2811     return NO_ERROR;
2812 }
2813 
2814 struct NamespaceAttributePair {
2815     const char* ns;
2816     const char* attr;
2817 
NamespaceAttributePairNamespaceAttributePair2818     NamespaceAttributePair(const char* n, const char* a) : ns(n), attr(a) {}
NamespaceAttributePairNamespaceAttributePair2819     NamespaceAttributePair() : ns(NULL), attr(NULL) {}
2820 };
2821 
2822 status_t
writeProguardForXml(ProguardKeepSet * keep,const sp<AaptFile> & layoutFile,const Vector<String8> & startTags,const KeyedVector<String8,Vector<NamespaceAttributePair>> * tagAttrPairs)2823 writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
2824         const Vector<String8>& startTags, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs)
2825 {
2826     status_t err;
2827     ResXMLTree tree;
2828     size_t len;
2829     ResXMLTree::event_code_t code;
2830 
2831     err = parseXMLResource(layoutFile, &tree);
2832     if (err != NO_ERROR) {
2833         return err;
2834     }
2835 
2836     tree.restart();
2837 
2838     if (!startTags.isEmpty()) {
2839         bool haveStart = false;
2840         while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
2841             if (code != ResXMLTree::START_TAG) {
2842                 continue;
2843             }
2844             String8 tag(tree.getElementName(&len));
2845             const size_t numStartTags = startTags.size();
2846             for (size_t i = 0; i < numStartTags; i++) {
2847                 if (tag == startTags[i]) {
2848                     haveStart = true;
2849                 }
2850             }
2851             break;
2852         }
2853         if (!haveStart) {
2854             return NO_ERROR;
2855         }
2856     }
2857 
2858     while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
2859         if (code != ResXMLTree::START_TAG) {
2860             continue;
2861         }
2862         String8 tag(tree.getElementName(&len));
2863 
2864         // If there is no '.', we'll assume that it's one of the built in names.
2865         if (strchr(tag.string(), '.')) {
2866             addProguardKeepRule(keep, tag, NULL,
2867                     layoutFile->getPrintableSource(), tree.getLineNumber());
2868         } else if (tagAttrPairs != NULL) {
2869             ssize_t tagIndex = tagAttrPairs->indexOfKey(tag);
2870             if (tagIndex >= 0) {
2871                 const Vector<NamespaceAttributePair>& nsAttrVector = tagAttrPairs->valueAt(tagIndex);
2872                 for (size_t i = 0; i < nsAttrVector.size(); i++) {
2873                     const NamespaceAttributePair& nsAttr = nsAttrVector[i];
2874 
2875                     ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr);
2876                     if (attrIndex < 0) {
2877                         // fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n",
2878                         //        layoutFile->getPrintableSource().string(), tree.getLineNumber(),
2879                         //        tag.string(), nsAttr.ns, nsAttr.attr);
2880                     } else {
2881                         size_t len;
2882                         addProguardKeepRule(keep,
2883                                             String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
2884                                             layoutFile->getPrintableSource(), tree.getLineNumber());
2885                     }
2886                 }
2887             }
2888         }
2889         ssize_t attrIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "onClick");
2890         if (attrIndex >= 0) {
2891             size_t len;
2892             addProguardKeepMethodRule(keep,
2893                                 String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
2894                                 layoutFile->getPrintableSource(), tree.getLineNumber());
2895         }
2896     }
2897 
2898     return NO_ERROR;
2899 }
2900 
addTagAttrPair(KeyedVector<String8,Vector<NamespaceAttributePair>> * dest,const char * tag,const char * ns,const char * attr)2901 static void addTagAttrPair(KeyedVector<String8, Vector<NamespaceAttributePair> >* dest,
2902         const char* tag, const char* ns, const char* attr) {
2903     String8 tagStr(tag);
2904     ssize_t index = dest->indexOfKey(tagStr);
2905 
2906     if (index < 0) {
2907         Vector<NamespaceAttributePair> vector;
2908         vector.add(NamespaceAttributePair(ns, attr));
2909         dest->add(tagStr, vector);
2910     } else {
2911         dest->editValueAt(index).add(NamespaceAttributePair(ns, attr));
2912     }
2913 }
2914 
2915 status_t
writeProguardForLayouts(ProguardKeepSet * keep,const sp<AaptAssets> & assets)2916 writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
2917 {
2918     status_t err;
2919 
2920     // tag:attribute pairs that should be checked in layout files.
2921     KeyedVector<String8, Vector<NamespaceAttributePair> > kLayoutTagAttrPairs;
2922     addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class");
2923     addTagAttrPair(&kLayoutTagAttrPairs, "fragment", NULL, "class");
2924     addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name");
2925 
2926     // tag:attribute pairs that should be checked in xml files.
2927     KeyedVector<String8, Vector<NamespaceAttributePair> > kXmlTagAttrPairs;
2928     addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment");
2929     addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment");
2930 
2931     const Vector<sp<AaptDir> >& dirs = assets->resDirs();
2932     const size_t K = dirs.size();
2933     for (size_t k=0; k<K; k++) {
2934         const sp<AaptDir>& d = dirs.itemAt(k);
2935         const String8& dirName = d->getLeaf();
2936         Vector<String8> startTags;
2937         const char* startTag = NULL;
2938         const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs = NULL;
2939         if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) {
2940             tagAttrPairs = &kLayoutTagAttrPairs;
2941         } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
2942             startTags.add(String8("PreferenceScreen"));
2943             startTags.add(String8("preference-headers"));
2944             tagAttrPairs = &kXmlTagAttrPairs;
2945         } else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) {
2946             startTags.add(String8("menu"));
2947             tagAttrPairs = NULL;
2948         } else {
2949             continue;
2950         }
2951 
2952         const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles();
2953         const size_t N = groups.size();
2954         for (size_t i=0; i<N; i++) {
2955             const sp<AaptGroup>& group = groups.valueAt(i);
2956             const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles();
2957             const size_t M = files.size();
2958             for (size_t j=0; j<M; j++) {
2959                 err = writeProguardForXml(keep, files.valueAt(j), startTags, tagAttrPairs);
2960                 if (err < 0) {
2961                     return err;
2962                 }
2963             }
2964         }
2965     }
2966     // Handle the overlays
2967     sp<AaptAssets> overlay = assets->getOverlay();
2968     if (overlay.get()) {
2969         return writeProguardForLayouts(keep, overlay);
2970     }
2971 
2972     return NO_ERROR;
2973 }
2974 
2975 status_t
writeProguardFile(Bundle * bundle,const sp<AaptAssets> & assets)2976 writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets)
2977 {
2978     status_t err = -1;
2979 
2980     if (!bundle->getProguardFile()) {
2981         return NO_ERROR;
2982     }
2983 
2984     ProguardKeepSet keep;
2985 
2986     err = writeProguardForAndroidManifest(&keep, assets);
2987     if (err < 0) {
2988         return err;
2989     }
2990 
2991     err = writeProguardForLayouts(&keep, assets);
2992     if (err < 0) {
2993         return err;
2994     }
2995 
2996     FILE* fp = fopen(bundle->getProguardFile(), "w+");
2997     if (fp == NULL) {
2998         fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
2999                 bundle->getProguardFile(), strerror(errno));
3000         return UNKNOWN_ERROR;
3001     }
3002 
3003     const KeyedVector<String8, SortedVector<String8> >& rules = keep.rules;
3004     const size_t N = rules.size();
3005     for (size_t i=0; i<N; i++) {
3006         const SortedVector<String8>& locations = rules.valueAt(i);
3007         const size_t M = locations.size();
3008         for (size_t j=0; j<M; j++) {
3009             fprintf(fp, "# %s\n", locations.itemAt(j).string());
3010         }
3011         fprintf(fp, "%s\n\n", rules.keyAt(i).string());
3012     }
3013     fclose(fp);
3014 
3015     return err;
3016 }
3017 
3018 // Loops through the string paths and writes them to the file pointer
3019 // Each file path is written on its own line with a terminating backslash.
writePathsToFile(const sp<FilePathStore> & files,FILE * fp)3020 status_t writePathsToFile(const sp<FilePathStore>& files, FILE* fp)
3021 {
3022     status_t deps = -1;
3023     for (size_t file_i = 0; file_i < files->size(); ++file_i) {
3024         // Add the full file path to the dependency file
3025         fprintf(fp, "%s \\\n", files->itemAt(file_i).string());
3026         deps++;
3027     }
3028     return deps;
3029 }
3030 
3031 status_t
writeDependencyPreReqs(Bundle * bundle,const sp<AaptAssets> & assets,FILE * fp,bool includeRaw)3032 writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw)
3033 {
3034     status_t deps = -1;
3035     deps += writePathsToFile(assets->getFullResPaths(), fp);
3036     if (includeRaw) {
3037         deps += writePathsToFile(assets->getFullAssetPaths(), fp);
3038     }
3039     return deps;
3040 }
3041