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 {
   1146     ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
   1147     if (res > 0) {
   1148         mGroupEntries.add(kind);
   1149     }
   1150 
   1151     return res;
   1152 }
   1153 
   1154 ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
   1155 {
   1156     ssize_t err = 0;
   1157 
   1158     DIR* dir = opendir(srcDir.string());
   1159     if (dir == NULL) {
   1160         fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
   1161         return UNKNOWN_ERROR;
   1162     }
   1163 
   1164     status_t count = 0;
   1165 
   1166     /*
   1167      * Run through the directory, looking for dirs that match the
   1168      * expected pattern.
   1169      */
   1170     while (1) {
   1171         struct dirent* entry = readdir(dir);
   1172         if (entry == NULL) {
   1173             break;
   1174         }
   1175 
   1176         if (isHidden(srcDir.string(), entry->d_name)) {
   1177             continue;
   1178         }
   1179 
   1180         String8 subdirName(srcDir);
   1181         subdirName.appendPath(entry->d_name);
   1182 
   1183         AaptGroupEntry group;
   1184         String8 resType;
   1185         bool b = group.initFromDirName(entry->d_name, &resType);
   1186         if (!b) {
   1187             fprintf(stderr, "invalid resource directory name: %s %s\n", srcDir.string(),
   1188                     entry->d_name);
   1189             err = -1;
   1190             continue;
   1191         }
   1192 
   1193         if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
   1194             int maxResInt = atoi(bundle->getMaxResVersion());
   1195             const char *verString = group.getVersionString().string();
   1196             int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
   1197             if (dirVersionInt > maxResInt) {
   1198               fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
   1199               continue;
   1200             }
   1201         }
   1202 
   1203         FileType type = getFileType(subdirName.string());
   1204 
   1205         if (type == kFileTypeDirectory) {
   1206             sp<AaptDir> dir = makeDir(resType);
   1207             ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
   1208                                                 resType, mFullResPaths);
   1209             if (res < 0) {
   1210                 count = res;
   1211                 goto bail;
   1212             }
   1213             if (res > 0) {
   1214                 mGroupEntries.add(group);
   1215                 count += res;
   1216             }
   1217 
   1218             // Only add this directory if we don't already have a resource dir
   1219             // for the current type.  This ensures that we only add the dir once
   1220             // for all configs.
   1221             sp<AaptDir> rdir = resDir(resType);
   1222             if (rdir == NULL) {
   1223                 mResDirs.add(dir);
   1224             }
   1225         } else {
   1226             if (bundle->getVerbose()) {
   1227                 fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
   1228             }
   1229         }
   1230     }
   1231 
   1232 bail:
   1233     closedir(dir);
   1234     dir = NULL;
   1235 
   1236     if (err != 0) {
   1237         return err;
   1238     }
   1239     return count;
   1240 }
   1241 
   1242 ssize_t
   1243 AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
   1244 {
   1245     int count = 0;
   1246     SortedVector<AaptGroupEntry> entries;
   1247 
   1248     ZipFile* zip = new ZipFile;
   1249     status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
   1250     if (err != NO_ERROR) {
   1251         fprintf(stderr, "error opening zip file %s\n", filename);
   1252         count = err;
   1253         delete zip;
   1254         return -1;
   1255     }
   1256 
   1257     const int N = zip->getNumEntries();
   1258     for (int i=0; i<N; i++) {
   1259         ZipEntry* entry = zip->getEntryByIndex(i);
   1260         if (entry->getDeleted()) {
   1261             continue;
   1262         }
   1263 
   1264         String8 entryName(entry->getFileName());
   1265 
   1266         String8 dirName = entryName.getPathDir();
   1267         sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
   1268 
   1269         String8 resType;
   1270         AaptGroupEntry kind;
   1271 
   1272         String8 remain;
   1273         if (entryName.walkPath(&remain) == kResourceDir) {
   1274             // these are the resources, pull their type out of the directory name
   1275             kind.initFromDirName(remain.walkPath().string(), &resType);
   1276         } else {
   1277             // these are untyped and don't have an AaptGroupEntry
   1278         }
   1279         if (entries.indexOf(kind) < 0) {
   1280             entries.add(kind);
   1281             mGroupEntries.add(kind);
   1282         }
   1283 
   1284         // use the one from the zip file if they both exist.
   1285         dir->removeFile(entryName.getPathLeaf());
   1286 
   1287         sp<AaptFile> file = new AaptFile(entryName, kind, resType);
   1288         status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
   1289         if (err != NO_ERROR) {
   1290             fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
   1291             count = err;
   1292             goto bail;
   1293         }
   1294         file->setCompressionMethod(entry->getCompressionMethod());
   1295 
   1296 #if 0
   1297         if (entryName == "AndroidManifest.xml") {
   1298             printf("AndroidManifest.xml\n");
   1299         }
   1300         printf("\n\nfile: %s\n", entryName.string());
   1301 #endif
   1302 
   1303         size_t len = entry->getUncompressedLen();
   1304         void* data = zip->uncompress(entry);
   1305         void* buf = file->editData(len);
   1306         memcpy(buf, data, len);
   1307 
   1308 #if 0
   1309         const int OFF = 0;
   1310         const unsigned char* p = (unsigned char*)data;
   1311         const unsigned char* end = p+len;
   1312         p += OFF;
   1313         for (int i=0; i<32 && p < end; i++) {
   1314             printf("0x%03x ", i*0x10 + OFF);
   1315             for (int j=0; j<0x10 && p < end; j++) {
   1316                 printf(" %02x", *p);
   1317                 p++;
   1318             }
   1319             printf("\n");
   1320         }
   1321 #endif
   1322 
   1323         free(data);
   1324 
   1325         count++;
   1326     }
   1327 
   1328 bail:
   1329     delete zip;
   1330     return count;
   1331 }
   1332 
   1333 status_t AaptAssets::filter(Bundle* bundle)
   1334 {
   1335     WeakResourceFilter reqFilter;
   1336     status_t err = reqFilter.parse(bundle->getConfigurations());
   1337     if (err != NO_ERROR) {
   1338         return err;
   1339     }
   1340 
   1341     uint32_t preferredDensity = 0;
   1342     if (bundle->getPreferredDensity().size() > 0) {
   1343         ResTable_config preferredConfig;
   1344         if (!AaptConfig::parseDensity(bundle->getPreferredDensity().string(), &preferredConfig)) {
   1345             fprintf(stderr, "Error parsing preferred density: %s\n",
   1346                     bundle->getPreferredDensity().string());
   1347             return UNKNOWN_ERROR;
   1348         }
   1349         preferredDensity = preferredConfig.density;
   1350     }
   1351 
   1352     if (reqFilter.isEmpty() && preferredDensity == 0) {
   1353         return NO_ERROR;
   1354     }
   1355 
   1356     if (bundle->getVerbose()) {
   1357         if (!reqFilter.isEmpty()) {
   1358             printf("Applying required filter: %s\n",
   1359                     bundle->getConfigurations().string());
   1360         }
   1361         if (preferredDensity > 0) {
   1362             printf("Applying preferred density filter: %s\n",
   1363                     bundle->getPreferredDensity().string());
   1364         }
   1365     }
   1366 
   1367     const Vector<sp<AaptDir> >& resdirs = mResDirs;
   1368     const size_t ND = resdirs.size();
   1369     for (size_t i=0; i<ND; i++) {
   1370         const sp<AaptDir>& dir = resdirs.itemAt(i);
   1371         if (dir->getLeaf() == kValuesDir) {
   1372             // The "value" dir is special since a single file defines
   1373             // multiple resources, so we can not do filtering on the
   1374             // files themselves.
   1375             continue;
   1376         }
   1377         if (dir->getLeaf() == kMipmapDir) {
   1378             // We also skip the "mipmap" directory, since the point of this
   1379             // is to include all densities without stripping.  If you put
   1380             // other configurations in here as well they won't be stripped
   1381             // either...  So don't do that.  Seriously.  What is wrong with you?
   1382             continue;
   1383         }
   1384 
   1385         const size_t NG = dir->getFiles().size();
   1386         for (size_t j=0; j<NG; j++) {
   1387             sp<AaptGroup> grp = dir->getFiles().valueAt(j);
   1388 
   1389             // First remove any configurations we know we don't need.
   1390             for (size_t k=0; k<grp->getFiles().size(); k++) {
   1391                 sp<AaptFile> file = grp->getFiles().valueAt(k);
   1392                 if (k == 0 && grp->getFiles().size() == 1) {
   1393                     // If this is the only file left, we need to keep it.
   1394                     // Otherwise the resource IDs we are using will be inconsistent
   1395                     // with what we get when not stripping.  Sucky, but at least
   1396                     // for now we can rely on the back-end doing another filtering
   1397                     // pass to take this out and leave us with this resource name
   1398                     // containing no entries.
   1399                     continue;
   1400                 }
   1401                 if (file->getPath().getPathExtension() == ".xml") {
   1402                     // We can't remove .xml files at this point, because when
   1403                     // we parse them they may add identifier resources, so
   1404                     // removing them can cause our resource identifiers to
   1405                     // become inconsistent.
   1406                     continue;
   1407                 }
   1408                 const ResTable_config& config(file->getGroupEntry().toParams());
   1409                 if (!reqFilter.match(config)) {
   1410                     if (bundle->getVerbose()) {
   1411                         printf("Pruning unneeded resource: %s\n",
   1412                                 file->getPrintableSource().string());
   1413                     }
   1414                     grp->removeFile(k);
   1415                     k--;
   1416                 }
   1417             }
   1418 
   1419             // Quick check: no preferred filters, nothing more to do.
   1420             if (preferredDensity == 0) {
   1421                 continue;
   1422             }
   1423 
   1424             // Get the preferred density if there is one. We do not match exactly for density.
   1425             // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
   1426             // pick xhdpi.
   1427             for (size_t k=0; k<grp->getFiles().size(); k++) {
   1428                 sp<AaptFile> file = grp->getFiles().valueAt(k);
   1429                 if (k == 0 && grp->getFiles().size() == 1) {
   1430                     // If this is the only file left, we need to keep it.
   1431                     // Otherwise the resource IDs we are using will be inconsistent
   1432                     // with what we get when not stripping.  Sucky, but at least
   1433                     // for now we can rely on the back-end doing another filtering
   1434                     // pass to take this out and leave us with this resource name
   1435                     // containing no entries.
   1436                     continue;
   1437                 }
   1438                 if (file->getPath().getPathExtension() == ".xml") {
   1439                     // We can't remove .xml files at this point, because when
   1440                     // we parse them they may add identifier resources, so
   1441                     // removing them can cause our resource identifiers to
   1442                     // become inconsistent.
   1443                     continue;
   1444                 }
   1445                 const ResTable_config& config(file->getGroupEntry().toParams());
   1446                 if (config.density != 0 && config.density != preferredDensity) {
   1447                     // This is a resource we would prefer not to have.  Check
   1448                     // to see if have a similar variation that we would like
   1449                     // to have and, if so, we can drop it.
   1450                     uint32_t bestDensity = config.density;
   1451 
   1452                     for (size_t m=0; m<grp->getFiles().size(); m++) {
   1453                         if (m == k) {
   1454                             continue;
   1455                         }
   1456 
   1457                         sp<AaptFile> mfile = grp->getFiles().valueAt(m);
   1458                         const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
   1459                         if (AaptConfig::isSameExcept(config, mconfig, ResTable_config::CONFIG_DENSITY)) {
   1460                             // See if there is a better density resource
   1461                             if (mconfig.density < bestDensity &&
   1462                                     mconfig.density >= preferredDensity &&
   1463                                     bestDensity > preferredDensity) {
   1464                                 // This density is our preferred density, or between our best density and
   1465                                 // the preferred density, therefore it is better.
   1466                                 bestDensity = mconfig.density;
   1467                             } else if (mconfig.density > bestDensity &&
   1468                                     bestDensity < preferredDensity) {
   1469                                 // This density is better than our best density and
   1470                                 // our best density was smaller than our preferred
   1471                                 // density, so it is better.
   1472                                 bestDensity = mconfig.density;
   1473                             }
   1474                         }
   1475                     }
   1476 
   1477                     if (bestDensity != config.density) {
   1478                         if (bundle->getVerbose()) {
   1479                             printf("Pruning unneeded resource: %s\n",
   1480                                     file->getPrintableSource().string());
   1481                         }
   1482                         grp->removeFile(k);
   1483                         k--;
   1484                     }
   1485                 }
   1486             }
   1487         }
   1488     }
   1489 
   1490     return NO_ERROR;
   1491 }
   1492 
   1493 sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
   1494 {
   1495     sp<AaptSymbols> sym = mSymbols.valueFor(name);
   1496     if (sym == NULL) {
   1497         sym = new AaptSymbols();
   1498         mSymbols.add(name, sym);
   1499     }
   1500     return sym;
   1501 }
   1502 
   1503 sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
   1504 {
   1505     sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
   1506     if (sym == NULL) {
   1507         sym = new AaptSymbols();
   1508         mJavaSymbols.add(name, sym);
   1509     }
   1510     return sym;
   1511 }
   1512 
   1513 status_t AaptAssets::applyJavaSymbols()
   1514 {
   1515     size_t N = mJavaSymbols.size();
   1516     for (size_t i=0; i<N; i++) {
   1517         const String8& name = mJavaSymbols.keyAt(i);
   1518         const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
   1519         ssize_t pos = mSymbols.indexOfKey(name);
   1520         if (pos < 0) {
   1521             SourcePos pos;
   1522             pos.error("Java symbol dir %s not defined\n", name.string());
   1523             return UNKNOWN_ERROR;
   1524         }
   1525         //printf("**** applying java symbols in dir %s\n", name.string());
   1526         status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
   1527         if (err != NO_ERROR) {
   1528             return err;
   1529         }
   1530     }
   1531 
   1532     return NO_ERROR;
   1533 }
   1534 
   1535 bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
   1536     //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
   1537     //        sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
   1538     //        sym.isJavaSymbol ? 1 : 0);
   1539     if (!mHavePrivateSymbols) return true;
   1540     if (sym.isPublic) return true;
   1541     if (includePrivate && sym.isJavaSymbol) return true;
   1542     return false;
   1543 }
   1544 
   1545 status_t AaptAssets::buildIncludedResources(Bundle* bundle)
   1546 {
   1547     if (mHaveIncludedAssets) {
   1548         return NO_ERROR;
   1549     }
   1550 
   1551     // Add in all includes.
   1552     const Vector<String8>& includes = bundle->getPackageIncludes();
   1553     const size_t packageIncludeCount = includes.size();
   1554     for (size_t i = 0; i < packageIncludeCount; i++) {
   1555         if (bundle->getVerbose()) {
   1556             printf("Including resources from package: %s\n", includes[i].string());
   1557         }
   1558 
   1559         if (!mIncludedAssets.addAssetPath(includes[i], NULL)) {
   1560             fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
   1561                     includes[i].string());
   1562             return UNKNOWN_ERROR;
   1563         }
   1564     }
   1565 
   1566     const String8& featureOfBase = bundle->getFeatureOfPackage();
   1567     if (!featureOfBase.isEmpty()) {
   1568         if (bundle->getVerbose()) {
   1569             printf("Including base feature resources from package: %s\n",
   1570                     featureOfBase.string());
   1571         }
   1572 
   1573         if (!mIncludedAssets.addAssetPath(featureOfBase, NULL)) {
   1574             fprintf(stderr, "ERROR: base feature package '%s' not found.\n",
   1575                     featureOfBase.string());
   1576             return UNKNOWN_ERROR;
   1577         }
   1578     }
   1579 
   1580     mHaveIncludedAssets = true;
   1581 
   1582     return NO_ERROR;
   1583 }
   1584 
   1585 status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
   1586 {
   1587     const ResTable& res = getIncludedResources();
   1588     // XXX dirty!
   1589     return const_cast<ResTable&>(res).add(file->getData(), file->getSize());
   1590 }
   1591 
   1592 const ResTable& AaptAssets::getIncludedResources() const
   1593 {
   1594     return mIncludedAssets.getResources(false);
   1595 }
   1596 
   1597 AssetManager& AaptAssets::getAssetManager()
   1598 {
   1599     return mIncludedAssets;
   1600 }
   1601 
   1602 void AaptAssets::print(const String8& prefix) const
   1603 {
   1604     String8 innerPrefix(prefix);
   1605     innerPrefix.append("  ");
   1606     String8 innerInnerPrefix(innerPrefix);
   1607     innerInnerPrefix.append("  ");
   1608     printf("%sConfigurations:\n", prefix.string());
   1609     const size_t N=mGroupEntries.size();
   1610     for (size_t i=0; i<N; i++) {
   1611         String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
   1612         printf("%s %s\n", prefix.string(),
   1613                 cname != "" ? cname.string() : "(default)");
   1614     }
   1615 
   1616     printf("\n%sFiles:\n", prefix.string());
   1617     AaptDir::print(innerPrefix);
   1618 
   1619     printf("\n%sResource Dirs:\n", prefix.string());
   1620     const Vector<sp<AaptDir> >& resdirs = mResDirs;
   1621     const size_t NR = resdirs.size();
   1622     for (size_t i=0; i<NR; i++) {
   1623         const sp<AaptDir>& d = resdirs.itemAt(i);
   1624         printf("%s  Type %s\n", prefix.string(), d->getLeaf().string());
   1625         d->print(innerInnerPrefix);
   1626     }
   1627 }
   1628 
   1629 sp<AaptDir> AaptAssets::resDir(const String8& name) const
   1630 {
   1631     const Vector<sp<AaptDir> >& resdirs = mResDirs;
   1632     const size_t N = resdirs.size();
   1633     for (size_t i=0; i<N; i++) {
   1634         const sp<AaptDir>& d = resdirs.itemAt(i);
   1635         if (d->getLeaf() == name) {
   1636             return d;
   1637         }
   1638     }
   1639     return NULL;
   1640 }
   1641 
   1642 bool
   1643 valid_symbol_name(const String8& symbol)
   1644 {
   1645     static char const * const KEYWORDS[] = {
   1646         "abstract", "assert", "boolean", "break",
   1647         "byte", "case", "catch", "char", "class", "const", "continue",
   1648         "default", "do", "double", "else", "enum", "extends", "final",
   1649         "finally", "float", "for", "goto", "if", "implements", "import",
   1650         "instanceof", "int", "interface", "long", "native", "new", "package",
   1651         "private", "protected", "public", "return", "short", "static",
   1652         "strictfp", "super", "switch", "synchronized", "this", "throw",
   1653         "throws", "transient", "try", "void", "volatile", "while",
   1654         "true", "false", "null",
   1655         NULL
   1656     };
   1657     const char*const* k = KEYWORDS;
   1658     const char*const s = symbol.string();
   1659     while (*k) {
   1660         if (0 == strcmp(s, *k)) {
   1661             return false;
   1662         }
   1663         k++;
   1664     }
   1665     return true;
   1666 }
   1667