Home | History | Annotate | Download | only in aapt
      1 //
      2 // Copyright 2006 The Android Open Source Project
      3 //
      4 
      5 #include "AaptAssets.h"
      6 #include "ResourceFilter.h"
      7 #include "Main.h"
      8 
      9 #include <utils/misc.h>
     10 #include <utils/SortedVector.h>
     11 
     12 #include <ctype.h>
     13 #include <dirent.h>
     14 #include <errno.h>
     15 
     16 static const char* kDefaultLocale = "default";
     17 static const char* kWildcardName = "any";
     18 static const char* kAssetDir = "assets";
     19 static const char* kResourceDir = "res";
     20 static const char* kValuesDir = "values";
     21 static const char* kMipmapDir = "mipmap";
     22 static const char* kInvalidChars = "/\\:";
     23 static const size_t kMaxAssetFileName = 100;
     24 
     25 static const String8 kResString(kResourceDir);
     26 
     27 /*
     28  * Names of asset files must meet the following criteria:
     29  *
     30  *  - the filename length must be less than kMaxAssetFileName bytes long
     31  *    (and can't be empty)
     32  *  - all characters must be 7-bit printable ASCII
     33  *  - none of { '/' '\\' ':' }
     34  *
     35  * Pass in just the filename, not the full path.
     36  */
     37 static bool validateFileName(const char* fileName)
     38 {
     39     const char* cp = fileName;
     40     size_t len = 0;
     41 
     42     while (*cp != '\0') {
     43         if ((*cp & 0x80) != 0)
     44             return false;           // reject high ASCII
     45         if (*cp < 0x20 || *cp >= 0x7f)
     46             return false;           // reject control chars and 0x7f
     47         if (strchr(kInvalidChars, *cp) != NULL)
     48             return false;           // reject path sep chars
     49         cp++;
     50         len++;
     51     }
     52 
     53     if (len < 1 || len > kMaxAssetFileName)
     54         return false;               // reject empty or too long
     55 
     56     return true;
     57 }
     58 
     59 // The default to use if no other ignore pattern is defined.
     60 const char * const gDefaultIgnoreAssets =
     61     "!.svn:!.git:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~";
     62 // The ignore pattern that can be passed via --ignore-assets in Main.cpp
     63 const char * gUserIgnoreAssets = NULL;
     64 
     65 static bool isHidden(const char *root, const char *path)
     66 {
     67     // Patterns syntax:
     68     // - Delimiter is :
     69     // - Entry can start with the flag ! to avoid printing a warning
     70     //   about the file being ignored.
     71     // - Entry can have the flag "<dir>" to match only directories
     72     //   or <file> to match only files. Default is to match both.
     73     // - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
     74     //   where prefix/suffix must have at least 1 character (so that
     75     //   we don't match a '*' catch-all pattern.)
     76     // - The special filenames "." and ".." are always ignored.
     77     // - Otherwise the full string is matched.
     78     // - match is not case-sensitive.
     79 
     80     if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) {
     81         return true;
     82     }
     83 
     84     const char *delim = ":";
     85     const char *p = gUserIgnoreAssets;
     86     if (!p || !p[0]) {
     87         p = getenv("ANDROID_AAPT_IGNORE");
     88     }
     89     if (!p || !p[0]) {
     90         p = gDefaultIgnoreAssets;
     91     }
     92     char *patterns = strdup(p);
     93 
     94     bool ignore = false;
     95     bool chatty = true;
     96     char *matchedPattern = NULL;
     97 
     98     String8 fullPath(root);
     99     fullPath.appendPath(path);
    100     FileType type = getFileType(fullPath);
    101 
    102     int plen = strlen(path);
    103 
    104     // Note: we don't have strtok_r under mingw.
    105     for(char *token = strtok(patterns, delim);
    106             !ignore && token != NULL;
    107             token = strtok(NULL, delim)) {
    108         chatty = token[0] != '!';
    109         if (!chatty) token++; // skip !
    110         if (strncasecmp(token, "<dir>" , 5) == 0) {
    111             if (type != kFileTypeDirectory) continue;
    112             token += 5;
    113         }
    114         if (strncasecmp(token, "<file>", 6) == 0) {
    115             if (type != kFileTypeRegular) continue;
    116             token += 6;
    117         }
    118 
    119         matchedPattern = token;
    120         int n = strlen(token);
    121 
    122         if (token[0] == '*') {
    123             // Match *suffix
    124             token++;
    125             n--;
    126             if (n <= plen) {
    127                 ignore = strncasecmp(token, path + plen - n, n) == 0;
    128             }
    129         } else if (n > 1 && token[n - 1] == '*') {
    130             // Match prefix*
    131             ignore = strncasecmp(token, path, n - 1) == 0;
    132         } else {
    133             ignore = strcasecmp(token, path) == 0;
    134         }
    135     }
    136 
    137     if (ignore && chatty) {
    138         fprintf(stderr, "    (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n",
    139                 type == kFileTypeDirectory ? "dir" : "file",
    140                 path,
    141                 matchedPattern ? matchedPattern : "");
    142     }
    143 
    144     free(patterns);
    145     return ignore;
    146 }
    147 
    148 // =========================================================================
    149 // =========================================================================
    150 // =========================================================================
    151 
    152 status_t
    153 AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value)
    154 {
    155     ResTable_config config;
    156 
    157     // IMSI - MCC
    158     if (getMccName(part.string(), &config)) {
    159         *axis = AXIS_MCC;
    160         *value = config.mcc;
    161         return 0;
    162     }
    163 
    164     // IMSI - MNC
    165     if (getMncName(part.string(), &config)) {
    166         *axis = AXIS_MNC;
    167         *value = config.mnc;
    168         return 0;
    169     }
    170 
    171     // locale - language
    172     if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
    173         *axis = AXIS_LANGUAGE;
    174         *value = part[1] << 8 | part[0];
    175         return 0;
    176     }
    177 
    178     // locale - language_REGION
    179     if (part.length() == 5 && isalpha(part[0]) && isalpha(part[1])
    180             && part[2] == '_' && isalpha(part[3]) && isalpha(part[4])) {
    181         *axis = AXIS_LANGUAGE;
    182         *value = (part[4] << 24) | (part[3] << 16) | (part[1] << 8) | (part[0]);
    183         return 0;
    184     }
    185 
    186     // smallest screen dp width
    187     if (getSmallestScreenWidthDpName(part.string(), &config)) {
    188         *axis = AXIS_SMALLESTSCREENWIDTHDP;
    189         *value = config.smallestScreenWidthDp;
    190         return 0;
    191     }
    192 
    193     // screen dp width
    194     if (getScreenWidthDpName(part.string(), &config)) {
    195         *axis = AXIS_SCREENWIDTHDP;
    196         *value = config.screenWidthDp;
    197         return 0;
    198     }
    199 
    200     // screen dp height
    201     if (getScreenHeightDpName(part.string(), &config)) {
    202         *axis = AXIS_SCREENHEIGHTDP;
    203         *value = config.screenHeightDp;
    204         return 0;
    205     }
    206 
    207     // screen layout size
    208     if (getScreenLayoutSizeName(part.string(), &config)) {
    209         *axis = AXIS_SCREENLAYOUTSIZE;
    210         *value = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
    211         return 0;
    212     }
    213 
    214     // screen layout long
    215     if (getScreenLayoutLongName(part.string(), &config)) {
    216         *axis = AXIS_SCREENLAYOUTLONG;
    217         *value = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
    218         return 0;
    219     }
    220 
    221     // orientation
    222     if (getOrientationName(part.string(), &config)) {
    223         *axis = AXIS_ORIENTATION;
    224         *value = config.orientation;
    225         return 0;
    226     }
    227 
    228     // ui mode type
    229     if (getUiModeTypeName(part.string(), &config)) {
    230         *axis = AXIS_UIMODETYPE;
    231         *value = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
    232         return 0;
    233     }
    234 
    235     // ui mode night
    236     if (getUiModeNightName(part.string(), &config)) {
    237         *axis = AXIS_UIMODENIGHT;
    238         *value = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
    239         return 0;
    240     }
    241 
    242     // density
    243     if (getDensityName(part.string(), &config)) {
    244         *axis = AXIS_DENSITY;
    245         *value = config.density;
    246         return 0;
    247     }
    248 
    249     // touchscreen
    250     if (getTouchscreenName(part.string(), &config)) {
    251         *axis = AXIS_TOUCHSCREEN;
    252         *value = config.touchscreen;
    253         return 0;
    254     }
    255 
    256     // keyboard hidden
    257     if (getKeysHiddenName(part.string(), &config)) {
    258         *axis = AXIS_KEYSHIDDEN;
    259         *value = config.inputFlags;
    260         return 0;
    261     }
    262 
    263     // keyboard
    264     if (getKeyboardName(part.string(), &config)) {
    265         *axis = AXIS_KEYBOARD;
    266         *value = config.keyboard;
    267         return 0;
    268     }
    269 
    270     // navigation hidden
    271     if (getNavHiddenName(part.string(), &config)) {
    272         *axis = AXIS_NAVHIDDEN;
    273         *value = config.inputFlags;
    274         return 0;
    275     }
    276 
    277     // navigation
    278     if (getNavigationName(part.string(), &config)) {
    279         *axis = AXIS_NAVIGATION;
    280         *value = config.navigation;
    281         return 0;
    282     }
    283 
    284     // screen size
    285     if (getScreenSizeName(part.string(), &config)) {
    286         *axis = AXIS_SCREENSIZE;
    287         *value = config.screenSize;
    288         return 0;
    289     }
    290 
    291     // version
    292     if (getVersionName(part.string(), &config)) {
    293         *axis = AXIS_VERSION;
    294         *value = config.version;
    295         return 0;
    296     }
    297 
    298     return 1;
    299 }
    300 
    301 uint32_t
    302 AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
    303 {
    304     switch (axis) {
    305         case AXIS_MCC:
    306             return config.mcc;
    307         case AXIS_MNC:
    308             return config.mnc;
    309         case AXIS_LANGUAGE:
    310             return (((uint32_t)config.country[1]) << 24) | (((uint32_t)config.country[0]) << 16)
    311                 | (((uint32_t)config.language[1]) << 8) | (config.language[0]);
    312         case AXIS_SCREENLAYOUTSIZE:
    313             return config.screenLayout&ResTable_config::MASK_SCREENSIZE;
    314         case AXIS_ORIENTATION:
    315             return config.orientation;
    316         case AXIS_UIMODETYPE:
    317             return (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
    318         case AXIS_UIMODENIGHT:
    319             return (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
    320         case AXIS_DENSITY:
    321             return config.density;
    322         case AXIS_TOUCHSCREEN:
    323             return config.touchscreen;
    324         case AXIS_KEYSHIDDEN:
    325             return config.inputFlags;
    326         case AXIS_KEYBOARD:
    327             return config.keyboard;
    328         case AXIS_NAVIGATION:
    329             return config.navigation;
    330         case AXIS_SCREENSIZE:
    331             return config.screenSize;
    332         case AXIS_SMALLESTSCREENWIDTHDP:
    333             return config.smallestScreenWidthDp;
    334         case AXIS_SCREENWIDTHDP:
    335             return config.screenWidthDp;
    336         case AXIS_SCREENHEIGHTDP:
    337             return config.screenHeightDp;
    338         case AXIS_VERSION:
    339             return config.version;
    340     }
    341     return 0;
    342 }
    343 
    344 bool
    345 AaptGroupEntry::configSameExcept(const ResTable_config& config,
    346         const ResTable_config& otherConfig, int axis)
    347 {
    348     for (int i=AXIS_START; i<=AXIS_END; i++) {
    349         if (i == axis) {
    350             continue;
    351         }
    352         if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) {
    353             return false;
    354         }
    355     }
    356     return true;
    357 }
    358 
    359 bool
    360 AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
    361 {
    362     mParamsChanged = true;
    363 
    364     Vector<String8> parts;
    365 
    366     String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
    367     String8 touch, key, keysHidden, nav, navHidden, size, vers;
    368     String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
    369 
    370     const char *p = dir;
    371     const char *q;
    372     while (NULL != (q = strchr(p, '-'))) {
    373         String8 val(p, q-p);
    374         val.toLower();
    375         parts.add(val);
    376         //printf("part: %s\n", parts[parts.size()-1].string());
    377         p = q+1;
    378     }
    379     String8 val(p);
    380     val.toLower();
    381     parts.add(val);
    382     //printf("part: %s\n", parts[parts.size()-1].string());
    383 
    384     const int N = parts.size();
    385     int index = 0;
    386     String8 part = parts[index];
    387 
    388     // resource type
    389     if (!isValidResourceType(part)) {
    390         return false;
    391     }
    392     *resType = part;
    393 
    394     index++;
    395     if (index == N) {
    396         goto success;
    397     }
    398     part = parts[index];
    399 
    400     // imsi - mcc
    401     if (getMccName(part.string())) {
    402         mcc = part;
    403 
    404         index++;
    405         if (index == N) {
    406             goto success;
    407         }
    408         part = parts[index];
    409     } else {
    410         //printf("not mcc: %s\n", part.string());
    411     }
    412 
    413     // imsi - mnc
    414     if (getMncName(part.string())) {
    415         mnc = part;
    416 
    417         index++;
    418         if (index == N) {
    419             goto success;
    420         }
    421         part = parts[index];
    422     } else {
    423         //printf("not mcc: %s\n", part.string());
    424     }
    425 
    426     // locale - language
    427     if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
    428         loc = part;
    429 
    430         index++;
    431         if (index == N) {
    432             goto success;
    433         }
    434         part = parts[index];
    435     } else {
    436         //printf("not language: %s\n", part.string());
    437     }
    438 
    439     // locale - region
    440     if (loc.length() > 0
    441             && part.length() == 3 && part[0] == 'r' && part[0] && part[1]) {
    442         loc += "-";
    443         part.toUpper();
    444         loc += part.string() + 1;
    445 
    446         index++;
    447         if (index == N) {
    448             goto success;
    449         }
    450         part = parts[index];
    451     } else {
    452         //printf("not region: %s\n", part.string());
    453     }
    454 
    455     if (getSmallestScreenWidthDpName(part.string())) {
    456         smallestwidthdp = part;
    457 
    458         index++;
    459         if (index == N) {
    460             goto success;
    461         }
    462         part = parts[index];
    463     } else {
    464         //printf("not smallest screen width dp: %s\n", part.string());
    465     }
    466 
    467     if (getScreenWidthDpName(part.string())) {
    468         widthdp = part;
    469 
    470         index++;
    471         if (index == N) {
    472             goto success;
    473         }
    474         part = parts[index];
    475     } else {
    476         //printf("not screen width dp: %s\n", part.string());
    477     }
    478 
    479     if (getScreenHeightDpName(part.string())) {
    480         heightdp = part;
    481 
    482         index++;
    483         if (index == N) {
    484             goto success;
    485         }
    486         part = parts[index];
    487     } else {
    488         //printf("not screen height dp: %s\n", part.string());
    489     }
    490 
    491     if (getScreenLayoutSizeName(part.string())) {
    492         layoutsize = part;
    493 
    494         index++;
    495         if (index == N) {
    496             goto success;
    497         }
    498         part = parts[index];
    499     } else {
    500         //printf("not screen layout size: %s\n", part.string());
    501     }
    502 
    503     if (getScreenLayoutLongName(part.string())) {
    504         layoutlong = part;
    505 
    506         index++;
    507         if (index == N) {
    508             goto success;
    509         }
    510         part = parts[index];
    511     } else {
    512         //printf("not screen layout long: %s\n", part.string());
    513     }
    514 
    515     // orientation
    516     if (getOrientationName(part.string())) {
    517         orient = part;
    518 
    519         index++;
    520         if (index == N) {
    521             goto success;
    522         }
    523         part = parts[index];
    524     } else {
    525         //printf("not orientation: %s\n", part.string());
    526     }
    527 
    528     // ui mode type
    529     if (getUiModeTypeName(part.string())) {
    530         uiModeType = part;
    531 
    532         index++;
    533         if (index == N) {
    534             goto success;
    535         }
    536         part = parts[index];
    537     } else {
    538         //printf("not ui mode type: %s\n", part.string());
    539     }
    540 
    541     // ui mode night
    542     if (getUiModeNightName(part.string())) {
    543         uiModeNight = part;
    544 
    545         index++;
    546         if (index == N) {
    547             goto success;
    548         }
    549         part = parts[index];
    550     } else {
    551         //printf("not ui mode night: %s\n", part.string());
    552     }
    553 
    554     // density
    555     if (getDensityName(part.string())) {
    556         den = part;
    557 
    558         index++;
    559         if (index == N) {
    560             goto success;
    561         }
    562         part = parts[index];
    563     } else {
    564         //printf("not density: %s\n", part.string());
    565     }
    566 
    567     // touchscreen
    568     if (getTouchscreenName(part.string())) {
    569         touch = part;
    570 
    571         index++;
    572         if (index == N) {
    573             goto success;
    574         }
    575         part = parts[index];
    576     } else {
    577         //printf("not touchscreen: %s\n", part.string());
    578     }
    579 
    580     // keyboard hidden
    581     if (getKeysHiddenName(part.string())) {
    582         keysHidden = part;
    583 
    584         index++;
    585         if (index == N) {
    586             goto success;
    587         }
    588         part = parts[index];
    589     } else {
    590         //printf("not keysHidden: %s\n", part.string());
    591     }
    592 
    593     // keyboard
    594     if (getKeyboardName(part.string())) {
    595         key = part;
    596 
    597         index++;
    598         if (index == N) {
    599             goto success;
    600         }
    601         part = parts[index];
    602     } else {
    603         //printf("not keyboard: %s\n", part.string());
    604     }
    605 
    606     // navigation hidden
    607     if (getNavHiddenName(part.string())) {
    608         navHidden = part;
    609 
    610         index++;
    611         if (index == N) {
    612             goto success;
    613         }
    614         part = parts[index];
    615     } else {
    616         //printf("not navHidden: %s\n", part.string());
    617     }
    618 
    619     if (getNavigationName(part.string())) {
    620         nav = part;
    621 
    622         index++;
    623         if (index == N) {
    624             goto success;
    625         }
    626         part = parts[index];
    627     } else {
    628         //printf("not navigation: %s\n", part.string());
    629     }
    630 
    631     if (getScreenSizeName(part.string())) {
    632         size = part;
    633 
    634         index++;
    635         if (index == N) {
    636             goto success;
    637         }
    638         part = parts[index];
    639     } else {
    640         //printf("not screen size: %s\n", part.string());
    641     }
    642 
    643     if (getVersionName(part.string())) {
    644         vers = part;
    645 
    646         index++;
    647         if (index == N) {
    648             goto success;
    649         }
    650         part = parts[index];
    651     } else {
    652         //printf("not version: %s\n", part.string());
    653     }
    654 
    655     // if there are extra parts, it doesn't match
    656     return false;
    657 
    658 success:
    659     this->mcc = mcc;
    660     this->mnc = mnc;
    661     this->locale = loc;
    662     this->screenLayoutSize = layoutsize;
    663     this->screenLayoutLong = layoutlong;
    664     this->smallestScreenWidthDp = smallestwidthdp;
    665     this->screenWidthDp = widthdp;
    666     this->screenHeightDp = heightdp;
    667     this->orientation = orient;
    668     this->uiModeType = uiModeType;
    669     this->uiModeNight = uiModeNight;
    670     this->density = den;
    671     this->touchscreen = touch;
    672     this->keysHidden = keysHidden;
    673     this->keyboard = key;
    674     this->navHidden = navHidden;
    675     this->navigation = nav;
    676     this->screenSize = size;
    677     this->version = vers;
    678 
    679     // what is this anyway?
    680     this->vendor = "";
    681 
    682     return true;
    683 }
    684 
    685 String8
    686 AaptGroupEntry::toString() const
    687 {
    688     String8 s = this->mcc;
    689     s += ",";
    690     s += this->mnc;
    691     s += ",";
    692     s += this->locale;
    693     s += ",";
    694     s += smallestScreenWidthDp;
    695     s += ",";
    696     s += screenWidthDp;
    697     s += ",";
    698     s += screenHeightDp;
    699     s += ",";
    700     s += screenLayoutSize;
    701     s += ",";
    702     s += screenLayoutLong;
    703     s += ",";
    704     s += this->orientation;
    705     s += ",";
    706     s += uiModeType;
    707     s += ",";
    708     s += uiModeNight;
    709     s += ",";
    710     s += density;
    711     s += ",";
    712     s += touchscreen;
    713     s += ",";
    714     s += keysHidden;
    715     s += ",";
    716     s += keyboard;
    717     s += ",";
    718     s += navHidden;
    719     s += ",";
    720     s += navigation;
    721     s += ",";
    722     s += screenSize;
    723     s += ",";
    724     s += version;
    725     return s;
    726 }
    727 
    728 String8
    729 AaptGroupEntry::toDirName(const String8& resType) const
    730 {
    731     String8 s = resType;
    732     if (this->mcc != "") {
    733         if (s.length() > 0) {
    734             s += "-";
    735         }
    736         s += mcc;
    737     }
    738     if (this->mnc != "") {
    739         if (s.length() > 0) {
    740             s += "-";
    741         }
    742         s += mnc;
    743     }
    744     if (this->locale != "") {
    745         if (s.length() > 0) {
    746             s += "-";
    747         }
    748         s += locale;
    749     }
    750     if (this->smallestScreenWidthDp != "") {
    751         if (s.length() > 0) {
    752             s += "-";
    753         }
    754         s += smallestScreenWidthDp;
    755     }
    756     if (this->screenWidthDp != "") {
    757         if (s.length() > 0) {
    758             s += "-";
    759         }
    760         s += screenWidthDp;
    761     }
    762     if (this->screenHeightDp != "") {
    763         if (s.length() > 0) {
    764             s += "-";
    765         }
    766         s += screenHeightDp;
    767     }
    768     if (this->screenLayoutSize != "") {
    769         if (s.length() > 0) {
    770             s += "-";
    771         }
    772         s += screenLayoutSize;
    773     }
    774     if (this->screenLayoutLong != "") {
    775         if (s.length() > 0) {
    776             s += "-";
    777         }
    778         s += screenLayoutLong;
    779     }
    780     if (this->orientation != "") {
    781         if (s.length() > 0) {
    782             s += "-";
    783         }
    784         s += orientation;
    785     }
    786     if (this->uiModeType != "") {
    787         if (s.length() > 0) {
    788             s += "-";
    789         }
    790         s += uiModeType;
    791     }
    792     if (this->uiModeNight != "") {
    793         if (s.length() > 0) {
    794             s += "-";
    795         }
    796         s += uiModeNight;
    797     }
    798     if (this->density != "") {
    799         if (s.length() > 0) {
    800             s += "-";
    801         }
    802         s += density;
    803     }
    804     if (this->touchscreen != "") {
    805         if (s.length() > 0) {
    806             s += "-";
    807         }
    808         s += touchscreen;
    809     }
    810     if (this->keysHidden != "") {
    811         if (s.length() > 0) {
    812             s += "-";
    813         }
    814         s += keysHidden;
    815     }
    816     if (this->keyboard != "") {
    817         if (s.length() > 0) {
    818             s += "-";
    819         }
    820         s += keyboard;
    821     }
    822     if (this->navHidden != "") {
    823         if (s.length() > 0) {
    824             s += "-";
    825         }
    826         s += navHidden;
    827     }
    828     if (this->navigation != "") {
    829         if (s.length() > 0) {
    830             s += "-";
    831         }
    832         s += navigation;
    833     }
    834     if (this->screenSize != "") {
    835         if (s.length() > 0) {
    836             s += "-";
    837         }
    838         s += screenSize;
    839     }
    840     if (this->version != "") {
    841         if (s.length() > 0) {
    842             s += "-";
    843         }
    844         s += version;
    845     }
    846 
    847     return s;
    848 }
    849 
    850 bool AaptGroupEntry::getMccName(const char* name,
    851                                     ResTable_config* out)
    852 {
    853     if (strcmp(name, kWildcardName) == 0) {
    854         if (out) out->mcc = 0;
    855         return true;
    856     }
    857     const char* c = name;
    858     if (tolower(*c) != 'm') return false;
    859     c++;
    860     if (tolower(*c) != 'c') return false;
    861     c++;
    862     if (tolower(*c) != 'c') return false;
    863     c++;
    864 
    865     const char* val = c;
    866 
    867     while (*c >= '0' && *c <= '9') {
    868         c++;
    869     }
    870     if (*c != 0) return false;
    871     if (c-val != 3) return false;
    872 
    873     int d = atoi(val);
    874     if (d != 0) {
    875         if (out) out->mcc = d;
    876         return true;
    877     }
    878 
    879     return false;
    880 }
    881 
    882 bool AaptGroupEntry::getMncName(const char* name,
    883                                     ResTable_config* out)
    884 {
    885     if (strcmp(name, kWildcardName) == 0) {
    886         if (out) out->mcc = 0;
    887         return true;
    888     }
    889     const char* c = name;
    890     if (tolower(*c) != 'm') return false;
    891     c++;
    892     if (tolower(*c) != 'n') return false;
    893     c++;
    894     if (tolower(*c) != 'c') return false;
    895     c++;
    896 
    897     const char* val = c;
    898 
    899     while (*c >= '0' && *c <= '9') {
    900         c++;
    901     }
    902     if (*c != 0) return false;
    903     if (c-val == 0 || c-val > 3) return false;
    904 
    905     if (out) {
    906         out->mnc = atoi(val);
    907     }
    908 
    909     return true;
    910 }
    911 
    912 /*
    913  * Does this directory name fit the pattern of a locale dir ("en-rUS" or
    914  * "default")?
    915  *
    916  * TODO: Should insist that the first two letters are lower case, and the
    917  * second two are upper.
    918  */
    919 bool AaptGroupEntry::getLocaleName(const char* fileName,
    920                                    ResTable_config* out)
    921 {
    922     if (strcmp(fileName, kWildcardName) == 0
    923             || strcmp(fileName, kDefaultLocale) == 0) {
    924         if (out) {
    925             out->language[0] = 0;
    926             out->language[1] = 0;
    927             out->country[0] = 0;
    928             out->country[1] = 0;
    929         }
    930         return true;
    931     }
    932 
    933     if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) {
    934         if (out) {
    935             out->language[0] = fileName[0];
    936             out->language[1] = fileName[1];
    937             out->country[0] = 0;
    938             out->country[1] = 0;
    939         }
    940         return true;
    941     }
    942 
    943     if (strlen(fileName) == 5 &&
    944         isalpha(fileName[0]) &&
    945         isalpha(fileName[1]) &&
    946         fileName[2] == '-' &&
    947         isalpha(fileName[3]) &&
    948         isalpha(fileName[4])) {
    949         if (out) {
    950             out->language[0] = fileName[0];
    951             out->language[1] = fileName[1];
    952             out->country[0] = fileName[3];
    953             out->country[1] = fileName[4];
    954         }
    955         return true;
    956     }
    957 
    958     return false;
    959 }
    960 
    961 bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
    962                                      ResTable_config* out)
    963 {
    964     if (strcmp(name, kWildcardName) == 0) {
    965         if (out) out->screenLayout =
    966                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
    967                 | ResTable_config::SCREENSIZE_ANY;
    968         return true;
    969     } else if (strcmp(name, "small") == 0) {
    970         if (out) out->screenLayout =
    971                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
    972                 | ResTable_config::SCREENSIZE_SMALL;
    973         return true;
    974     } else if (strcmp(name, "normal") == 0) {
    975         if (out) out->screenLayout =
    976                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
    977                 | ResTable_config::SCREENSIZE_NORMAL;
    978         return true;
    979     } else if (strcmp(name, "large") == 0) {
    980         if (out) out->screenLayout =
    981                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
    982                 | ResTable_config::SCREENSIZE_LARGE;
    983         return true;
    984     } else if (strcmp(name, "xlarge") == 0) {
    985         if (out) out->screenLayout =
    986                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
    987                 | ResTable_config::SCREENSIZE_XLARGE;
    988         return true;
    989     }
    990 
    991     return false;
    992 }
    993 
    994 bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
    995                                      ResTable_config* out)
    996 {
    997     if (strcmp(name, kWildcardName) == 0) {
    998         if (out) out->screenLayout =
    999                 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
   1000                 | ResTable_config::SCREENLONG_ANY;
   1001         return true;
   1002     } else if (strcmp(name, "long") == 0) {
   1003         if (out) out->screenLayout =
   1004                 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
   1005                 | ResTable_config::SCREENLONG_YES;
   1006         return true;
   1007     } else if (strcmp(name, "notlong") == 0) {
   1008         if (out) out->screenLayout =
   1009                 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
   1010                 | ResTable_config::SCREENLONG_NO;
   1011         return true;
   1012     }
   1013 
   1014     return false;
   1015 }
   1016 
   1017 bool AaptGroupEntry::getOrientationName(const char* name,
   1018                                         ResTable_config* out)
   1019 {
   1020     if (strcmp(name, kWildcardName) == 0) {
   1021         if (out) out->orientation = out->ORIENTATION_ANY;
   1022         return true;
   1023     } else if (strcmp(name, "port") == 0) {
   1024         if (out) out->orientation = out->ORIENTATION_PORT;
   1025         return true;
   1026     } else if (strcmp(name, "land") == 0) {
   1027         if (out) out->orientation = out->ORIENTATION_LAND;
   1028         return true;
   1029     } else if (strcmp(name, "square") == 0) {
   1030         if (out) out->orientation = out->ORIENTATION_SQUARE;
   1031         return true;
   1032     }
   1033 
   1034     return false;
   1035 }
   1036 
   1037 bool AaptGroupEntry::getUiModeTypeName(const char* name,
   1038                                        ResTable_config* out)
   1039 {
   1040     if (strcmp(name, kWildcardName) == 0) {
   1041         if (out) out->uiMode =
   1042                 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
   1043                 | ResTable_config::UI_MODE_TYPE_ANY;
   1044         return true;
   1045     } else if (strcmp(name, "desk") == 0) {
   1046       if (out) out->uiMode =
   1047               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
   1048               | ResTable_config::UI_MODE_TYPE_DESK;
   1049         return true;
   1050     } else if (strcmp(name, "car") == 0) {
   1051       if (out) out->uiMode =
   1052               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
   1053               | ResTable_config::UI_MODE_TYPE_CAR;
   1054         return true;
   1055     } else if (strcmp(name, "television") == 0) {
   1056       if (out) out->uiMode =
   1057               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
   1058               | ResTable_config::UI_MODE_TYPE_TELEVISION;
   1059         return true;
   1060     } else if (strcmp(name, "appliance") == 0) {
   1061       if (out) out->uiMode =
   1062               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
   1063               | ResTable_config::UI_MODE_TYPE_APPLIANCE;
   1064         return true;
   1065     }
   1066 
   1067     return false;
   1068 }
   1069 
   1070 bool AaptGroupEntry::getUiModeNightName(const char* name,
   1071                                           ResTable_config* out)
   1072 {
   1073     if (strcmp(name, kWildcardName) == 0) {
   1074         if (out) out->uiMode =
   1075                 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
   1076                 | ResTable_config::UI_MODE_NIGHT_ANY;
   1077         return true;
   1078     } else if (strcmp(name, "night") == 0) {
   1079         if (out) out->uiMode =
   1080                 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
   1081                 | ResTable_config::UI_MODE_NIGHT_YES;
   1082         return true;
   1083     } else if (strcmp(name, "notnight") == 0) {
   1084       if (out) out->uiMode =
   1085               (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
   1086               | ResTable_config::UI_MODE_NIGHT_NO;
   1087         return true;
   1088     }
   1089 
   1090     return false;
   1091 }
   1092 
   1093 bool AaptGroupEntry::getDensityName(const char* name,
   1094                                     ResTable_config* out)
   1095 {
   1096     if (strcmp(name, kWildcardName) == 0) {
   1097         if (out) out->density = ResTable_config::DENSITY_DEFAULT;
   1098         return true;
   1099     }
   1100 
   1101     if (strcmp(name, "nodpi") == 0) {
   1102         if (out) out->density = ResTable_config::DENSITY_NONE;
   1103         return true;
   1104     }
   1105 
   1106     if (strcmp(name, "ldpi") == 0) {
   1107         if (out) out->density = ResTable_config::DENSITY_LOW;
   1108         return true;
   1109     }
   1110 
   1111     if (strcmp(name, "mdpi") == 0) {
   1112         if (out) out->density = ResTable_config::DENSITY_MEDIUM;
   1113         return true;
   1114     }
   1115 
   1116     if (strcmp(name, "tvdpi") == 0) {
   1117         if (out) out->density = ResTable_config::DENSITY_TV;
   1118         return true;
   1119     }
   1120 
   1121     if (strcmp(name, "hdpi") == 0) {
   1122         if (out) out->density = ResTable_config::DENSITY_HIGH;
   1123         return true;
   1124     }
   1125 
   1126     if (strcmp(name, "xhdpi") == 0) {
   1127         if (out) out->density = ResTable_config::DENSITY_XHIGH;
   1128         return true;
   1129     }
   1130 
   1131     if (strcmp(name, "xxhdpi") == 0) {
   1132         if (out) out->density = ResTable_config::DENSITY_XXHIGH;
   1133         return true;
   1134     }
   1135 
   1136     char* c = (char*)name;
   1137     while (*c >= '0' && *c <= '9') {
   1138         c++;
   1139     }
   1140 
   1141     // check that we have 'dpi' after the last digit.
   1142     if (toupper(c[0]) != 'D' ||
   1143             toupper(c[1]) != 'P' ||
   1144             toupper(c[2]) != 'I' ||
   1145             c[3] != 0) {
   1146         return false;
   1147     }
   1148 
   1149     // temporarily replace the first letter with \0 to
   1150     // use atoi.
   1151     char tmp = c[0];
   1152     c[0] = '\0';
   1153 
   1154     int d = atoi(name);
   1155     c[0] = tmp;
   1156 
   1157     if (d != 0) {
   1158         if (out) out->density = d;
   1159         return true;
   1160     }
   1161 
   1162     return false;
   1163 }
   1164 
   1165 bool AaptGroupEntry::getTouchscreenName(const char* name,
   1166                                         ResTable_config* out)
   1167 {
   1168     if (strcmp(name, kWildcardName) == 0) {
   1169         if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
   1170         return true;
   1171     } else if (strcmp(name, "notouch") == 0) {
   1172         if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
   1173         return true;
   1174     } else if (strcmp(name, "stylus") == 0) {
   1175         if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
   1176         return true;
   1177     } else if (strcmp(name, "finger") == 0) {
   1178         if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
   1179         return true;
   1180     }
   1181 
   1182     return false;
   1183 }
   1184 
   1185 bool AaptGroupEntry::getKeysHiddenName(const char* name,
   1186                                        ResTable_config* out)
   1187 {
   1188     uint8_t mask = 0;
   1189     uint8_t value = 0;
   1190     if (strcmp(name, kWildcardName) == 0) {
   1191         mask = ResTable_config::MASK_KEYSHIDDEN;
   1192         value = ResTable_config::KEYSHIDDEN_ANY;
   1193     } else if (strcmp(name, "keysexposed") == 0) {
   1194         mask = ResTable_config::MASK_KEYSHIDDEN;
   1195         value = ResTable_config::KEYSHIDDEN_NO;
   1196     } else if (strcmp(name, "keyshidden") == 0) {
   1197         mask = ResTable_config::MASK_KEYSHIDDEN;
   1198         value = ResTable_config::KEYSHIDDEN_YES;
   1199     } else if (strcmp(name, "keyssoft") == 0) {
   1200         mask = ResTable_config::MASK_KEYSHIDDEN;
   1201         value = ResTable_config::KEYSHIDDEN_SOFT;
   1202     }
   1203 
   1204     if (mask != 0) {
   1205         if (out) out->inputFlags = (out->inputFlags&~mask) | value;
   1206         return true;
   1207     }
   1208 
   1209     return false;
   1210 }
   1211 
   1212 bool AaptGroupEntry::getKeyboardName(const char* name,
   1213                                         ResTable_config* out)
   1214 {
   1215     if (strcmp(name, kWildcardName) == 0) {
   1216         if (out) out->keyboard = out->KEYBOARD_ANY;
   1217         return true;
   1218     } else if (strcmp(name, "nokeys") == 0) {
   1219         if (out) out->keyboard = out->KEYBOARD_NOKEYS;
   1220         return true;
   1221     } else if (strcmp(name, "qwerty") == 0) {
   1222         if (out) out->keyboard = out->KEYBOARD_QWERTY;
   1223         return true;
   1224     } else if (strcmp(name, "12key") == 0) {
   1225         if (out) out->keyboard = out->KEYBOARD_12KEY;
   1226         return true;
   1227     }
   1228 
   1229     return false;
   1230 }
   1231 
   1232 bool AaptGroupEntry::getNavHiddenName(const char* name,
   1233                                        ResTable_config* out)
   1234 {
   1235     uint8_t mask = 0;
   1236     uint8_t value = 0;
   1237     if (strcmp(name, kWildcardName) == 0) {
   1238         mask = ResTable_config::MASK_NAVHIDDEN;
   1239         value = ResTable_config::NAVHIDDEN_ANY;
   1240     } else if (strcmp(name, "navexposed") == 0) {
   1241         mask = ResTable_config::MASK_NAVHIDDEN;
   1242         value = ResTable_config::NAVHIDDEN_NO;
   1243     } else if (strcmp(name, "navhidden") == 0) {
   1244         mask = ResTable_config::MASK_NAVHIDDEN;
   1245         value = ResTable_config::NAVHIDDEN_YES;
   1246     }
   1247 
   1248     if (mask != 0) {
   1249         if (out) out->inputFlags = (out->inputFlags&~mask) | value;
   1250         return true;
   1251     }
   1252 
   1253     return false;
   1254 }
   1255 
   1256 bool AaptGroupEntry::getNavigationName(const char* name,
   1257                                      ResTable_config* out)
   1258 {
   1259     if (strcmp(name, kWildcardName) == 0) {
   1260         if (out) out->navigation = out->NAVIGATION_ANY;
   1261         return true;
   1262     } else if (strcmp(name, "nonav") == 0) {
   1263         if (out) out->navigation = out->NAVIGATION_NONAV;
   1264         return true;
   1265     } else if (strcmp(name, "dpad") == 0) {
   1266         if (out) out->navigation = out->NAVIGATION_DPAD;
   1267         return true;
   1268     } else if (strcmp(name, "trackball") == 0) {
   1269         if (out) out->navigation = out->NAVIGATION_TRACKBALL;
   1270         return true;
   1271     } else if (strcmp(name, "wheel") == 0) {
   1272         if (out) out->navigation = out->NAVIGATION_WHEEL;
   1273         return true;
   1274     }
   1275 
   1276     return false;
   1277 }
   1278 
   1279 bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
   1280 {
   1281     if (strcmp(name, kWildcardName) == 0) {
   1282         if (out) {
   1283             out->screenWidth = out->SCREENWIDTH_ANY;
   1284             out->screenHeight = out->SCREENHEIGHT_ANY;
   1285         }
   1286         return true;
   1287     }
   1288 
   1289     const char* x = name;
   1290     while (*x >= '0' && *x <= '9') x++;
   1291     if (x == name || *x != 'x') return false;
   1292     String8 xName(name, x-name);
   1293     x++;
   1294 
   1295     const char* y = x;
   1296     while (*y >= '0' && *y <= '9') y++;
   1297     if (y == name || *y != 0) return false;
   1298     String8 yName(x, y-x);
   1299 
   1300     uint16_t w = (uint16_t)atoi(xName.string());
   1301     uint16_t h = (uint16_t)atoi(yName.string());
   1302     if (w < h) {
   1303         return false;
   1304     }
   1305 
   1306     if (out) {
   1307         out->screenWidth = w;
   1308         out->screenHeight = h;
   1309     }
   1310 
   1311     return true;
   1312 }
   1313 
   1314 bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
   1315 {
   1316     if (strcmp(name, kWildcardName) == 0) {
   1317         if (out) {
   1318             out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
   1319         }
   1320         return true;
   1321     }
   1322 
   1323     if (*name != 's') return false;
   1324     name++;
   1325     if (*name != 'w') return false;
   1326     name++;
   1327     const char* x = name;
   1328     while (*x >= '0' && *x <= '9') x++;
   1329     if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
   1330     String8 xName(name, x-name);
   1331 
   1332     if (out) {
   1333         out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
   1334     }
   1335 
   1336     return true;
   1337 }
   1338 
   1339 bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
   1340 {
   1341     if (strcmp(name, kWildcardName) == 0) {
   1342         if (out) {
   1343             out->screenWidthDp = out->SCREENWIDTH_ANY;
   1344         }
   1345         return true;
   1346     }
   1347 
   1348     if (*name != 'w') return false;
   1349     name++;
   1350     const char* x = name;
   1351     while (*x >= '0' && *x <= '9') x++;
   1352     if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
   1353     String8 xName(name, x-name);
   1354 
   1355     if (out) {
   1356         out->screenWidthDp = (uint16_t)atoi(xName.string());
   1357     }
   1358 
   1359     return true;
   1360 }
   1361 
   1362 bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
   1363 {
   1364     if (strcmp(name, kWildcardName) == 0) {
   1365         if (out) {
   1366             out->screenHeightDp = out->SCREENWIDTH_ANY;
   1367         }
   1368         return true;
   1369     }
   1370 
   1371     if (*name != 'h') return false;
   1372     name++;
   1373     const char* x = name;
   1374     while (*x >= '0' && *x <= '9') x++;
   1375     if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
   1376     String8 xName(name, x-name);
   1377 
   1378     if (out) {
   1379         out->screenHeightDp = (uint16_t)atoi(xName.string());
   1380     }
   1381 
   1382     return true;
   1383 }
   1384 
   1385 bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
   1386 {
   1387     if (strcmp(name, kWildcardName) == 0) {
   1388         if (out) {
   1389             out->sdkVersion = out->SDKVERSION_ANY;
   1390             out->minorVersion = out->MINORVERSION_ANY;
   1391         }
   1392         return true;
   1393     }
   1394 
   1395     if (*name != 'v') {
   1396         return false;
   1397     }
   1398 
   1399     name++;
   1400     const char* s = name;
   1401     while (*s >= '0' && *s <= '9') s++;
   1402     if (s == name || *s != 0) return false;
   1403     String8 sdkName(name, s-name);
   1404 
   1405     if (out) {
   1406         out->sdkVersion = (uint16_t)atoi(sdkName.string());
   1407         out->minorVersion = 0;
   1408     }
   1409 
   1410     return true;
   1411 }
   1412 
   1413 int AaptGroupEntry::compare(const AaptGroupEntry& o) const
   1414 {
   1415     int v = mcc.compare(o.mcc);
   1416     if (v == 0) v = mnc.compare(o.mnc);
   1417     if (v == 0) v = locale.compare(o.locale);
   1418     if (v == 0) v = vendor.compare(o.vendor);
   1419     if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
   1420     if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
   1421     if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
   1422     if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
   1423     if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
   1424     if (v == 0) v = orientation.compare(o.orientation);
   1425     if (v == 0) v = uiModeType.compare(o.uiModeType);
   1426     if (v == 0) v = uiModeNight.compare(o.uiModeNight);
   1427     if (v == 0) v = density.compare(o.density);
   1428     if (v == 0) v = touchscreen.compare(o.touchscreen);
   1429     if (v == 0) v = keysHidden.compare(o.keysHidden);
   1430     if (v == 0) v = keyboard.compare(o.keyboard);
   1431     if (v == 0) v = navHidden.compare(o.navHidden);
   1432     if (v == 0) v = navigation.compare(o.navigation);
   1433     if (v == 0) v = screenSize.compare(o.screenSize);
   1434     if (v == 0) v = version.compare(o.version);
   1435     return v;
   1436 }
   1437 
   1438 const ResTable_config& AaptGroupEntry::toParams() const
   1439 {
   1440     if (!mParamsChanged) {
   1441         return mParams;
   1442     }
   1443 
   1444     mParamsChanged = false;
   1445     ResTable_config& params(mParams);
   1446     memset(&params, 0, sizeof(params));
   1447     getMccName(mcc.string(), &params);
   1448     getMncName(mnc.string(), &params);
   1449     getLocaleName(locale.string(), &params);
   1450     getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
   1451     getScreenWidthDpName(screenWidthDp.string(), &params);
   1452     getScreenHeightDpName(screenHeightDp.string(), &params);
   1453     getScreenLayoutSizeName(screenLayoutSize.string(), &params);
   1454     getScreenLayoutLongName(screenLayoutLong.string(), &params);
   1455     getOrientationName(orientation.string(), &params);
   1456     getUiModeTypeName(uiModeType.string(), &params);
   1457     getUiModeNightName(uiModeNight.string(), &params);
   1458     getDensityName(density.string(), &params);
   1459     getTouchscreenName(touchscreen.string(), &params);
   1460     getKeysHiddenName(keysHidden.string(), &params);
   1461     getKeyboardName(keyboard.string(), &params);
   1462     getNavHiddenName(navHidden.string(), &params);
   1463     getNavigationName(navigation.string(), &params);
   1464     getScreenSizeName(screenSize.string(), &params);
   1465     getVersionName(version.string(), &params);
   1466 
   1467     // Fix up version number based on specified parameters.
   1468     int minSdk = 0;
   1469     if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
   1470             || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
   1471             || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
   1472         minSdk = SDK_HONEYCOMB_MR2;
   1473     } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
   1474                 != ResTable_config::UI_MODE_TYPE_ANY
   1475             ||  (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
   1476                 != ResTable_config::UI_MODE_NIGHT_ANY) {
   1477         minSdk = SDK_FROYO;
   1478     } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
   1479                 != ResTable_config::SCREENSIZE_ANY
   1480             ||  (params.screenLayout&ResTable_config::MASK_SCREENLONG)
   1481                 != ResTable_config::SCREENLONG_ANY
   1482             || params.density != ResTable_config::DENSITY_DEFAULT) {
   1483         minSdk = SDK_DONUT;
   1484     }
   1485 
   1486     if (minSdk > params.sdkVersion) {
   1487         params.sdkVersion = minSdk;
   1488     }
   1489 
   1490     return params;
   1491 }
   1492 
   1493 // =========================================================================
   1494 // =========================================================================
   1495 // =========================================================================
   1496 
   1497 void* AaptFile::editData(size_t size)
   1498 {
   1499     if (size <= mBufferSize) {
   1500         mDataSize = size;
   1501         return mData;
   1502     }
   1503     size_t allocSize = (size*3)/2;
   1504     void* buf = realloc(mData, allocSize);
   1505     if (buf == NULL) {
   1506         return NULL;
   1507     }
   1508     mData = buf;
   1509     mDataSize = size;
   1510     mBufferSize = allocSize;
   1511     return buf;
   1512 }
   1513 
   1514 void* AaptFile::editData(size_t* outSize)
   1515 {
   1516     if (outSize) {
   1517         *outSize = mDataSize;
   1518     }
   1519     return mData;
   1520 }
   1521 
   1522 void* AaptFile::padData(size_t wordSize)
   1523 {
   1524     const size_t extra = mDataSize%wordSize;
   1525     if (extra == 0) {
   1526         return mData;
   1527     }
   1528 
   1529     size_t initial = mDataSize;
   1530     void* data = editData(initial+(wordSize-extra));
   1531     if (data != NULL) {
   1532         memset(((uint8_t*)data) + initial, 0, wordSize-extra);
   1533     }
   1534     return data;
   1535 }
   1536 
   1537 status_t AaptFile::writeData(const void* data, size_t size)
   1538 {
   1539     size_t end = mDataSize;
   1540     size_t total = size + end;
   1541     void* buf = editData(total);
   1542     if (buf == NULL) {
   1543         return UNKNOWN_ERROR;
   1544     }
   1545     memcpy(((char*)buf)+end, data, size);
   1546     return NO_ERROR;
   1547 }
   1548 
   1549 void AaptFile::clearData()
   1550 {
   1551     if (mData != NULL) free(mData);
   1552     mData = NULL;
   1553     mDataSize = 0;
   1554     mBufferSize = 0;
   1555 }
   1556 
   1557 String8 AaptFile::getPrintableSource() const
   1558 {
   1559     if (hasData()) {
   1560         String8 name(mGroupEntry.toDirName(String8()));
   1561         name.appendPath(mPath);
   1562         name.append(" #generated");
   1563         return name;
   1564     }
   1565     return mSourceFile;
   1566 }
   1567 
   1568 // =========================================================================
   1569 // =========================================================================
   1570 // =========================================================================
   1571 
   1572 status_t AaptGroup::addFile(const sp<AaptFile>& file)
   1573 {
   1574     if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
   1575         file->mPath = mPath;
   1576         mFiles.add(file->getGroupEntry(), file);
   1577         return NO_ERROR;
   1578     }
   1579 
   1580 #if 0
   1581     printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
   1582             file->getSourceFile().string(),
   1583             file->getGroupEntry().toDirName(String8()).string(),
   1584             mLeaf.string(), mPath.string());
   1585 #endif
   1586 
   1587     SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
   1588                                                getPrintableSource().string());
   1589     return UNKNOWN_ERROR;
   1590 }
   1591 
   1592 void AaptGroup::removeFile(size_t index)
   1593 {
   1594 	mFiles.removeItemsAt(index);
   1595 }
   1596 
   1597 void AaptGroup::print(const String8& prefix) const
   1598 {
   1599     printf("%s%s\n", prefix.string(), getPath().string());
   1600     const size_t N=mFiles.size();
   1601     size_t i;
   1602     for (i=0; i<N; i++) {
   1603         sp<AaptFile> file = mFiles.valueAt(i);
   1604         const AaptGroupEntry& e = file->getGroupEntry();
   1605         if (file->hasData()) {
   1606             printf("%s  Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
   1607                     (int)file->getSize());
   1608         } else {
   1609             printf("%s  Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
   1610                     file->getPrintableSource().string());
   1611         }
   1612         //printf("%s  File Group Entry: %s\n", prefix.string(),
   1613         //        file->getGroupEntry().toDirName(String8()).string());
   1614     }
   1615 }
   1616 
   1617 String8 AaptGroup::getPrintableSource() const
   1618 {
   1619     if (mFiles.size() > 0) {
   1620         // Arbitrarily pull the first source file out of the list.
   1621         return mFiles.valueAt(0)->getPrintableSource();
   1622     }
   1623 
   1624     // Should never hit this case, but to be safe...
   1625     return getPath();
   1626 
   1627 }
   1628 
   1629 // =========================================================================
   1630 // =========================================================================
   1631 // =========================================================================
   1632 
   1633 status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
   1634 {
   1635     if (mFiles.indexOfKey(name) >= 0) {
   1636         return ALREADY_EXISTS;
   1637     }
   1638     mFiles.add(name, file);
   1639     return NO_ERROR;
   1640 }
   1641 
   1642 status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
   1643 {
   1644     if (mDirs.indexOfKey(name) >= 0) {
   1645         return ALREADY_EXISTS;
   1646     }
   1647     mDirs.add(name, dir);
   1648     return NO_ERROR;
   1649 }
   1650 
   1651 sp<AaptDir> AaptDir::makeDir(const String8& path)
   1652 {
   1653     String8 name;
   1654     String8 remain = path;
   1655 
   1656     sp<AaptDir> subdir = this;
   1657     while (name = remain.walkPath(&remain), remain != "") {
   1658         subdir = subdir->makeDir(name);
   1659     }
   1660 
   1661     ssize_t i = subdir->mDirs.indexOfKey(name);
   1662     if (i >= 0) {
   1663         return subdir->mDirs.valueAt(i);
   1664     }
   1665     sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
   1666     subdir->mDirs.add(name, dir);
   1667     return dir;
   1668 }
   1669 
   1670 void AaptDir::removeFile(const String8& name)
   1671 {
   1672     mFiles.removeItem(name);
   1673 }
   1674 
   1675 void AaptDir::removeDir(const String8& name)
   1676 {
   1677     mDirs.removeItem(name);
   1678 }
   1679 
   1680 status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
   1681 {
   1682     sp<AaptGroup> group;
   1683     if (mFiles.indexOfKey(leafName) >= 0) {
   1684         group = mFiles.valueFor(leafName);
   1685     } else {
   1686         group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
   1687         mFiles.add(leafName, group);
   1688     }
   1689 
   1690     return group->addFile(file);
   1691 }
   1692 
   1693 ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
   1694                             const AaptGroupEntry& kind, const String8& resType,
   1695                             sp<FilePathStore>& fullResPaths)
   1696 {
   1697     Vector<String8> fileNames;
   1698     {
   1699         DIR* dir = NULL;
   1700 
   1701         dir = opendir(srcDir.string());
   1702         if (dir == NULL) {
   1703             fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
   1704             return UNKNOWN_ERROR;
   1705         }
   1706 
   1707         /*
   1708          * Slurp the filenames out of the directory.
   1709          */
   1710         while (1) {
   1711             struct dirent* entry;
   1712 
   1713             entry = readdir(dir);
   1714             if (entry == NULL)
   1715                 break;
   1716 
   1717             if (isHidden(srcDir.string(), entry->d_name))
   1718                 continue;
   1719 
   1720             String8 name(entry->d_name);
   1721             fileNames.add(name);
   1722             // Add fully qualified path for dependency purposes
   1723             // if we're collecting them
   1724             if (fullResPaths != NULL) {
   1725                 fullResPaths->add(srcDir.appendPathCopy(name));
   1726             }
   1727         }
   1728         closedir(dir);
   1729     }
   1730 
   1731     ssize_t count = 0;
   1732 
   1733     /*
   1734      * Stash away the files and recursively descend into subdirectories.
   1735      */
   1736     const size_t N = fileNames.size();
   1737     size_t i;
   1738     for (i = 0; i < N; i++) {
   1739         String8 pathName(srcDir);
   1740         FileType type;
   1741 
   1742         pathName.appendPath(fileNames[i].string());
   1743         type = getFileType(pathName.string());
   1744         if (type == kFileTypeDirectory) {
   1745             sp<AaptDir> subdir;
   1746             bool notAdded = false;
   1747             if (mDirs.indexOfKey(fileNames[i]) >= 0) {
   1748                 subdir = mDirs.valueFor(fileNames[i]);
   1749             } else {
   1750                 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
   1751                 notAdded = true;
   1752             }
   1753             ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
   1754                                                 resType, fullResPaths);
   1755             if (res < NO_ERROR) {
   1756                 return res;
   1757             }
   1758             if (res > 0 && notAdded) {
   1759                 mDirs.add(fileNames[i], subdir);
   1760             }
   1761             count += res;
   1762         } else if (type == kFileTypeRegular) {
   1763             sp<AaptFile> file = new AaptFile(pathName, kind, resType);
   1764             status_t err = addLeafFile(fileNames[i], file);
   1765             if (err != NO_ERROR) {
   1766                 return err;
   1767             }
   1768 
   1769             count++;
   1770 
   1771         } else {
   1772             if (bundle->getVerbose())
   1773                 printf("   (ignoring non-file/dir '%s')\n", pathName.string());
   1774         }
   1775     }
   1776 
   1777     return count;
   1778 }
   1779 
   1780 status_t AaptDir::validate() const
   1781 {
   1782     const size_t NF = mFiles.size();
   1783     const size_t ND = mDirs.size();
   1784     size_t i;
   1785     for (i = 0; i < NF; i++) {
   1786         if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
   1787             SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
   1788                     "Invalid filename.  Unable to add.");
   1789             return UNKNOWN_ERROR;
   1790         }
   1791 
   1792         size_t j;
   1793         for (j = i+1; j < NF; j++) {
   1794             if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
   1795                            mFiles.valueAt(j)->getLeaf().string()) == 0) {
   1796                 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
   1797                         "File is case-insensitive equivalent to: %s",
   1798                         mFiles.valueAt(j)->getPrintableSource().string());
   1799                 return UNKNOWN_ERROR;
   1800             }
   1801 
   1802             // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
   1803             // (this is mostly caught by the "marked" stuff, below)
   1804         }
   1805 
   1806         for (j = 0; j < ND; j++) {
   1807             if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
   1808                            mDirs.valueAt(j)->getLeaf().string()) == 0) {
   1809                 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
   1810                         "File conflicts with dir from: %s",
   1811                         mDirs.valueAt(j)->getPrintableSource().string());
   1812                 return UNKNOWN_ERROR;
   1813             }
   1814         }
   1815     }
   1816 
   1817     for (i = 0; i < ND; i++) {
   1818         if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
   1819             SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
   1820                     "Invalid directory name, unable to add.");
   1821             return UNKNOWN_ERROR;
   1822         }
   1823 
   1824         size_t j;
   1825         for (j = i+1; j < ND; j++) {
   1826             if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
   1827                            mDirs.valueAt(j)->getLeaf().string()) == 0) {
   1828                 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
   1829                         "Directory is case-insensitive equivalent to: %s",
   1830                         mDirs.valueAt(j)->getPrintableSource().string());
   1831                 return UNKNOWN_ERROR;
   1832             }
   1833         }
   1834 
   1835         status_t err = mDirs.valueAt(i)->validate();
   1836         if (err != NO_ERROR) {
   1837             return err;
   1838         }
   1839     }
   1840 
   1841     return NO_ERROR;
   1842 }
   1843 
   1844 void AaptDir::print(const String8& prefix) const
   1845 {
   1846     const size_t ND=getDirs().size();
   1847     size_t i;
   1848     for (i=0; i<ND; i++) {
   1849         getDirs().valueAt(i)->print(prefix);
   1850     }
   1851 
   1852     const size_t NF=getFiles().size();
   1853     for (i=0; i<NF; i++) {
   1854         getFiles().valueAt(i)->print(prefix);
   1855     }
   1856 }
   1857 
   1858 String8 AaptDir::getPrintableSource() const
   1859 {
   1860     if (mFiles.size() > 0) {
   1861         // Arbitrarily pull the first file out of the list as the source dir.
   1862         return mFiles.valueAt(0)->getPrintableSource().getPathDir();
   1863     }
   1864     if (mDirs.size() > 0) {
   1865         // Or arbitrarily pull the first dir out of the list as the source dir.
   1866         return mDirs.valueAt(0)->getPrintableSource().getPathDir();
   1867     }
   1868 
   1869     // Should never hit this case, but to be safe...
   1870     return mPath;
   1871 
   1872 }
   1873 
   1874 // =========================================================================
   1875 // =========================================================================
   1876 // =========================================================================
   1877 
   1878 status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
   1879 {
   1880     status_t err = NO_ERROR;
   1881     size_t N = javaSymbols->mSymbols.size();
   1882     for (size_t i=0; i<N; i++) {
   1883         const String8& name = javaSymbols->mSymbols.keyAt(i);
   1884         const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
   1885         ssize_t pos = mSymbols.indexOfKey(name);
   1886         if (pos < 0) {
   1887             entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
   1888             err = UNKNOWN_ERROR;
   1889             continue;
   1890         }
   1891         //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
   1892         //        i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
   1893         mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
   1894     }
   1895 
   1896     N = javaSymbols->mNestedSymbols.size();
   1897     for (size_t i=0; i<N; i++) {
   1898         const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
   1899         const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
   1900         ssize_t pos = mNestedSymbols.indexOfKey(name);
   1901         if (pos < 0) {
   1902             SourcePos pos;
   1903             pos.error("Java symbol dir %s not defined\n", name.string());
   1904             err = UNKNOWN_ERROR;
   1905             continue;
   1906         }
   1907         //printf("**** applying java symbols in dir %s\n", name.string());
   1908         status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
   1909         if (myerr != NO_ERROR) {
   1910             err = myerr;
   1911         }
   1912     }
   1913 
   1914     return err;
   1915 }
   1916 
   1917 // =========================================================================
   1918 // =========================================================================
   1919 // =========================================================================
   1920 
   1921 AaptAssets::AaptAssets()
   1922     : AaptDir(String8(), String8()),
   1923       mChanged(false), mHaveIncludedAssets(false), mRes(NULL)
   1924 {
   1925 }
   1926 
   1927 const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
   1928     if (mChanged) {
   1929     }
   1930     return mGroupEntries;
   1931 }
   1932 
   1933 status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
   1934 {
   1935     mChanged = true;
   1936     return AaptDir::addFile(name, file);
   1937 }
   1938 
   1939 sp<AaptFile> AaptAssets::addFile(
   1940         const String8& filePath, const AaptGroupEntry& entry,
   1941         const String8& srcDir, sp<AaptGroup>* outGroup,
   1942         const String8& resType)
   1943 {
   1944     sp<AaptDir> dir = this;
   1945     sp<AaptGroup> group;
   1946     sp<AaptFile> file;
   1947     String8 root, remain(filePath), partialPath;
   1948     while (remain.length() > 0) {
   1949         root = remain.walkPath(&remain);
   1950         partialPath.appendPath(root);
   1951 
   1952         const String8 rootStr(root);
   1953 
   1954         if (remain.length() == 0) {
   1955             ssize_t i = dir->getFiles().indexOfKey(rootStr);
   1956             if (i >= 0) {
   1957                 group = dir->getFiles().valueAt(i);
   1958             } else {
   1959                 group = new AaptGroup(rootStr, filePath);
   1960                 status_t res = dir->addFile(rootStr, group);
   1961                 if (res != NO_ERROR) {
   1962                     return NULL;
   1963                 }
   1964             }
   1965             file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
   1966             status_t res = group->addFile(file);
   1967             if (res != NO_ERROR) {
   1968                 return NULL;
   1969             }
   1970             break;
   1971 
   1972         } else {
   1973             ssize_t i = dir->getDirs().indexOfKey(rootStr);
   1974             if (i >= 0) {
   1975                 dir = dir->getDirs().valueAt(i);
   1976             } else {
   1977                 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
   1978                 status_t res = dir->addDir(rootStr, subdir);
   1979                 if (res != NO_ERROR) {
   1980                     return NULL;
   1981                 }
   1982                 dir = subdir;
   1983             }
   1984         }
   1985     }
   1986 
   1987     mGroupEntries.add(entry);
   1988     if (outGroup) *outGroup = group;
   1989     return file;
   1990 }
   1991 
   1992 void AaptAssets::addResource(const String8& leafName, const String8& path,
   1993                 const sp<AaptFile>& file, const String8& resType)
   1994 {
   1995     sp<AaptDir> res = AaptDir::makeDir(kResString);
   1996     String8 dirname = file->getGroupEntry().toDirName(resType);
   1997     sp<AaptDir> subdir = res->makeDir(dirname);
   1998     sp<AaptGroup> grr = new AaptGroup(leafName, path);
   1999     grr->addFile(file);
   2000 
   2001     subdir->addFile(leafName, grr);
   2002 }
   2003 
   2004 
   2005 ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
   2006 {
   2007     int count;
   2008     int totalCount = 0;
   2009     FileType type;
   2010     const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
   2011     const size_t dirCount =resDirs.size();
   2012     sp<AaptAssets> current = this;
   2013 
   2014     const int N = bundle->getFileSpecCount();
   2015 
   2016     /*
   2017      * If a package manifest was specified, include that first.
   2018      */
   2019     if (bundle->getAndroidManifestFile() != NULL) {
   2020         // place at root of zip.
   2021         String8 srcFile(bundle->getAndroidManifestFile());
   2022         addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
   2023                 NULL, String8());
   2024         totalCount++;
   2025     }
   2026 
   2027     /*
   2028      * If a directory of custom assets was supplied, slurp 'em up.
   2029      */
   2030     if (bundle->getAssetSourceDir()) {
   2031         const char* assetDir = bundle->getAssetSourceDir();
   2032 
   2033         FileType type = getFileType(assetDir);
   2034         if (type == kFileTypeNonexistent) {
   2035             fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
   2036             return UNKNOWN_ERROR;
   2037         }
   2038         if (type != kFileTypeDirectory) {
   2039             fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
   2040             return UNKNOWN_ERROR;
   2041         }
   2042 
   2043         String8 assetRoot(assetDir);
   2044         sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
   2045         AaptGroupEntry group;
   2046         count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
   2047                                             String8(), mFullAssetPaths);
   2048         if (count < 0) {
   2049             totalCount = count;
   2050             goto bail;
   2051         }
   2052         if (count > 0) {
   2053             mGroupEntries.add(group);
   2054         }
   2055         totalCount += count;
   2056 
   2057         if (bundle->getVerbose())
   2058             printf("Found %d custom asset file%s in %s\n",
   2059                    count, (count==1) ? "" : "s", assetDir);
   2060     }
   2061 
   2062     /*
   2063      * If a directory of resource-specific assets was supplied, slurp 'em up.
   2064      */
   2065     for (size_t i=0; i<dirCount; i++) {
   2066         const char *res = resDirs[i];
   2067         if (res) {
   2068             type = getFileType(res);
   2069             if (type == kFileTypeNonexistent) {
   2070                 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
   2071                 return UNKNOWN_ERROR;
   2072             }
   2073             if (type == kFileTypeDirectory) {
   2074                 if (i>0) {
   2075                     sp<AaptAssets> nextOverlay = new AaptAssets();
   2076                     current->setOverlay(nextOverlay);
   2077                     current = nextOverlay;
   2078                     current->setFullResPaths(mFullResPaths);
   2079                 }
   2080                 count = current->slurpResourceTree(bundle, String8(res));
   2081 
   2082                 if (count < 0) {
   2083                     totalCount = count;
   2084                     goto bail;
   2085                 }
   2086                 totalCount += count;
   2087             }
   2088             else {
   2089                 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
   2090                 return UNKNOWN_ERROR;
   2091             }
   2092         }
   2093 
   2094     }
   2095     /*
   2096      * Now do any additional raw files.
   2097      */
   2098     for (int arg=0; arg<N; arg++) {
   2099         const char* assetDir = bundle->getFileSpecEntry(arg);
   2100 
   2101         FileType type = getFileType(assetDir);
   2102         if (type == kFileTypeNonexistent) {
   2103             fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
   2104             return UNKNOWN_ERROR;
   2105         }
   2106         if (type != kFileTypeDirectory) {
   2107             fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
   2108             return UNKNOWN_ERROR;
   2109         }
   2110 
   2111         String8 assetRoot(assetDir);
   2112 
   2113         if (bundle->getVerbose())
   2114             printf("Processing raw dir '%s'\n", (const char*) assetDir);
   2115 
   2116         /*
   2117          * Do a recursive traversal of subdir tree.  We don't make any
   2118          * guarantees about ordering, so we're okay with an inorder search
   2119          * using whatever order the OS happens to hand back to us.
   2120          */
   2121         count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
   2122         if (count < 0) {
   2123             /* failure; report error and remove archive */
   2124             totalCount = count;
   2125             goto bail;
   2126         }
   2127         totalCount += count;
   2128 
   2129         if (bundle->getVerbose())
   2130             printf("Found %d asset file%s in %s\n",
   2131                    count, (count==1) ? "" : "s", assetDir);
   2132     }
   2133 
   2134     count = validate();
   2135     if (count != NO_ERROR) {
   2136         totalCount = count;
   2137         goto bail;
   2138     }
   2139 
   2140     count = filter(bundle);
   2141     if (count != NO_ERROR) {
   2142         totalCount = count;
   2143         goto bail;
   2144     }
   2145 
   2146 bail:
   2147     return totalCount;
   2148 }
   2149 
   2150 ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
   2151                                     const AaptGroupEntry& kind,
   2152                                     const String8& resType,
   2153                                     sp<FilePathStore>& fullResPaths)
   2154 {
   2155     ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
   2156     if (res > 0) {
   2157         mGroupEntries.add(kind);
   2158     }
   2159 
   2160     return res;
   2161 }
   2162 
   2163 ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
   2164 {
   2165     ssize_t err = 0;
   2166 
   2167     DIR* dir = opendir(srcDir.string());
   2168     if (dir == NULL) {
   2169         fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
   2170         return UNKNOWN_ERROR;
   2171     }
   2172 
   2173     status_t count = 0;
   2174 
   2175     /*
   2176      * Run through the directory, looking for dirs that match the
   2177      * expected pattern.
   2178      */
   2179     while (1) {
   2180         struct dirent* entry = readdir(dir);
   2181         if (entry == NULL) {
   2182             break;
   2183         }
   2184 
   2185         if (isHidden(srcDir.string(), entry->d_name)) {
   2186             continue;
   2187         }
   2188 
   2189         String8 subdirName(srcDir);
   2190         subdirName.appendPath(entry->d_name);
   2191 
   2192         AaptGroupEntry group;
   2193         String8 resType;
   2194         bool b = group.initFromDirName(entry->d_name, &resType);
   2195         if (!b) {
   2196             fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
   2197                     entry->d_name);
   2198             err = -1;
   2199             continue;
   2200         }
   2201 
   2202         if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
   2203             int maxResInt = atoi(bundle->getMaxResVersion());
   2204             const char *verString = group.getVersionString().string();
   2205             int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
   2206             if (dirVersionInt > maxResInt) {
   2207               fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
   2208               continue;
   2209             }
   2210         }
   2211 
   2212         FileType type = getFileType(subdirName.string());
   2213 
   2214         if (type == kFileTypeDirectory) {
   2215             sp<AaptDir> dir = makeDir(resType);
   2216             ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
   2217                                                 resType, mFullResPaths);
   2218             if (res < 0) {
   2219                 count = res;
   2220                 goto bail;
   2221             }
   2222             if (res > 0) {
   2223                 mGroupEntries.add(group);
   2224                 count += res;
   2225             }
   2226 
   2227             // Only add this directory if we don't already have a resource dir
   2228             // for the current type.  This ensures that we only add the dir once
   2229             // for all configs.
   2230             sp<AaptDir> rdir = resDir(resType);
   2231             if (rdir == NULL) {
   2232                 mResDirs.add(dir);
   2233             }
   2234         } else {
   2235             if (bundle->getVerbose()) {
   2236                 fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
   2237             }
   2238         }
   2239     }
   2240 
   2241 bail:
   2242     closedir(dir);
   2243     dir = NULL;
   2244 
   2245     if (err != 0) {
   2246         return err;
   2247     }
   2248     return count;
   2249 }
   2250 
   2251 ssize_t
   2252 AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
   2253 {
   2254     int count = 0;
   2255     SortedVector<AaptGroupEntry> entries;
   2256 
   2257     ZipFile* zip = new ZipFile;
   2258     status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
   2259     if (err != NO_ERROR) {
   2260         fprintf(stderr, "error opening zip file %s\n", filename);
   2261         count = err;
   2262         delete zip;
   2263         return -1;
   2264     }
   2265 
   2266     const int N = zip->getNumEntries();
   2267     for (int i=0; i<N; i++) {
   2268         ZipEntry* entry = zip->getEntryByIndex(i);
   2269         if (entry->getDeleted()) {
   2270             continue;
   2271         }
   2272 
   2273         String8 entryName(entry->getFileName());
   2274 
   2275         String8 dirName = entryName.getPathDir();
   2276         sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
   2277 
   2278         String8 resType;
   2279         AaptGroupEntry kind;
   2280 
   2281         String8 remain;
   2282         if (entryName.walkPath(&remain) == kResourceDir) {
   2283             // these are the resources, pull their type out of the directory name
   2284             kind.initFromDirName(remain.walkPath().string(), &resType);
   2285         } else {
   2286             // these are untyped and don't have an AaptGroupEntry
   2287         }
   2288         if (entries.indexOf(kind) < 0) {
   2289             entries.add(kind);
   2290             mGroupEntries.add(kind);
   2291         }
   2292 
   2293         // use the one from the zip file if they both exist.
   2294         dir->removeFile(entryName.getPathLeaf());
   2295 
   2296         sp<AaptFile> file = new AaptFile(entryName, kind, resType);
   2297         status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
   2298         if (err != NO_ERROR) {
   2299             fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
   2300             count = err;
   2301             goto bail;
   2302         }
   2303         file->setCompressionMethod(entry->getCompressionMethod());
   2304 
   2305 #if 0
   2306         if (entryName == "AndroidManifest.xml") {
   2307             printf("AndroidManifest.xml\n");
   2308         }
   2309         printf("\n\nfile: %s\n", entryName.string());
   2310 #endif
   2311 
   2312         size_t len = entry->getUncompressedLen();
   2313         void* data = zip->uncompress(entry);
   2314         void* buf = file->editData(len);
   2315         memcpy(buf, data, len);
   2316 
   2317 #if 0
   2318         const int OFF = 0;
   2319         const unsigned char* p = (unsigned char*)data;
   2320         const unsigned char* end = p+len;
   2321         p += OFF;
   2322         for (int i=0; i<32 && p < end; i++) {
   2323             printf("0x%03x ", i*0x10 + OFF);
   2324             for (int j=0; j<0x10 && p < end; j++) {
   2325                 printf(" %02x", *p);
   2326                 p++;
   2327             }
   2328             printf("\n");
   2329         }
   2330 #endif
   2331 
   2332         free(data);
   2333 
   2334         count++;
   2335     }
   2336 
   2337 bail:
   2338     delete zip;
   2339     return count;
   2340 }
   2341 
   2342 status_t AaptAssets::filter(Bundle* bundle)
   2343 {
   2344     ResourceFilter reqFilter;
   2345     status_t err = reqFilter.parse(bundle->getConfigurations());
   2346     if (err != NO_ERROR) {
   2347         return err;
   2348     }
   2349 
   2350     ResourceFilter prefFilter;
   2351     err = prefFilter.parse(bundle->getPreferredConfigurations());
   2352     if (err != NO_ERROR) {
   2353         return err;
   2354     }
   2355 
   2356     if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
   2357         return NO_ERROR;
   2358     }
   2359 
   2360     if (bundle->getVerbose()) {
   2361         if (!reqFilter.isEmpty()) {
   2362             printf("Applying required filter: %s\n",
   2363                     bundle->getConfigurations());
   2364         }
   2365         if (!prefFilter.isEmpty()) {
   2366             printf("Applying preferred filter: %s\n",
   2367                     bundle->getPreferredConfigurations());
   2368         }
   2369     }
   2370 
   2371     const Vector<sp<AaptDir> >& resdirs = mResDirs;
   2372     const size_t ND = resdirs.size();
   2373     for (size_t i=0; i<ND; i++) {
   2374         const sp<AaptDir>& dir = resdirs.itemAt(i);
   2375         if (dir->getLeaf() == kValuesDir) {
   2376             // The "value" dir is special since a single file defines
   2377             // multiple resources, so we can not do filtering on the
   2378             // files themselves.
   2379             continue;
   2380         }
   2381         if (dir->getLeaf() == kMipmapDir) {
   2382             // We also skip the "mipmap" directory, since the point of this
   2383             // is to include all densities without stripping.  If you put
   2384             // other configurations in here as well they won't be stripped
   2385             // either...  So don't do that.  Seriously.  What is wrong with you?
   2386             continue;
   2387         }
   2388 
   2389         const size_t NG = dir->getFiles().size();
   2390         for (size_t j=0; j<NG; j++) {
   2391             sp<AaptGroup> grp = dir->getFiles().valueAt(j);
   2392 
   2393             // First remove any configurations we know we don't need.
   2394             for (size_t k=0; k<grp->getFiles().size(); k++) {
   2395                 sp<AaptFile> file = grp->getFiles().valueAt(k);
   2396                 if (k == 0 && grp->getFiles().size() == 1) {
   2397                     // If this is the only file left, we need to keep it.
   2398                     // Otherwise the resource IDs we are using will be inconsistent
   2399                     // with what we get when not stripping.  Sucky, but at least
   2400                     // for now we can rely on the back-end doing another filtering
   2401                     // pass to take this out and leave us with this resource name
   2402                     // containing no entries.
   2403                     continue;
   2404                 }
   2405                 if (file->getPath().getPathExtension() == ".xml") {
   2406                     // We can't remove .xml files at this point, because when
   2407                     // we parse them they may add identifier resources, so
   2408                     // removing them can cause our resource identifiers to
   2409                     // become inconsistent.
   2410                     continue;
   2411                 }
   2412                 const ResTable_config& config(file->getGroupEntry().toParams());
   2413                 if (!reqFilter.match(config)) {
   2414                     if (bundle->getVerbose()) {
   2415                         printf("Pruning unneeded resource: %s\n",
   2416                                 file->getPrintableSource().string());
   2417                     }
   2418                     grp->removeFile(k);
   2419                     k--;
   2420                 }
   2421             }
   2422 
   2423             // Quick check: no preferred filters, nothing more to do.
   2424             if (prefFilter.isEmpty()) {
   2425                 continue;
   2426             }
   2427 
   2428             // Now deal with preferred configurations.
   2429             for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
   2430                 for (size_t k=0; k<grp->getFiles().size(); k++) {
   2431                     sp<AaptFile> file = grp->getFiles().valueAt(k);
   2432                     if (k == 0 && grp->getFiles().size() == 1) {
   2433                         // If this is the only file left, we need to keep it.
   2434                         // Otherwise the resource IDs we are using will be inconsistent
   2435                         // with what we get when not stripping.  Sucky, but at least
   2436                         // for now we can rely on the back-end doing another filtering
   2437                         // pass to take this out and leave us with this resource name
   2438                         // containing no entries.
   2439                         continue;
   2440                     }
   2441                     if (file->getPath().getPathExtension() == ".xml") {
   2442                         // We can't remove .xml files at this point, because when
   2443                         // we parse them they may add identifier resources, so
   2444                         // removing them can cause our resource identifiers to
   2445                         // become inconsistent.
   2446                         continue;
   2447                     }
   2448                     const ResTable_config& config(file->getGroupEntry().toParams());
   2449                     if (!prefFilter.match(axis, config)) {
   2450                         // This is a resource we would prefer not to have.  Check
   2451                         // to see if have a similar variation that we would like
   2452                         // to have and, if so, we can drop it.
   2453                         for (size_t m=0; m<grp->getFiles().size(); m++) {
   2454                             if (m == k) continue;
   2455                             sp<AaptFile> mfile = grp->getFiles().valueAt(m);
   2456                             const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
   2457                             if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
   2458                                 if (prefFilter.match(axis, mconfig)) {
   2459                                     if (bundle->getVerbose()) {
   2460                                         printf("Pruning unneeded resource: %s\n",
   2461                                                 file->getPrintableSource().string());
   2462                                     }
   2463                                     grp->removeFile(k);
   2464                                     k--;
   2465                                     break;
   2466                                 }
   2467                             }
   2468                         }
   2469                     }
   2470                 }
   2471             }
   2472         }
   2473     }
   2474 
   2475     return NO_ERROR;
   2476 }
   2477 
   2478 sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
   2479 {
   2480     sp<AaptSymbols> sym = mSymbols.valueFor(name);
   2481     if (sym == NULL) {
   2482         sym = new AaptSymbols();
   2483         mSymbols.add(name, sym);
   2484     }
   2485     return sym;
   2486 }
   2487 
   2488 sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
   2489 {
   2490     sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
   2491     if (sym == NULL) {
   2492         sym = new AaptSymbols();
   2493         mJavaSymbols.add(name, sym);
   2494     }
   2495     return sym;
   2496 }
   2497 
   2498 status_t AaptAssets::applyJavaSymbols()
   2499 {
   2500     size_t N = mJavaSymbols.size();
   2501     for (size_t i=0; i<N; i++) {
   2502         const String8& name = mJavaSymbols.keyAt(i);
   2503         const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
   2504         ssize_t pos = mSymbols.indexOfKey(name);
   2505         if (pos < 0) {
   2506             SourcePos pos;
   2507             pos.error("Java symbol dir %s not defined\n", name.string());
   2508             return UNKNOWN_ERROR;
   2509         }
   2510         //printf("**** applying java symbols in dir %s\n", name.string());
   2511         status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
   2512         if (err != NO_ERROR) {
   2513             return err;
   2514         }
   2515     }
   2516 
   2517     return NO_ERROR;
   2518 }
   2519 
   2520 bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
   2521     //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
   2522     //        sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
   2523     //        sym.isJavaSymbol ? 1 : 0);
   2524     if (!mHavePrivateSymbols) return true;
   2525     if (sym.isPublic) return true;
   2526     if (includePrivate && sym.isJavaSymbol) return true;
   2527     return false;
   2528 }
   2529 
   2530 status_t AaptAssets::buildIncludedResources(Bundle* bundle)
   2531 {
   2532     if (!mHaveIncludedAssets) {
   2533         // Add in all includes.
   2534         const Vector<const char*>& incl = bundle->getPackageIncludes();
   2535         const size_t N=incl.size();
   2536         for (size_t i=0; i<N; i++) {
   2537             if (bundle->getVerbose())
   2538                 printf("Including resources from package: %s\n", incl[i]);
   2539             if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
   2540                 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
   2541                         incl[i]);
   2542                 return UNKNOWN_ERROR;
   2543             }
   2544         }
   2545         mHaveIncludedAssets = true;
   2546     }
   2547 
   2548     return NO_ERROR;
   2549 }
   2550 
   2551 status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
   2552 {
   2553     const ResTable& res = getIncludedResources();
   2554     // XXX dirty!
   2555     return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
   2556 }
   2557 
   2558 const ResTable& AaptAssets::getIncludedResources() const
   2559 {
   2560     return mIncludedAssets.getResources(false);
   2561 }
   2562 
   2563 void AaptAssets::print(const String8& prefix) const
   2564 {
   2565     String8 innerPrefix(prefix);
   2566     innerPrefix.append("  ");
   2567     String8 innerInnerPrefix(innerPrefix);
   2568     innerInnerPrefix.append("  ");
   2569     printf("%sConfigurations:\n", prefix.string());
   2570     const size_t N=mGroupEntries.size();
   2571     for (size_t i=0; i<N; i++) {
   2572         String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
   2573         printf("%s %s\n", prefix.string(),
   2574                 cname != "" ? cname.string() : "(default)");
   2575     }
   2576 
   2577     printf("\n%sFiles:\n", prefix.string());
   2578     AaptDir::print(innerPrefix);
   2579 
   2580     printf("\n%sResource Dirs:\n", prefix.string());
   2581     const Vector<sp<AaptDir> >& resdirs = mResDirs;
   2582     const size_t NR = resdirs.size();
   2583     for (size_t i=0; i<NR; i++) {
   2584         const sp<AaptDir>& d = resdirs.itemAt(i);
   2585         printf("%s  Type %s\n", prefix.string(), d->getLeaf().string());
   2586         d->print(innerInnerPrefix);
   2587     }
   2588 }
   2589 
   2590 sp<AaptDir> AaptAssets::resDir(const String8& name) const
   2591 {
   2592     const Vector<sp<AaptDir> >& resdirs = mResDirs;
   2593     const size_t N = resdirs.size();
   2594     for (size_t i=0; i<N; i++) {
   2595         const sp<AaptDir>& d = resdirs.itemAt(i);
   2596         if (d->getLeaf() == name) {
   2597             return d;
   2598         }
   2599     }
   2600     return NULL;
   2601 }
   2602 
   2603 bool
   2604 valid_symbol_name(const String8& symbol)
   2605 {
   2606     static char const * const KEYWORDS[] = {
   2607         "abstract", "assert", "boolean", "break",
   2608         "byte", "case", "catch", "char", "class", "const", "continue",
   2609         "default", "do", "double", "else", "enum", "extends", "final",
   2610         "finally", "float", "for", "goto", "if", "implements", "import",
   2611         "instanceof", "int", "interface", "long", "native", "new", "package",
   2612         "private", "protected", "public", "return", "short", "static",
   2613         "strictfp", "super", "switch", "synchronized", "this", "throw",
   2614         "throws", "transient", "try", "void", "volatile", "while",
   2615         "true", "false", "null",
   2616         NULL
   2617     };
   2618     const char*const* k = KEYWORDS;
   2619     const char*const s = symbol.string();
   2620     while (*k) {
   2621         if (0 == strcmp(s, *k)) {
   2622             return false;
   2623         }
   2624         k++;
   2625     }
   2626     return true;
   2627 }
   2628