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