OSDN Git Service

Merge "docs: Added note - test subscriptions cannot use reserved IDs" into nyc-dev
[android-x86/frameworks-base.git] / tools / aapt / AaptAssets.cpp
1 //
2 // Copyright 2006 The Android Open Source Project
3 //
4
5 #include "AaptAssets.h"
6 #include "AaptConfig.h"
7 #include "AaptUtil.h"
8 #include "Main.h"
9 #include "ResourceFilter.h"
10
11 #include <utils/misc.h>
12 #include <utils/SortedVector.h>
13
14 #include <ctype.h>
15 #include <dirent.h>
16 #include <errno.h>
17
18 static const char* kAssetDir = "assets";
19 static const char* kResourceDir = "res";
20 static const char* kValuesDir = "values";
21 static const char* kMipmapDir = "mipmap";
22 static const char* kInvalidChars = "/\\:";
23 static const size_t kMaxAssetFileName = 100;
24
25 static const String8 kResString(kResourceDir);
26
27 /*
28  * Names of asset files must meet the following criteria:
29  *
30  *  - the filename length must be less than kMaxAssetFileName bytes long
31  *    (and can't be empty)
32  *  - all characters must be 7-bit printable ASCII
33  *  - none of { '/' '\\' ':' }
34  *
35  * Pass in just the filename, not the full path.
36  */
37 static bool validateFileName(const char* fileName)
38 {
39     const char* cp = fileName;
40     size_t len = 0;
41
42     while (*cp != '\0') {
43         if ((*cp & 0x80) != 0)
44             return false;           // reject high ASCII
45         if (*cp < 0x20 || *cp >= 0x7f)
46             return false;           // reject control chars and 0x7f
47         if (strchr(kInvalidChars, *cp) != NULL)
48             return false;           // reject path sep chars
49         cp++;
50         len++;
51     }
52
53     if (len < 1 || len > kMaxAssetFileName)
54         return false;               // reject empty or too long
55
56     return true;
57 }
58
59 // The default to use if no other ignore pattern is defined.
60 const char * const gDefaultIgnoreAssets =
61     "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~";
62 // The ignore pattern that can be passed via --ignore-assets in Main.cpp
63 const char * gUserIgnoreAssets = NULL;
64
65 static bool isHidden(const char *root, const char *path)
66 {
67     // Patterns syntax:
68     // - Delimiter is :
69     // - Entry can start with the flag ! to avoid printing a warning
70     //   about the file being ignored.
71     // - Entry can have the flag "<dir>" to match only directories
72     //   or <file> to match only files. Default is to match both.
73     // - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
74     //   where prefix/suffix must have at least 1 character (so that
75     //   we don't match a '*' catch-all pattern.)
76     // - The special filenames "." and ".." are always ignored.
77     // - Otherwise the full string is matched.
78     // - match is not case-sensitive.
79
80     if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) {
81         return true;
82     }
83
84     const char *delim = ":";
85     const char *p = gUserIgnoreAssets;
86     if (!p || !p[0]) {
87         p = getenv("ANDROID_AAPT_IGNORE");
88     }
89     if (!p || !p[0]) {
90         p = gDefaultIgnoreAssets;
91     }
92     char *patterns = strdup(p);
93
94     bool ignore = false;
95     bool chatty = true;
96     char *matchedPattern = NULL;
97
98     String8 fullPath(root);
99     fullPath.appendPath(path);
100     FileType type = getFileType(fullPath);
101
102     int plen = strlen(path);
103
104     // Note: we don't have strtok_r under mingw.
105     for(char *token = strtok(patterns, delim);
106             !ignore && token != NULL;
107             token = strtok(NULL, delim)) {
108         chatty = token[0] != '!';
109         if (!chatty) token++; // skip !
110         if (strncasecmp(token, "<dir>" , 5) == 0) {
111             if (type != kFileTypeDirectory) continue;
112             token += 5;
113         }
114         if (strncasecmp(token, "<file>", 6) == 0) {
115             if (type != kFileTypeRegular) continue;
116             token += 6;
117         }
118
119         matchedPattern = token;
120         int n = strlen(token);
121
122         if (token[0] == '*') {
123             // Match *suffix
124             token++;
125             n--;
126             if (n <= plen) {
127                 ignore = strncasecmp(token, path + plen - n, n) == 0;
128             }
129         } else if (n > 1 && token[n - 1] == '*') {
130             // Match prefix*
131             ignore = strncasecmp(token, path, n - 1) == 0;
132         } else {
133             ignore = strcasecmp(token, path) == 0;
134         }
135     }
136
137     if (ignore && chatty) {
138         fprintf(stderr, "    (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n",
139                 type == kFileTypeDirectory ? "dir" : "file",
140                 path,
141                 matchedPattern ? matchedPattern : "");
142     }
143
144     free(patterns);
145     return ignore;
146 }
147
148 // =========================================================================
149 // =========================================================================
150 // =========================================================================
151
152 /* static */
153 inline bool isAlpha(const String8& string) {
154      const size_t length = string.length();
155      for (size_t i = 0; i < length; ++i) {
156           if (!isalpha(string[i])) {
157               return false;
158           }
159      }
160
161      return true;
162 }
163
164 /* static */
165 inline bool isNumber(const String8& string) {
166      const size_t length = string.length();
167      for (size_t i = 0; i < length; ++i) {
168           if (!isdigit(string[i])) {
169               return false;
170           }
171      }
172
173      return true;
174 }
175
176 void AaptLocaleValue::setLanguage(const char* languageChars) {
177      size_t i = 0;
178      while ((*languageChars) != '\0' && i < sizeof(language)/sizeof(language[0])) {
179           language[i++] = tolower(*languageChars);
180           languageChars++;
181      }
182 }
183
184 void AaptLocaleValue::setRegion(const char* regionChars) {
185     size_t i = 0;
186     while ((*regionChars) != '\0' && i < sizeof(region)/sizeof(region[0])) {
187          region[i++] = toupper(*regionChars);
188          regionChars++;
189     }
190 }
191
192 void AaptLocaleValue::setScript(const char* scriptChars) {
193     size_t i = 0;
194     while ((*scriptChars) != '\0' && i < sizeof(script)/sizeof(script[0])) {
195          if (i == 0) {
196              script[i++] = toupper(*scriptChars);
197          } else {
198              script[i++] = tolower(*scriptChars);
199          }
200          scriptChars++;
201     }
202 }
203
204 void AaptLocaleValue::setVariant(const char* variantChars) {
205      size_t i = 0;
206      while ((*variantChars) != '\0' && i < sizeof(variant)/sizeof(variant[0])) {
207           variant[i++] = *variantChars;
208           variantChars++;
209      }
210 }
211
212 bool AaptLocaleValue::initFromFilterString(const String8& str) {
213      // A locale (as specified in the filter) is an underscore separated name such
214      // as "en_US", "en_Latn_US", or "en_US_POSIX".
215      Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '_');
216
217      const int numTags = parts.size();
218      bool valid = false;
219      if (numTags >= 1) {
220          const String8& lang = parts[0];
221          if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
222              setLanguage(lang.string());
223              valid = true;
224          }
225      }
226
227      if (!valid || numTags == 1) {
228          return valid;
229      }
230
231      // At this point, valid == true && numTags > 1.
232      const String8& part2 = parts[1];
233      if ((part2.length() == 2 && isAlpha(part2)) ||
234          (part2.length() == 3 && isNumber(part2))) {
235          setRegion(part2.string());
236      } else if (part2.length() == 4 && isAlpha(part2)) {
237          setScript(part2.string());
238      } else if (part2.length() >= 4 && part2.length() <= 8) {
239          setVariant(part2.string());
240      } else {
241          valid = false;
242      }
243
244      if (!valid || numTags == 2) {
245          return valid;
246      }
247
248      // At this point, valid == true && numTags > 1.
249      const String8& part3 = parts[2];
250      if (((part3.length() == 2 && isAlpha(part3)) ||
251          (part3.length() == 3 && isNumber(part3))) && script[0]) {
252          setRegion(part3.string());
253      } else if (part3.length() >= 4 && part3.length() <= 8) {
254          setVariant(part3.string());
255      } else {
256          valid = false;
257      }
258
259      if (!valid || numTags == 3) {
260          return valid;
261      }
262
263      const String8& part4 = parts[3];
264      if (part4.length() >= 4 && part4.length() <= 8) {
265          setVariant(part4.string());
266      } else {
267          valid = false;
268      }
269
270      if (!valid || numTags > 4) {
271          return false;
272      }
273
274      return true;
275 }
276
277 int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int startIndex) {
278     const int size = parts.size();
279     int currentIndex = startIndex;
280
281     String8 part = parts[currentIndex];
282     if (part[0] == 'b' && part[1] == '+') {
283         // This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags,
284         // except that the separator is "+" and not "-".
285         Vector<String8> subtags = AaptUtil::splitAndLowerCase(part, '+');
286         subtags.removeItemsAt(0);
287         if (subtags.size() == 1) {
288             setLanguage(subtags[0]);
289         } else if (subtags.size() == 2) {
290             setLanguage(subtags[0]);
291
292             // The second tag can either be a region, a variant or a script.
293             switch (subtags[1].size()) {
294                 case 2:
295                 case 3:
296                     setRegion(subtags[1]);
297                     break;
298                 case 4:
299                     if (isAlpha(subtags[1])) {
300                         setScript(subtags[1]);
301                         break;
302                     }
303                     // This is not alphabetical, so we fall through to variant
304                 case 5:
305                 case 6:
306                 case 7:
307                 case 8:
308                     setVariant(subtags[1]);
309                     break;
310                 default:
311                     fprintf(stderr, "ERROR: Invalid BCP 47 tag in directory name %s\n",
312                             part.string());
313                     return -1;
314             }
315         } else if (subtags.size() == 3) {
316             // The language is always the first subtag.
317             setLanguage(subtags[0]);
318
319             // The second subtag can either be a script or a region code.
320             // If its size is 4, it's a script code, else it's a region code.
321             bool hasRegion = false;
322             if (subtags[1].size() == 4) {
323                 setScript(subtags[1]);
324             } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
325                 setRegion(subtags[1]);
326                 hasRegion = true;
327             } else {
328                 fprintf(stderr, "ERROR: Invalid BCP 47 tag in directory name %s\n", part.string());
329                 return -1;
330             }
331
332             // The third tag can either be a region code (if the second tag was
333             // a script), else a variant code.
334             if (subtags[2].size() >= 4) {
335                 setVariant(subtags[2]);
336             } else {
337                 setRegion(subtags[2]);
338             }
339         } else if (subtags.size() == 4) {
340             setLanguage(subtags[0]);
341             setScript(subtags[1]);
342             setRegion(subtags[2]);
343             setVariant(subtags[3]);
344         } else {
345             fprintf(stderr, "ERROR: Invalid BCP 47 tag in directory name: %s\n", part.string());
346             return -1;
347         }
348
349         return ++currentIndex;
350     } else {
351         if ((part.length() == 2 || part.length() == 3)
352                && isAlpha(part) && strcmp("car", part.string())) {
353             setLanguage(part);
354             if (++currentIndex == size) {
355                 return size;
356             }
357         } else {
358             return currentIndex;
359         }
360
361         part = parts[currentIndex];
362         if (part.string()[0] == 'r' && part.length() == 3) {
363             setRegion(part.string() + 1);
364             if (++currentIndex == size) {
365                 return size;
366             }
367         }
368     }
369
370     return currentIndex;
371 }
372
373 void AaptLocaleValue::initFromResTable(const ResTable_config& config) {
374     config.unpackLanguage(language);
375     config.unpackRegion(region);
376     if (config.localeScript[0] && !config.localeScriptWasComputed) {
377         memcpy(script, config.localeScript, sizeof(config.localeScript));
378     }
379
380     if (config.localeVariant[0]) {
381         memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
382     }
383 }
384
385 void AaptLocaleValue::writeTo(ResTable_config* out) const {
386     out->packLanguage(language);
387     out->packRegion(region);
388
389     if (script[0]) {
390         memcpy(out->localeScript, script, sizeof(out->localeScript));
391     }
392
393     if (variant[0]) {
394         memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
395     }
396 }
397
398 bool
399 AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
400 {
401     const char* q = strchr(dir, '-');
402     size_t typeLen;
403     if (q != NULL) {
404         typeLen = q - dir;
405     } else {
406         typeLen = strlen(dir);
407     }
408
409     String8 type(dir, typeLen);
410     if (!isValidResourceType(type)) {
411         return false;
412     }
413
414     if (q != NULL) {
415         if (!AaptConfig::parse(String8(q + 1), &mParams)) {
416             return false;
417         }
418     }
419
420     *resType = type;
421     return true;
422 }
423
424 String8
425 AaptGroupEntry::toDirName(const String8& resType) const
426 {
427     String8 s = resType;
428     String8 params = mParams.toString();
429     if (params.length() > 0) {
430         if (s.length() > 0) {
431             s += "-";
432         }
433         s += params;
434     }
435     return s;
436 }
437
438
439 // =========================================================================
440 // =========================================================================
441 // =========================================================================
442
443 void* AaptFile::editData(size_t size)
444 {
445     if (size <= mBufferSize) {
446         mDataSize = size;
447         return mData;
448     }
449     size_t allocSize = (size*3)/2;
450     void* buf = realloc(mData, allocSize);
451     if (buf == NULL) {
452         return NULL;
453     }
454     mData = buf;
455     mDataSize = size;
456     mBufferSize = allocSize;
457     return buf;
458 }
459
460 void* AaptFile::editDataInRange(size_t offset, size_t size)
461 {
462     return (void*)(((uint8_t*) editData(offset + size)) + offset);
463 }
464
465 void* AaptFile::editData(size_t* outSize)
466 {
467     if (outSize) {
468         *outSize = mDataSize;
469     }
470     return mData;
471 }
472
473 void* AaptFile::padData(size_t wordSize)
474 {
475     const size_t extra = mDataSize%wordSize;
476     if (extra == 0) {
477         return mData;
478     }
479
480     size_t initial = mDataSize;
481     void* data = editData(initial+(wordSize-extra));
482     if (data != NULL) {
483         memset(((uint8_t*)data) + initial, 0, wordSize-extra);
484     }
485     return data;
486 }
487
488 status_t AaptFile::writeData(const void* data, size_t size)
489 {
490     size_t end = mDataSize;
491     size_t total = size + end;
492     void* buf = editData(total);
493     if (buf == NULL) {
494         return UNKNOWN_ERROR;
495     }
496     memcpy(((char*)buf)+end, data, size);
497     return NO_ERROR;
498 }
499
500 void AaptFile::clearData()
501 {
502     if (mData != NULL) free(mData);
503     mData = NULL;
504     mDataSize = 0;
505     mBufferSize = 0;
506 }
507
508 String8 AaptFile::getPrintableSource() const
509 {
510     if (hasData()) {
511         String8 name(mGroupEntry.toDirName(String8()));
512         name.appendPath(mPath);
513         name.append(" #generated");
514         return name;
515     }
516     return mSourceFile;
517 }
518
519 // =========================================================================
520 // =========================================================================
521 // =========================================================================
522
523 status_t AaptGroup::addFile(const sp<AaptFile>& file, const bool overwriteDuplicate)
524 {
525     ssize_t index = mFiles.indexOfKey(file->getGroupEntry());
526     if (index >= 0 && overwriteDuplicate) {
527         fprintf(stderr, "warning: overwriting '%s' with '%s'\n",
528                 mFiles[index]->getSourceFile().string(),
529                 file->getSourceFile().string());
530         removeFile(index);
531         index = -1;
532     }
533
534     if (index < 0) {
535         file->mPath = mPath;
536         mFiles.add(file->getGroupEntry(), file);
537         return NO_ERROR;
538     }
539
540     // Check if the version is automatically applied. This is a common source of
541     // error.
542     ConfigDescription withoutVersion = file->getGroupEntry().toParams();
543     withoutVersion.version = 0;
544     AaptConfig::applyVersionForCompatibility(&withoutVersion);
545
546     const sp<AaptFile>& originalFile = mFiles.valueAt(index);
547     SourcePos(file->getSourceFile(), -1)
548             .error("Duplicate file.\n%s: Original is here. %s",
549                    originalFile->getPrintableSource().string(),
550                    (withoutVersion.version != 0) ? "The version qualifier may be implied." : "");
551     return UNKNOWN_ERROR;
552 }
553
554 void AaptGroup::removeFile(size_t index)
555 {
556         mFiles.removeItemsAt(index);
557 }
558
559 void AaptGroup::print(const String8& prefix) const
560 {
561     printf("%s%s\n", prefix.string(), getPath().string());
562     const size_t N=mFiles.size();
563     size_t i;
564     for (i=0; i<N; i++) {
565         sp<AaptFile> file = mFiles.valueAt(i);
566         const AaptGroupEntry& e = file->getGroupEntry();
567         if (file->hasData()) {
568             printf("%s  Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
569                     (int)file->getSize());
570         } else {
571             printf("%s  Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
572                     file->getPrintableSource().string());
573         }
574         //printf("%s  File Group Entry: %s\n", prefix.string(),
575         //        file->getGroupEntry().toDirName(String8()).string());
576     }
577 }
578
579 String8 AaptGroup::getPrintableSource() const
580 {
581     if (mFiles.size() > 0) {
582         // Arbitrarily pull the first source file out of the list.
583         return mFiles.valueAt(0)->getPrintableSource();
584     }
585
586     // Should never hit this case, but to be safe...
587     return getPath();
588
589 }
590
591 // =========================================================================
592 // =========================================================================
593 // =========================================================================
594
595 status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
596 {
597     if (mFiles.indexOfKey(name) >= 0) {
598         return ALREADY_EXISTS;
599     }
600     mFiles.add(name, file);
601     return NO_ERROR;
602 }
603
604 status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
605 {
606     if (mDirs.indexOfKey(name) >= 0) {
607         return ALREADY_EXISTS;
608     }
609     mDirs.add(name, dir);
610     return NO_ERROR;
611 }
612
613 sp<AaptDir> AaptDir::makeDir(const String8& path)
614 {
615     String8 name;
616     String8 remain = path;
617
618     sp<AaptDir> subdir = this;
619     while (name = remain.walkPath(&remain), remain != "") {
620         subdir = subdir->makeDir(name);
621     }
622
623     ssize_t i = subdir->mDirs.indexOfKey(name);
624     if (i >= 0) {
625         return subdir->mDirs.valueAt(i);
626     }
627     sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
628     subdir->mDirs.add(name, dir);
629     return dir;
630 }
631
632 void AaptDir::removeFile(const String8& name)
633 {
634     mFiles.removeItem(name);
635 }
636
637 void AaptDir::removeDir(const String8& name)
638 {
639     mDirs.removeItem(name);
640 }
641
642 status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file,
643         const bool overwrite)
644 {
645     sp<AaptGroup> group;
646     if (mFiles.indexOfKey(leafName) >= 0) {
647         group = mFiles.valueFor(leafName);
648     } else {
649         group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
650         mFiles.add(leafName, group);
651     }
652
653     return group->addFile(file, overwrite);
654 }
655
656 ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
657                             const AaptGroupEntry& kind, const String8& resType,
658                             sp<FilePathStore>& fullResPaths, const bool overwrite)
659 {
660     Vector<String8> fileNames;
661     {
662         DIR* dir = NULL;
663
664         dir = opendir(srcDir.string());
665         if (dir == NULL) {
666             fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
667             return UNKNOWN_ERROR;
668         }
669
670         /*
671          * Slurp the filenames out of the directory.
672          */
673         while (1) {
674             struct dirent* entry;
675
676             entry = readdir(dir);
677             if (entry == NULL)
678                 break;
679
680             if (isHidden(srcDir.string(), entry->d_name))
681                 continue;
682
683             String8 name(entry->d_name);
684             fileNames.add(name);
685             // Add fully qualified path for dependency purposes
686             // if we're collecting them
687             if (fullResPaths != NULL) {
688                 fullResPaths->add(srcDir.appendPathCopy(name));
689             }
690         }
691         closedir(dir);
692     }
693
694     ssize_t count = 0;
695
696     /*
697      * Stash away the files and recursively descend into subdirectories.
698      */
699     const size_t N = fileNames.size();
700     size_t i;
701     for (i = 0; i < N; i++) {
702         String8 pathName(srcDir);
703         FileType type;
704
705         pathName.appendPath(fileNames[i].string());
706         type = getFileType(pathName.string());
707         if (type == kFileTypeDirectory) {
708             sp<AaptDir> subdir;
709             bool notAdded = false;
710             if (mDirs.indexOfKey(fileNames[i]) >= 0) {
711                 subdir = mDirs.valueFor(fileNames[i]);
712             } else {
713                 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
714                 notAdded = true;
715             }
716             ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
717                                                 resType, fullResPaths, overwrite);
718             if (res < NO_ERROR) {
719                 return res;
720             }
721             if (res > 0 && notAdded) {
722                 mDirs.add(fileNames[i], subdir);
723             }
724             count += res;
725         } else if (type == kFileTypeRegular) {
726             sp<AaptFile> file = new AaptFile(pathName, kind, resType);
727             status_t err = addLeafFile(fileNames[i], file, overwrite);
728             if (err != NO_ERROR) {
729                 return err;
730             }
731
732             count++;
733
734         } else {
735             if (bundle->getVerbose())
736                 printf("   (ignoring non-file/dir '%s')\n", pathName.string());
737         }
738     }
739
740     return count;
741 }
742
743 status_t AaptDir::validate() const
744 {
745     const size_t NF = mFiles.size();
746     const size_t ND = mDirs.size();
747     size_t i;
748     for (i = 0; i < NF; i++) {
749         if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
750             SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
751                     "Invalid filename.  Unable to add.");
752             return UNKNOWN_ERROR;
753         }
754
755         size_t j;
756         for (j = i+1; j < NF; j++) {
757             if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
758                            mFiles.valueAt(j)->getLeaf().string()) == 0) {
759                 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
760                         "File is case-insensitive equivalent to: %s",
761                         mFiles.valueAt(j)->getPrintableSource().string());
762                 return UNKNOWN_ERROR;
763             }
764
765             // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
766             // (this is mostly caught by the "marked" stuff, below)
767         }
768
769         for (j = 0; j < ND; j++) {
770             if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
771                            mDirs.valueAt(j)->getLeaf().string()) == 0) {
772                 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
773                         "File conflicts with dir from: %s",
774                         mDirs.valueAt(j)->getPrintableSource().string());
775                 return UNKNOWN_ERROR;
776             }
777         }
778     }
779
780     for (i = 0; i < ND; i++) {
781         if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
782             SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
783                     "Invalid directory name, unable to add.");
784             return UNKNOWN_ERROR;
785         }
786
787         size_t j;
788         for (j = i+1; j < ND; j++) {
789             if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
790                            mDirs.valueAt(j)->getLeaf().string()) == 0) {
791                 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
792                         "Directory is case-insensitive equivalent to: %s",
793                         mDirs.valueAt(j)->getPrintableSource().string());
794                 return UNKNOWN_ERROR;
795             }
796         }
797
798         status_t err = mDirs.valueAt(i)->validate();
799         if (err != NO_ERROR) {
800             return err;
801         }
802     }
803
804     return NO_ERROR;
805 }
806
807 void AaptDir::print(const String8& prefix) const
808 {
809     const size_t ND=getDirs().size();
810     size_t i;
811     for (i=0; i<ND; i++) {
812         getDirs().valueAt(i)->print(prefix);
813     }
814
815     const size_t NF=getFiles().size();
816     for (i=0; i<NF; i++) {
817         getFiles().valueAt(i)->print(prefix);
818     }
819 }
820
821 String8 AaptDir::getPrintableSource() const
822 {
823     if (mFiles.size() > 0) {
824         // Arbitrarily pull the first file out of the list as the source dir.
825         return mFiles.valueAt(0)->getPrintableSource().getPathDir();
826     }
827     if (mDirs.size() > 0) {
828         // Or arbitrarily pull the first dir out of the list as the source dir.
829         return mDirs.valueAt(0)->getPrintableSource().getPathDir();
830     }
831
832     // Should never hit this case, but to be safe...
833     return mPath;
834
835 }
836
837 // =========================================================================
838 // =========================================================================
839 // =========================================================================
840
841 status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
842 {
843     status_t err = NO_ERROR;
844     size_t N = javaSymbols->mSymbols.size();
845     for (size_t i=0; i<N; i++) {
846         const String8& name = javaSymbols->mSymbols.keyAt(i);
847         const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
848         ssize_t pos = mSymbols.indexOfKey(name);
849         if (pos < 0) {
850             entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
851             err = UNKNOWN_ERROR;
852             continue;
853         }
854         //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
855         //        i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
856         mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
857     }
858
859     N = javaSymbols->mNestedSymbols.size();
860     for (size_t i=0; i<N; i++) {
861         const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
862         const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
863         ssize_t pos = mNestedSymbols.indexOfKey(name);
864         if (pos < 0) {
865             SourcePos pos;
866             pos.error("Java symbol dir %s not defined\n", name.string());
867             err = UNKNOWN_ERROR;
868             continue;
869         }
870         //printf("**** applying java symbols in dir %s\n", name.string());
871         status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
872         if (myerr != NO_ERROR) {
873             err = myerr;
874         }
875     }
876
877     return err;
878 }
879
880 // =========================================================================
881 // =========================================================================
882 // =========================================================================
883
884 AaptAssets::AaptAssets()
885     : AaptDir(String8(), String8()),
886       mHavePrivateSymbols(false),
887       mChanged(false), mHaveIncludedAssets(false),
888       mRes(NULL) {}
889
890 const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
891     if (mChanged) {
892     }
893     return mGroupEntries;
894 }
895
896 status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
897 {
898     mChanged = true;
899     return AaptDir::addFile(name, file);
900 }
901
902 sp<AaptFile> AaptAssets::addFile(
903         const String8& filePath, const AaptGroupEntry& entry,
904         const String8& srcDir, sp<AaptGroup>* outGroup,
905         const String8& resType)
906 {
907     sp<AaptDir> dir = this;
908     sp<AaptGroup> group;
909     sp<AaptFile> file;
910     String8 root, remain(filePath), partialPath;
911     while (remain.length() > 0) {
912         root = remain.walkPath(&remain);
913         partialPath.appendPath(root);
914
915         const String8 rootStr(root);
916
917         if (remain.length() == 0) {
918             ssize_t i = dir->getFiles().indexOfKey(rootStr);
919             if (i >= 0) {
920                 group = dir->getFiles().valueAt(i);
921             } else {
922                 group = new AaptGroup(rootStr, filePath);
923                 status_t res = dir->addFile(rootStr, group);
924                 if (res != NO_ERROR) {
925                     return NULL;
926                 }
927             }
928             file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
929             status_t res = group->addFile(file);
930             if (res != NO_ERROR) {
931                 return NULL;
932             }
933             break;
934
935         } else {
936             ssize_t i = dir->getDirs().indexOfKey(rootStr);
937             if (i >= 0) {
938                 dir = dir->getDirs().valueAt(i);
939             } else {
940                 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
941                 status_t res = dir->addDir(rootStr, subdir);
942                 if (res != NO_ERROR) {
943                     return NULL;
944                 }
945                 dir = subdir;
946             }
947         }
948     }
949
950     mGroupEntries.add(entry);
951     if (outGroup) *outGroup = group;
952     return file;
953 }
954
955 void AaptAssets::addResource(const String8& leafName, const String8& path,
956                 const sp<AaptFile>& file, const String8& resType)
957 {
958     sp<AaptDir> res = AaptDir::makeDir(kResString);
959     String8 dirname = file->getGroupEntry().toDirName(resType);
960     sp<AaptDir> subdir = res->makeDir(dirname);
961     sp<AaptGroup> grr = new AaptGroup(leafName, path);
962     grr->addFile(file);
963
964     subdir->addFile(leafName, grr);
965 }
966
967
968 ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
969 {
970     int count;
971     int totalCount = 0;
972     FileType type;
973     const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
974     const size_t dirCount =resDirs.size();
975     sp<AaptAssets> current = this;
976
977     const int N = bundle->getFileSpecCount();
978
979     /*
980      * If a package manifest was specified, include that first.
981      */
982     if (bundle->getAndroidManifestFile() != NULL) {
983         // place at root of zip.
984         String8 srcFile(bundle->getAndroidManifestFile());
985         addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
986                 NULL, String8());
987         totalCount++;
988     }
989
990     /*
991      * If a directory of custom assets was supplied, slurp 'em up.
992      */
993     const Vector<const char*>& assetDirs = bundle->getAssetSourceDirs();
994     const int AN = assetDirs.size();
995     for (int i = 0; i < AN; i++) {
996         FileType type = getFileType(assetDirs[i]);
997         if (type == kFileTypeNonexistent) {
998             fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDirs[i]);
999             return UNKNOWN_ERROR;
1000         }
1001         if (type != kFileTypeDirectory) {
1002             fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDirs[i]);
1003             return UNKNOWN_ERROR;
1004         }
1005
1006         String8 assetRoot(assetDirs[i]);
1007         sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
1008         AaptGroupEntry group;
1009         count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
1010                                             String8(), mFullAssetPaths, true);
1011         if (count < 0) {
1012             totalCount = count;
1013             goto bail;
1014         }
1015         if (count > 0) {
1016             mGroupEntries.add(group);
1017         }
1018         totalCount += count;
1019
1020         if (bundle->getVerbose()) {
1021             printf("Found %d custom asset file%s in %s\n",
1022                    count, (count==1) ? "" : "s", assetDirs[i]);
1023         }
1024     }
1025
1026     /*
1027      * If a directory of resource-specific assets was supplied, slurp 'em up.
1028      */
1029     for (size_t i=0; i<dirCount; i++) {
1030         const char *res = resDirs[i];
1031         if (res) {
1032             type = getFileType(res);
1033             if (type == kFileTypeNonexistent) {
1034                 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
1035                 return UNKNOWN_ERROR;
1036             }
1037             if (type == kFileTypeDirectory) {
1038                 if (i>0) {
1039                     sp<AaptAssets> nextOverlay = new AaptAssets();
1040                     current->setOverlay(nextOverlay);
1041                     current = nextOverlay;
1042                     current->setFullResPaths(mFullResPaths);
1043                 }
1044                 count = current->slurpResourceTree(bundle, String8(res));
1045                 if (i > 0 && count > 0) {
1046                   count = current->filter(bundle);
1047                 }
1048
1049                 if (count < 0) {
1050                     totalCount = count;
1051                     goto bail;
1052                 }
1053                 totalCount += count;
1054             }
1055             else {
1056                 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
1057                 return UNKNOWN_ERROR;
1058             }
1059         }
1060         
1061     }
1062     /*
1063      * Now do any additional raw files.
1064      */
1065     for (int arg=0; arg<N; arg++) {
1066         const char* assetDir = bundle->getFileSpecEntry(arg);
1067
1068         FileType type = getFileType(assetDir);
1069         if (type == kFileTypeNonexistent) {
1070             fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
1071             return UNKNOWN_ERROR;
1072         }
1073         if (type != kFileTypeDirectory) {
1074             fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1075             return UNKNOWN_ERROR;
1076         }
1077
1078         String8 assetRoot(assetDir);
1079
1080         if (bundle->getVerbose())
1081             printf("Processing raw dir '%s'\n", (const char*) assetDir);
1082
1083         /*
1084          * Do a recursive traversal of subdir tree.  We don't make any
1085          * guarantees about ordering, so we're okay with an inorder search
1086          * using whatever order the OS happens to hand back to us.
1087          */
1088         count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
1089         if (count < 0) {
1090             /* failure; report error and remove archive */
1091             totalCount = count;
1092             goto bail;
1093         }
1094         totalCount += count;
1095
1096         if (bundle->getVerbose())
1097             printf("Found %d asset file%s in %s\n",
1098                    count, (count==1) ? "" : "s", assetDir);
1099     }
1100
1101     count = validate();
1102     if (count != NO_ERROR) {
1103         totalCount = count;
1104         goto bail;
1105     }
1106
1107     count = filter(bundle);
1108     if (count != NO_ERROR) {
1109         totalCount = count;
1110         goto bail;
1111     }
1112
1113 bail:
1114     return totalCount;
1115 }
1116
1117 ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
1118                                     const AaptGroupEntry& kind,
1119                                     const String8& resType,
1120                                     sp<FilePathStore>& fullResPaths,
1121                                     const bool overwrite)
1122 {
1123     ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths, overwrite);
1124     if (res > 0) {
1125         mGroupEntries.add(kind);
1126     }
1127
1128     return res;
1129 }
1130
1131 ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
1132 {
1133     ssize_t err = 0;
1134
1135     DIR* dir = opendir(srcDir.string());
1136     if (dir == NULL) {
1137         fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1138         return UNKNOWN_ERROR;
1139     }
1140
1141     status_t count = 0;
1142
1143     /*
1144      * Run through the directory, looking for dirs that match the
1145      * expected pattern.
1146      */
1147     while (1) {
1148         struct dirent* entry = readdir(dir);
1149         if (entry == NULL) {
1150             break;
1151         }
1152
1153         if (isHidden(srcDir.string(), entry->d_name)) {
1154             continue;
1155         }
1156
1157         String8 subdirName(srcDir);
1158         subdirName.appendPath(entry->d_name);
1159
1160         AaptGroupEntry group;
1161         String8 resType;
1162         bool b = group.initFromDirName(entry->d_name, &resType);
1163         if (!b) {
1164             fprintf(stderr, "invalid resource directory name: %s %s\n", srcDir.string(),
1165                     entry->d_name);
1166             err = -1;
1167             continue;
1168         }
1169
1170         if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
1171             int maxResInt = atoi(bundle->getMaxResVersion());
1172             const char *verString = group.getVersionString().string();
1173             int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
1174             if (dirVersionInt > maxResInt) {
1175               fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
1176               continue;
1177             }
1178         }
1179
1180         FileType type = getFileType(subdirName.string());
1181
1182         if (type == kFileTypeDirectory) {
1183             sp<AaptDir> dir = makeDir(resType);
1184             ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
1185                                                 resType, mFullResPaths);
1186             if (res < 0) {
1187                 count = res;
1188                 goto bail;
1189             }
1190             if (res > 0) {
1191                 mGroupEntries.add(group);
1192                 count += res;
1193             }
1194
1195             // Only add this directory if we don't already have a resource dir
1196             // for the current type.  This ensures that we only add the dir once
1197             // for all configs.
1198             sp<AaptDir> rdir = resDir(resType);
1199             if (rdir == NULL) {
1200                 mResDirs.add(dir);
1201             }
1202         } else {
1203             if (bundle->getVerbose()) {
1204                 fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
1205             }
1206         }
1207     }
1208
1209 bail:
1210     closedir(dir);
1211     dir = NULL;
1212
1213     if (err != 0) {
1214         return err;
1215     }
1216     return count;
1217 }
1218
1219 ssize_t
1220 AaptAssets::slurpResourceZip(Bundle* /* bundle */, const char* filename)
1221 {
1222     int count = 0;
1223     SortedVector<AaptGroupEntry> entries;
1224
1225     ZipFile* zip = new ZipFile;
1226     status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
1227     if (err != NO_ERROR) {
1228         fprintf(stderr, "error opening zip file %s\n", filename);
1229         count = err;
1230         delete zip;
1231         return -1;
1232     }
1233
1234     const int N = zip->getNumEntries();
1235     for (int i=0; i<N; i++) {
1236         ZipEntry* entry = zip->getEntryByIndex(i);
1237         if (entry->getDeleted()) {
1238             continue;
1239         }
1240
1241         String8 entryName(entry->getFileName());
1242
1243         String8 dirName = entryName.getPathDir();
1244         sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
1245
1246         String8 resType;
1247         AaptGroupEntry kind;
1248
1249         String8 remain;
1250         if (entryName.walkPath(&remain) == kResourceDir) {
1251             // these are the resources, pull their type out of the directory name
1252             kind.initFromDirName(remain.walkPath().string(), &resType);
1253         } else {
1254             // these are untyped and don't have an AaptGroupEntry
1255         }
1256         if (entries.indexOf(kind) < 0) {
1257             entries.add(kind);
1258             mGroupEntries.add(kind);
1259         }
1260
1261         // use the one from the zip file if they both exist.
1262         dir->removeFile(entryName.getPathLeaf());
1263
1264         sp<AaptFile> file = new AaptFile(entryName, kind, resType);
1265         status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
1266         if (err != NO_ERROR) {
1267             fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
1268             count = err;
1269             goto bail;
1270         }
1271         file->setCompressionMethod(entry->getCompressionMethod());
1272
1273 #if 0
1274         if (entryName == "AndroidManifest.xml") {
1275             printf("AndroidManifest.xml\n");
1276         }
1277         printf("\n\nfile: %s\n", entryName.string());
1278 #endif
1279
1280         size_t len = entry->getUncompressedLen();
1281         void* data = zip->uncompress(entry);
1282         void* buf = file->editData(len);
1283         memcpy(buf, data, len);
1284
1285 #if 0
1286         const int OFF = 0;
1287         const unsigned char* p = (unsigned char*)data;
1288         const unsigned char* end = p+len;
1289         p += OFF;
1290         for (int i=0; i<32 && p < end; i++) {
1291             printf("0x%03x ", i*0x10 + OFF);
1292             for (int j=0; j<0x10 && p < end; j++) {
1293                 printf(" %02x", *p);
1294                 p++;
1295             }
1296             printf("\n");
1297         }
1298 #endif
1299
1300         free(data);
1301
1302         count++;
1303     }
1304
1305 bail:
1306     delete zip;
1307     return count;
1308 }
1309
1310 status_t AaptAssets::filter(Bundle* bundle)
1311 {
1312     WeakResourceFilter reqFilter;
1313     status_t err = reqFilter.parse(bundle->getConfigurations());
1314     if (err != NO_ERROR) {
1315         return err;
1316     }
1317
1318     uint32_t preferredDensity = 0;
1319     if (bundle->getPreferredDensity().size() > 0) {
1320         ResTable_config preferredConfig;
1321         if (!AaptConfig::parseDensity(bundle->getPreferredDensity().string(), &preferredConfig)) {
1322             fprintf(stderr, "Error parsing preferred density: %s\n",
1323                     bundle->getPreferredDensity().string());
1324             return UNKNOWN_ERROR;
1325         }
1326         preferredDensity = preferredConfig.density;
1327     }
1328
1329     if (reqFilter.isEmpty() && preferredDensity == 0) {
1330         return NO_ERROR;
1331     }
1332
1333     if (bundle->getVerbose()) {
1334         if (!reqFilter.isEmpty()) {
1335             printf("Applying required filter: %s\n",
1336                     bundle->getConfigurations().string());
1337         }
1338         if (preferredDensity > 0) {
1339             printf("Applying preferred density filter: %s\n",
1340                     bundle->getPreferredDensity().string());
1341         }
1342     }
1343
1344     const Vector<sp<AaptDir> >& resdirs = mResDirs;
1345     const size_t ND = resdirs.size();
1346     for (size_t i=0; i<ND; i++) {
1347         const sp<AaptDir>& dir = resdirs.itemAt(i);
1348         if (dir->getLeaf() == kValuesDir) {
1349             // The "value" dir is special since a single file defines
1350             // multiple resources, so we can not do filtering on the
1351             // files themselves.
1352             continue;
1353         }
1354         if (dir->getLeaf() == kMipmapDir) {
1355             // We also skip the "mipmap" directory, since the point of this
1356             // is to include all densities without stripping.  If you put
1357             // other configurations in here as well they won't be stripped
1358             // either...  So don't do that.  Seriously.  What is wrong with you?
1359             continue;
1360         }
1361
1362         const size_t NG = dir->getFiles().size();
1363         for (size_t j=0; j<NG; j++) {
1364             sp<AaptGroup> grp = dir->getFiles().valueAt(j);
1365
1366             // First remove any configurations we know we don't need.
1367             for (size_t k=0; k<grp->getFiles().size(); k++) {
1368                 sp<AaptFile> file = grp->getFiles().valueAt(k);
1369                 if (k == 0 && grp->getFiles().size() == 1) {
1370                     // If this is the only file left, we need to keep it.
1371                     // Otherwise the resource IDs we are using will be inconsistent
1372                     // with what we get when not stripping.  Sucky, but at least
1373                     // for now we can rely on the back-end doing another filtering
1374                     // pass to take this out and leave us with this resource name
1375                     // containing no entries.
1376                     continue;
1377                 }
1378                 if (file->getPath().getPathExtension() == ".xml") {
1379                     // We can't remove .xml files at this point, because when
1380                     // we parse them they may add identifier resources, so
1381                     // removing them can cause our resource identifiers to
1382                     // become inconsistent.
1383                     continue;
1384                 }
1385                 const ResTable_config& config(file->getGroupEntry().toParams());
1386                 if (!reqFilter.match(config)) {
1387                     if (bundle->getVerbose()) {
1388                         printf("Pruning unneeded resource: %s\n",
1389                                 file->getPrintableSource().string());
1390                     }
1391                     grp->removeFile(k);
1392                     k--;
1393                 }
1394             }
1395
1396             // Quick check: no preferred filters, nothing more to do.
1397             if (preferredDensity == 0) {
1398                 continue;
1399             }
1400
1401             // Get the preferred density if there is one. We do not match exactly for density.
1402             // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
1403             // pick xhdpi.
1404             for (size_t k=0; k<grp->getFiles().size(); k++) {
1405                 sp<AaptFile> file = grp->getFiles().valueAt(k);
1406                 if (k == 0 && grp->getFiles().size() == 1) {
1407                     // If this is the only file left, we need to keep it.
1408                     // Otherwise the resource IDs we are using will be inconsistent
1409                     // with what we get when not stripping.  Sucky, but at least
1410                     // for now we can rely on the back-end doing another filtering
1411                     // pass to take this out and leave us with this resource name
1412                     // containing no entries.
1413                     continue;
1414                 }
1415                 if (file->getPath().getPathExtension() == ".xml") {
1416                     // We can't remove .xml files at this point, because when
1417                     // we parse them they may add identifier resources, so
1418                     // removing them can cause our resource identifiers to
1419                     // become inconsistent.
1420                     continue;
1421                 }
1422                 const ResTable_config& config(file->getGroupEntry().toParams());
1423                 if (config.density != 0 && config.density != preferredDensity) {
1424                     // This is a resource we would prefer not to have.  Check
1425                     // to see if have a similar variation that we would like
1426                     // to have and, if so, we can drop it.
1427                     uint32_t bestDensity = config.density;
1428
1429                     for (size_t m=0; m<grp->getFiles().size(); m++) {
1430                         if (m == k) {
1431                             continue;
1432                         }
1433
1434                         sp<AaptFile> mfile = grp->getFiles().valueAt(m);
1435                         const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
1436                         if (AaptConfig::isSameExcept(config, mconfig, ResTable_config::CONFIG_DENSITY)) {
1437                             // See if there is a better density resource
1438                             if (mconfig.density < bestDensity &&
1439                                     mconfig.density >= preferredDensity &&
1440                                     bestDensity > preferredDensity) {
1441                                 // This density is our preferred density, or between our best density and
1442                                 // the preferred density, therefore it is better.
1443                                 bestDensity = mconfig.density;
1444                             } else if (mconfig.density > bestDensity &&
1445                                     bestDensity < preferredDensity) {
1446                                 // This density is better than our best density and
1447                                 // our best density was smaller than our preferred
1448                                 // density, so it is better.
1449                                 bestDensity = mconfig.density;
1450                             }
1451                         }
1452                     }
1453
1454                     if (bestDensity != config.density) {
1455                         if (bundle->getVerbose()) {
1456                             printf("Pruning unneeded resource: %s\n",
1457                                     file->getPrintableSource().string());
1458                         }
1459                         grp->removeFile(k);
1460                         k--;
1461                     }
1462                 }
1463             }
1464         }
1465     }
1466
1467     return NO_ERROR;
1468 }
1469
1470 sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
1471 {
1472     sp<AaptSymbols> sym = mSymbols.valueFor(name);
1473     if (sym == NULL) {
1474         sym = new AaptSymbols();
1475         mSymbols.add(name, sym);
1476     }
1477     return sym;
1478 }
1479
1480 sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
1481 {
1482     sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
1483     if (sym == NULL) {
1484         sym = new AaptSymbols();
1485         mJavaSymbols.add(name, sym);
1486     }
1487     return sym;
1488 }
1489
1490 status_t AaptAssets::applyJavaSymbols()
1491 {
1492     size_t N = mJavaSymbols.size();
1493     for (size_t i=0; i<N; i++) {
1494         const String8& name = mJavaSymbols.keyAt(i);
1495         const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
1496         ssize_t pos = mSymbols.indexOfKey(name);
1497         if (pos < 0) {
1498             SourcePos pos;
1499             pos.error("Java symbol dir %s not defined\n", name.string());
1500             return UNKNOWN_ERROR;
1501         }
1502         //printf("**** applying java symbols in dir %s\n", name.string());
1503         status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
1504         if (err != NO_ERROR) {
1505             return err;
1506         }
1507     }
1508
1509     return NO_ERROR;
1510 }
1511
1512 bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
1513     //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
1514     //        sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
1515     //        sym.isJavaSymbol ? 1 : 0);
1516     if (!mHavePrivateSymbols) return true;
1517     if (sym.isPublic) return true;
1518     if (includePrivate && sym.isJavaSymbol) return true;
1519     return false;
1520 }
1521
1522 status_t AaptAssets::buildIncludedResources(Bundle* bundle)
1523 {
1524     if (mHaveIncludedAssets) {
1525         return NO_ERROR;
1526     }
1527
1528     // Add in all includes.
1529     const Vector<String8>& includes = bundle->getPackageIncludes();
1530     const size_t packageIncludeCount = includes.size();
1531     for (size_t i = 0; i < packageIncludeCount; i++) {
1532         if (bundle->getVerbose()) {
1533             printf("Including resources from package: %s\n", includes[i].string());
1534         }
1535
1536         if (!mIncludedAssets.addAssetPath(includes[i], NULL)) {
1537             fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
1538                     includes[i].string());
1539             return UNKNOWN_ERROR;
1540         }
1541     }
1542
1543     const String8& featureOfBase = bundle->getFeatureOfPackage();
1544     if (!featureOfBase.isEmpty()) {
1545         if (bundle->getVerbose()) {
1546             printf("Including base feature resources from package: %s\n",
1547                     featureOfBase.string());
1548         }
1549
1550         if (!mIncludedAssets.addAssetPath(featureOfBase, NULL)) {
1551             fprintf(stderr, "ERROR: base feature package '%s' not found.\n",
1552                     featureOfBase.string());
1553             return UNKNOWN_ERROR;
1554         }
1555     }
1556
1557     mHaveIncludedAssets = true;
1558
1559     return NO_ERROR;
1560 }
1561
1562 status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
1563 {
1564     const ResTable& res = getIncludedResources();
1565     // XXX dirty!
1566     return const_cast<ResTable&>(res).add(file->getData(), file->getSize());
1567 }
1568
1569 const ResTable& AaptAssets::getIncludedResources() const
1570 {
1571     return mIncludedAssets.getResources(false);
1572 }
1573
1574 AssetManager& AaptAssets::getAssetManager()
1575 {
1576     return mIncludedAssets;
1577 }
1578
1579 void AaptAssets::print(const String8& prefix) const
1580 {
1581     String8 innerPrefix(prefix);
1582     innerPrefix.append("  ");
1583     String8 innerInnerPrefix(innerPrefix);
1584     innerInnerPrefix.append("  ");
1585     printf("%sConfigurations:\n", prefix.string());
1586     const size_t N=mGroupEntries.size();
1587     for (size_t i=0; i<N; i++) {
1588         String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
1589         printf("%s %s\n", prefix.string(),
1590                 cname != "" ? cname.string() : "(default)");
1591     }
1592
1593     printf("\n%sFiles:\n", prefix.string());
1594     AaptDir::print(innerPrefix);
1595
1596     printf("\n%sResource Dirs:\n", prefix.string());
1597     const Vector<sp<AaptDir> >& resdirs = mResDirs;
1598     const size_t NR = resdirs.size();
1599     for (size_t i=0; i<NR; i++) {
1600         const sp<AaptDir>& d = resdirs.itemAt(i);
1601         printf("%s  Type %s\n", prefix.string(), d->getLeaf().string());
1602         d->print(innerInnerPrefix);
1603     }
1604 }
1605
1606 sp<AaptDir> AaptAssets::resDir(const String8& name) const
1607 {
1608     const Vector<sp<AaptDir> >& resdirs = mResDirs;
1609     const size_t N = resdirs.size();
1610     for (size_t i=0; i<N; i++) {
1611         const sp<AaptDir>& d = resdirs.itemAt(i);
1612         if (d->getLeaf() == name) {
1613             return d;
1614         }
1615     }
1616     return NULL;
1617 }
1618
1619 bool
1620 valid_symbol_name(const String8& symbol)
1621 {
1622     static char const * const KEYWORDS[] = {
1623         "abstract", "assert", "boolean", "break",
1624         "byte", "case", "catch", "char", "class", "const", "continue",
1625         "default", "do", "double", "else", "enum", "extends", "final",
1626         "finally", "float", "for", "goto", "if", "implements", "import",
1627         "instanceof", "int", "interface", "long", "native", "new", "package",
1628         "private", "protected", "public", "return", "short", "static",
1629         "strictfp", "super", "switch", "synchronized", "this", "throw",
1630         "throws", "transient", "try", "void", "volatile", "while",
1631         "true", "false", "null",
1632         NULL
1633     };
1634     const char*const* k = KEYWORDS;
1635     const char*const s = symbol.string();
1636     while (*k) {
1637         if (0 == strcmp(s, *k)) {
1638             return false;
1639         }
1640         k++;
1641     }
1642     return true;
1643 }