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  */
validateFileName(const char * fileName)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 
isHidden(const char * root,const char * path)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 */
isAlpha(const String8 & string)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 */
isNumber(const String8 & string)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 
setLanguage(const char * languageChars)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 
setRegion(const char * regionChars)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 
setScript(const char * scriptChars)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 
setVariant(const char * variantChars)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 
initFromFilterString(const String8 & str)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 
initFromDirName(const Vector<String8> & parts,const int startIndex)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 
initFromResTable(const ResTable_config & config)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 
writeTo(ResTable_config * out) const385 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
initFromDirName(const char * dir,String8 * resType)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
toDirName(const String8 & resType) const425 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 
editData(size_t size)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 
editDataInRange(size_t offset,size_t size)460 void* AaptFile::editDataInRange(size_t offset, size_t size)
461 {
462     return (void*)(((uint8_t*) editData(offset + size)) + offset);
463 }
464 
editData(size_t * outSize)465 void* AaptFile::editData(size_t* outSize)
466 {
467     if (outSize) {
468         *outSize = mDataSize;
469     }
470     return mData;
471 }
472 
padData(size_t wordSize)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 
writeData(const void * data,size_t size)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 
clearData()500 void AaptFile::clearData()
501 {
502     if (mData != NULL) free(mData);
503     mData = NULL;
504     mDataSize = 0;
505     mBufferSize = 0;
506 }
507 
getPrintableSource() const508 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 
addFile(const sp<AaptFile> & file,const bool overwriteDuplicate)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 
removeFile(size_t index)554 void AaptGroup::removeFile(size_t index)
555 {
556 	mFiles.removeItemsAt(index);
557 }
558 
print(const String8 & prefix) const559 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 
getPrintableSource() const579 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 
addFile(const String8 & name,const sp<AaptGroup> & file)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 
addDir(const String8 & name,const sp<AaptDir> & dir)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 
makeDir(const String8 & path)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 
removeFile(const String8 & name)632 void AaptDir::removeFile(const String8& name)
633 {
634     mFiles.removeItem(name);
635 }
636 
removeDir(const String8 & name)637 void AaptDir::removeDir(const String8& name)
638 {
639     mDirs.removeItem(name);
640 }
641 
addLeafFile(const String8 & leafName,const sp<AaptFile> & file,const bool overwrite)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 
slurpFullTree(Bundle * bundle,const String8 & srcDir,const AaptGroupEntry & kind,const String8 & resType,sp<FilePathStore> & fullResPaths,const bool overwrite)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 
validate() const743 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 
print(const String8 & prefix) const807 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 
getPrintableSource() const821 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 
applyJavaSymbols(const sp<AaptSymbols> & javaSymbols)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 
AaptAssets()884 AaptAssets::AaptAssets()
885     : AaptDir(String8(), String8()),
886       mHavePrivateSymbols(false),
887       mChanged(false), mHaveIncludedAssets(false),
888       mRes(NULL) {}
889 
getGroupEntries() const890 const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
891     if (mChanged) {
892     }
893     return mGroupEntries;
894 }
895 
addFile(const String8 & name,const sp<AaptGroup> & file)896 status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
897 {
898     mChanged = true;
899     return AaptDir::addFile(name, file);
900 }
901 
addFile(const String8 & filePath,const AaptGroupEntry & entry,const String8 & srcDir,sp<AaptGroup> * outGroup,const String8 & resType)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 
addResource(const String8 & leafName,const String8 & path,const sp<AaptFile> & file,const String8 & resType)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 
slurpFromArgs(Bundle * bundle)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 
slurpFullTree(Bundle * bundle,const String8 & srcDir,const AaptGroupEntry & kind,const String8 & resType,sp<FilePathStore> & fullResPaths,const bool overwrite)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 
slurpResourceTree(Bundle * bundle,const String8 & srcDir)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
slurpResourceZip(Bundle *,const char * filename)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 
filter(Bundle * bundle)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 
getSymbolsFor(const String8 & name)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 
getJavaSymbolsFor(const String8 & name)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 
applyJavaSymbols()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 
isJavaSymbol(const AaptSymbolEntry & sym,bool includePrivate) const1512 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 
buildIncludedResources(Bundle * bundle)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 
addIncludedResources(const sp<AaptFile> & file)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 
getIncludedResources() const1569 const ResTable& AaptAssets::getIncludedResources() const
1570 {
1571     return mIncludedAssets.getResources(false);
1572 }
1573 
getAssetManager()1574 AssetManager& AaptAssets::getAssetManager()
1575 {
1576     return mIncludedAssets;
1577 }
1578 
print(const String8 & prefix) const1579 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 
resDir(const String8 & name) const1606 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
valid_symbol_name(const String8 & symbol)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 }
1644