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