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