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