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