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