OSDN Git Service

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