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