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