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