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         if (out->mnc == 0) {
    938             out->mnc = ACONFIGURATION_MNC_ZERO;
    939         }
    940     }
    941 
    942     return true;
    943 }
    944 
    945 /*
    946  * Does this directory name fit the pattern of a locale dir ("en-rUS" or
    947  * "default")?
    948  *
    949  * TODO: Should insist that the first two letters are lower case, and the
    950  * second two are upper.
    951  */
    952 bool AaptGroupEntry::getLocaleName(const char* fileName,
    953                                    ResTable_config* out)
    954 {
    955     if (strcmp(fileName, kWildcardName) == 0
    956             || strcmp(fileName, kDefaultLocale) == 0) {
    957         if (out) {
    958             out->language[0] = 0;
    959             out->language[1] = 0;
    960             out->country[0] = 0;
    961             out->country[1] = 0;
    962         }
    963         return true;
    964     }
    965 
    966     if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) {
    967         if (out) {
    968             out->language[0] = fileName[0];
    969             out->language[1] = fileName[1];
    970             out->country[0] = 0;
    971             out->country[1] = 0;
    972         }
    973         return true;
    974     }
    975 
    976     if (strlen(fileName) == 5 &&
    977         isalpha(fileName[0]) &&
    978         isalpha(fileName[1]) &&
    979         fileName[2] == '-' &&
    980         isalpha(fileName[3]) &&
    981         isalpha(fileName[4])) {
    982         if (out) {
    983             out->language[0] = fileName[0];
    984             out->language[1] = fileName[1];
    985             out->country[0] = fileName[3];
    986             out->country[1] = fileName[4];
    987         }
    988         return true;
    989     }
    990 
    991     return false;
    992 }
    993 
    994 bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out)
    995 {
    996     if (strcmp(name, kWildcardName) == 0) {
    997         if (out) out->screenLayout =
    998                 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
    999                 | ResTable_config::LAYOUTDIR_ANY;
   1000         return true;
   1001     } else if (strcmp(name, "ldltr") == 0) {
   1002         if (out) out->screenLayout =
   1003                 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
   1004                 | ResTable_config::LAYOUTDIR_LTR;
   1005         return true;
   1006     } else if (strcmp(name, "ldrtl") == 0) {
   1007         if (out) out->screenLayout =
   1008                 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
   1009                 | ResTable_config::LAYOUTDIR_RTL;
   1010         return true;
   1011     }
   1012 
   1013     return false;
   1014 }
   1015 
   1016 bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
   1017                                      ResTable_config* out)
   1018 {
   1019     if (strcmp(name, kWildcardName) == 0) {
   1020         if (out) out->screenLayout =
   1021                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
   1022                 | ResTable_config::SCREENSIZE_ANY;
   1023         return true;
   1024     } else if (strcmp(name, "small") == 0) {
   1025         if (out) out->screenLayout =
   1026                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
   1027                 | ResTable_config::SCREENSIZE_SMALL;
   1028         return true;
   1029     } else if (strcmp(name, "normal") == 0) {
   1030         if (out) out->screenLayout =
   1031                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
   1032                 | ResTable_config::SCREENSIZE_NORMAL;
   1033         return true;
   1034     } else if (strcmp(name, "large") == 0) {
   1035         if (out) out->screenLayout =
   1036                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
   1037                 | ResTable_config::SCREENSIZE_LARGE;
   1038         return true;
   1039     } else if (strcmp(name, "xlarge") == 0) {
   1040         if (out) out->screenLayout =
   1041                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
   1042                 | ResTable_config::SCREENSIZE_XLARGE;
   1043         return true;
   1044     }
   1045 
   1046     return false;
   1047 }
   1048 
   1049 bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
   1050                                      ResTable_config* out)
   1051 {
   1052     if (strcmp(name, kWildcardName) == 0) {
   1053         if (out) out->screenLayout =
   1054                 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
   1055                 | ResTable_config::SCREENLONG_ANY;
   1056         return true;
   1057     } else if (strcmp(name, "long") == 0) {
   1058         if (out) out->screenLayout =
   1059                 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
   1060                 | ResTable_config::SCREENLONG_YES;
   1061         return true;
   1062     } else if (strcmp(name, "notlong") == 0) {
   1063         if (out) out->screenLayout =
   1064                 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
   1065                 | ResTable_config::SCREENLONG_NO;
   1066         return true;
   1067     }
   1068 
   1069     return false;
   1070 }
   1071 
   1072 bool AaptGroupEntry::getOrientationName(const char* name,
   1073                                         ResTable_config* out)
   1074 {
   1075     if (strcmp(name, kWildcardName) == 0) {
   1076         if (out) out->orientation = out->ORIENTATION_ANY;
   1077         return true;
   1078     } else if (strcmp(name, "port") == 0) {
   1079         if (out) out->orientation = out->ORIENTATION_PORT;
   1080         return true;
   1081     } else if (strcmp(name, "land") == 0) {
   1082         if (out) out->orientation = out->ORIENTATION_LAND;
   1083         return true;
   1084     } else if (strcmp(name, "square") == 0) {
   1085         if (out) out->orientation = out->ORIENTATION_SQUARE;
   1086         return true;
   1087     }
   1088 
   1089     return false;
   1090 }
   1091 
   1092 bool AaptGroupEntry::getUiModeTypeName(const char* name,
   1093                                        ResTable_config* out)
   1094 {
   1095     if (strcmp(name, kWildcardName) == 0) {
   1096         if (out) out->uiMode =
   1097                 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
   1098                 | ResTable_config::UI_MODE_TYPE_ANY;
   1099         return true;
   1100     } else if (strcmp(name, "desk") == 0) {
   1101       if (out) out->uiMode =
   1102               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
   1103               | ResTable_config::UI_MODE_TYPE_DESK;
   1104         return true;
   1105     } else if (strcmp(name, "car") == 0) {
   1106       if (out) out->uiMode =
   1107               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
   1108               | ResTable_config::UI_MODE_TYPE_CAR;
   1109         return true;
   1110     } else if (strcmp(name, "television") == 0) {
   1111       if (out) out->uiMode =
   1112               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
   1113               | ResTable_config::UI_MODE_TYPE_TELEVISION;
   1114         return true;
   1115     } else if (strcmp(name, "appliance") == 0) {
   1116       if (out) out->uiMode =
   1117               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
   1118               | ResTable_config::UI_MODE_TYPE_APPLIANCE;
   1119         return true;
   1120     }
   1121 
   1122     return false;
   1123 }
   1124 
   1125 bool AaptGroupEntry::getUiModeNightName(const char* name,
   1126                                           ResTable_config* out)
   1127 {
   1128     if (strcmp(name, kWildcardName) == 0) {
   1129         if (out) out->uiMode =
   1130                 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
   1131                 | ResTable_config::UI_MODE_NIGHT_ANY;
   1132         return true;
   1133     } else if (strcmp(name, "night") == 0) {
   1134         if (out) out->uiMode =
   1135                 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
   1136                 | ResTable_config::UI_MODE_NIGHT_YES;
   1137         return true;
   1138     } else if (strcmp(name, "notnight") == 0) {
   1139       if (out) out->uiMode =
   1140               (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
   1141               | ResTable_config::UI_MODE_NIGHT_NO;
   1142         return true;
   1143     }
   1144 
   1145     return false;
   1146 }
   1147 
   1148 bool AaptGroupEntry::getDensityName(const char* name,
   1149                                     ResTable_config* out)
   1150 {
   1151     if (strcmp(name, kWildcardName) == 0) {
   1152         if (out) out->density = ResTable_config::DENSITY_DEFAULT;
   1153         return true;
   1154     }
   1155 
   1156     if (strcmp(name, "nodpi") == 0) {
   1157         if (out) out->density = ResTable_config::DENSITY_NONE;
   1158         return true;
   1159     }
   1160 
   1161     if (strcmp(name, "ldpi") == 0) {
   1162         if (out) out->density = ResTable_config::DENSITY_LOW;
   1163         return true;
   1164     }
   1165 
   1166     if (strcmp(name, "mdpi") == 0) {
   1167         if (out) out->density = ResTable_config::DENSITY_MEDIUM;
   1168         return true;
   1169     }
   1170 
   1171     if (strcmp(name, "tvdpi") == 0) {
   1172         if (out) out->density = ResTable_config::DENSITY_TV;
   1173         return true;
   1174     }
   1175 
   1176     if (strcmp(name, "hdpi") == 0) {
   1177         if (out) out->density = ResTable_config::DENSITY_HIGH;
   1178         return true;
   1179     }
   1180 
   1181     if (strcmp(name, "xhdpi") == 0) {
   1182         if (out) out->density = ResTable_config::DENSITY_XHIGH;
   1183         return true;
   1184     }
   1185 
   1186     if (strcmp(name, "xxhdpi") == 0) {
   1187         if (out) out->density = ResTable_config::DENSITY_XXHIGH;
   1188         return true;
   1189     }
   1190 
   1191     if (strcmp(name, "xxxhdpi") == 0) {
   1192         if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
   1193         return true;
   1194     }
   1195 
   1196     char* c = (char*)name;
   1197     while (*c >= '0' && *c <= '9') {
   1198         c++;
   1199     }
   1200 
   1201     // check that we have 'dpi' after the last digit.
   1202     if (toupper(c[0]) != 'D' ||
   1203             toupper(c[1]) != 'P' ||
   1204             toupper(c[2]) != 'I' ||
   1205             c[3] != 0) {
   1206         return false;
   1207     }
   1208 
   1209     // temporarily replace the first letter with \0 to
   1210     // use atoi.
   1211     char tmp = c[0];
   1212     c[0] = '\0';
   1213 
   1214     int d = atoi(name);
   1215     c[0] = tmp;
   1216 
   1217     if (d != 0) {
   1218         if (out) out->density = d;
   1219         return true;
   1220     }
   1221 
   1222     return false;
   1223 }
   1224 
   1225 bool AaptGroupEntry::getTouchscreenName(const char* name,
   1226                                         ResTable_config* out)
   1227 {
   1228     if (strcmp(name, kWildcardName) == 0) {
   1229         if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
   1230         return true;
   1231     } else if (strcmp(name, "notouch") == 0) {
   1232         if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
   1233         return true;
   1234     } else if (strcmp(name, "stylus") == 0) {
   1235         if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
   1236         return true;
   1237     } else if (strcmp(name, "finger") == 0) {
   1238         if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
   1239         return true;
   1240     }
   1241 
   1242     return false;
   1243 }
   1244 
   1245 bool AaptGroupEntry::getKeysHiddenName(const char* name,
   1246                                        ResTable_config* out)
   1247 {
   1248     uint8_t mask = 0;
   1249     uint8_t value = 0;
   1250     if (strcmp(name, kWildcardName) == 0) {
   1251         mask = ResTable_config::MASK_KEYSHIDDEN;
   1252         value = ResTable_config::KEYSHIDDEN_ANY;
   1253     } else if (strcmp(name, "keysexposed") == 0) {
   1254         mask = ResTable_config::MASK_KEYSHIDDEN;
   1255         value = ResTable_config::KEYSHIDDEN_NO;
   1256     } else if (strcmp(name, "keyshidden") == 0) {
   1257         mask = ResTable_config::MASK_KEYSHIDDEN;
   1258         value = ResTable_config::KEYSHIDDEN_YES;
   1259     } else if (strcmp(name, "keyssoft") == 0) {
   1260         mask = ResTable_config::MASK_KEYSHIDDEN;
   1261         value = ResTable_config::KEYSHIDDEN_SOFT;
   1262     }
   1263 
   1264     if (mask != 0) {
   1265         if (out) out->inputFlags = (out->inputFlags&~mask) | value;
   1266         return true;
   1267     }
   1268 
   1269     return false;
   1270 }
   1271 
   1272 bool AaptGroupEntry::getKeyboardName(const char* name,
   1273                                         ResTable_config* out)
   1274 {
   1275     if (strcmp(name, kWildcardName) == 0) {
   1276         if (out) out->keyboard = out->KEYBOARD_ANY;
   1277         return true;
   1278     } else if (strcmp(name, "nokeys") == 0) {
   1279         if (out) out->keyboard = out->KEYBOARD_NOKEYS;
   1280         return true;
   1281     } else if (strcmp(name, "qwerty") == 0) {
   1282         if (out) out->keyboard = out->KEYBOARD_QWERTY;
   1283         return true;
   1284     } else if (strcmp(name, "12key") == 0) {
   1285         if (out) out->keyboard = out->KEYBOARD_12KEY;
   1286         return true;
   1287     }
   1288 
   1289     return false;
   1290 }
   1291 
   1292 bool AaptGroupEntry::getNavHiddenName(const char* name,
   1293                                        ResTable_config* out)
   1294 {
   1295     uint8_t mask = 0;
   1296     uint8_t value = 0;
   1297     if (strcmp(name, kWildcardName) == 0) {
   1298         mask = ResTable_config::MASK_NAVHIDDEN;
   1299         value = ResTable_config::NAVHIDDEN_ANY;
   1300     } else if (strcmp(name, "navexposed") == 0) {
   1301         mask = ResTable_config::MASK_NAVHIDDEN;
   1302         value = ResTable_config::NAVHIDDEN_NO;
   1303     } else if (strcmp(name, "navhidden") == 0) {
   1304         mask = ResTable_config::MASK_NAVHIDDEN;
   1305         value = ResTable_config::NAVHIDDEN_YES;
   1306     }
   1307 
   1308     if (mask != 0) {
   1309         if (out) out->inputFlags = (out->inputFlags&~mask) | value;
   1310         return true;
   1311     }
   1312 
   1313     return false;
   1314 }
   1315 
   1316 bool AaptGroupEntry::getNavigationName(const char* name,
   1317                                      ResTable_config* out)
   1318 {
   1319     if (strcmp(name, kWildcardName) == 0) {
   1320         if (out) out->navigation = out->NAVIGATION_ANY;
   1321         return true;
   1322     } else if (strcmp(name, "nonav") == 0) {
   1323         if (out) out->navigation = out->NAVIGATION_NONAV;
   1324         return true;
   1325     } else if (strcmp(name, "dpad") == 0) {
   1326         if (out) out->navigation = out->NAVIGATION_DPAD;
   1327         return true;
   1328     } else if (strcmp(name, "trackball") == 0) {
   1329         if (out) out->navigation = out->NAVIGATION_TRACKBALL;
   1330         return true;
   1331     } else if (strcmp(name, "wheel") == 0) {
   1332         if (out) out->navigation = out->NAVIGATION_WHEEL;
   1333         return true;
   1334     }
   1335 
   1336     return false;
   1337 }
   1338 
   1339 bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
   1340 {
   1341     if (strcmp(name, kWildcardName) == 0) {
   1342         if (out) {
   1343             out->screenWidth = out->SCREENWIDTH_ANY;
   1344             out->screenHeight = out->SCREENHEIGHT_ANY;
   1345         }
   1346         return true;
   1347     }
   1348 
   1349     const char* x = name;
   1350     while (*x >= '0' && *x <= '9') x++;
   1351     if (x == name || *x != 'x') return false;
   1352     String8 xName(name, x-name);
   1353     x++;
   1354 
   1355     const char* y = x;
   1356     while (*y >= '0' && *y <= '9') y++;
   1357     if (y == name || *y != 0) return false;
   1358     String8 yName(x, y-x);
   1359 
   1360     uint16_t w = (uint16_t)atoi(xName.string());
   1361     uint16_t h = (uint16_t)atoi(yName.string());
   1362     if (w < h) {
   1363         return false;
   1364     }
   1365 
   1366     if (out) {
   1367         out->screenWidth = w;
   1368         out->screenHeight = h;
   1369     }
   1370 
   1371     return true;
   1372 }
   1373 
   1374 bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
   1375 {
   1376     if (strcmp(name, kWildcardName) == 0) {
   1377         if (out) {
   1378             out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
   1379         }
   1380         return true;
   1381     }
   1382 
   1383     if (*name != 's') return false;
   1384     name++;
   1385     if (*name != 'w') return false;
   1386     name++;
   1387     const char* x = name;
   1388     while (*x >= '0' && *x <= '9') x++;
   1389     if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
   1390     String8 xName(name, x-name);
   1391 
   1392     if (out) {
   1393         out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
   1394     }
   1395 
   1396     return true;
   1397 }
   1398 
   1399 bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
   1400 {
   1401     if (strcmp(name, kWildcardName) == 0) {
   1402         if (out) {
   1403             out->screenWidthDp = out->SCREENWIDTH_ANY;
   1404         }
   1405         return true;
   1406     }
   1407 
   1408     if (*name != 'w') return false;
   1409     name++;
   1410     const char* x = name;
   1411     while (*x >= '0' && *x <= '9') x++;
   1412     if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
   1413     String8 xName(name, x-name);
   1414 
   1415     if (out) {
   1416         out->screenWidthDp = (uint16_t)atoi(xName.string());
   1417     }
   1418 
   1419     return true;
   1420 }
   1421 
   1422 bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
   1423 {
   1424     if (strcmp(name, kWildcardName) == 0) {
   1425         if (out) {
   1426             out->screenHeightDp = out->SCREENWIDTH_ANY;
   1427         }
   1428         return true;
   1429     }
   1430 
   1431     if (*name != 'h') return false;
   1432     name++;
   1433     const char* x = name;
   1434     while (*x >= '0' && *x <= '9') x++;
   1435     if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
   1436     String8 xName(name, x-name);
   1437 
   1438     if (out) {
   1439         out->screenHeightDp = (uint16_t)atoi(xName.string());
   1440     }
   1441 
   1442     return true;
   1443 }
   1444 
   1445 bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
   1446 {
   1447     if (strcmp(name, kWildcardName) == 0) {
   1448         if (out) {
   1449             out->sdkVersion = out->SDKVERSION_ANY;
   1450             out->minorVersion = out->MINORVERSION_ANY;
   1451         }
   1452         return true;
   1453     }
   1454 
   1455     if (*name != 'v') {
   1456         return false;
   1457     }
   1458 
   1459     name++;
   1460     const char* s = name;
   1461     while (*s >= '0' && *s <= '9') s++;
   1462     if (s == name || *s != 0) return false;
   1463     String8 sdkName(name, s-name);
   1464 
   1465     if (out) {
   1466         out->sdkVersion = (uint16_t)atoi(sdkName.string());
   1467         out->minorVersion = 0;
   1468     }
   1469 
   1470     return true;
   1471 }
   1472 
   1473 int AaptGroupEntry::compare(const AaptGroupEntry& o) const
   1474 {
   1475     int v = mcc.compare(o.mcc);
   1476     if (v == 0) v = mnc.compare(o.mnc);
   1477     if (v == 0) v = locale.compare(o.locale);
   1478     if (v == 0) v = layoutDirection.compare(o.layoutDirection);
   1479     if (v == 0) v = vendor.compare(o.vendor);
   1480     if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
   1481     if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
   1482     if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
   1483     if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
   1484     if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
   1485     if (v == 0) v = orientation.compare(o.orientation);
   1486     if (v == 0) v = uiModeType.compare(o.uiModeType);
   1487     if (v == 0) v = uiModeNight.compare(o.uiModeNight);
   1488     if (v == 0) v = density.compare(o.density);
   1489     if (v == 0) v = touchscreen.compare(o.touchscreen);
   1490     if (v == 0) v = keysHidden.compare(o.keysHidden);
   1491     if (v == 0) v = keyboard.compare(o.keyboard);
   1492     if (v == 0) v = navHidden.compare(o.navHidden);
   1493     if (v == 0) v = navigation.compare(o.navigation);
   1494     if (v == 0) v = screenSize.compare(o.screenSize);
   1495     if (v == 0) v = version.compare(o.version);
   1496     return v;
   1497 }
   1498 
   1499 const ResTable_config& AaptGroupEntry::toParams() const
   1500 {
   1501     if (!mParamsChanged) {
   1502         return mParams;
   1503     }
   1504 
   1505     mParamsChanged = false;
   1506     ResTable_config& params(mParams);
   1507     memset(&params, 0, sizeof(params));
   1508     getMccName(mcc.string(), &params);
   1509     getMncName(mnc.string(), &params);
   1510     getLocaleName(locale.string(), &params);
   1511     getLayoutDirectionName(layoutDirection.string(), &params);
   1512     getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
   1513     getScreenWidthDpName(screenWidthDp.string(), &params);
   1514     getScreenHeightDpName(screenHeightDp.string(), &params);
   1515     getScreenLayoutSizeName(screenLayoutSize.string(), &params);
   1516     getScreenLayoutLongName(screenLayoutLong.string(), &params);
   1517     getOrientationName(orientation.string(), &params);
   1518     getUiModeTypeName(uiModeType.string(), &params);
   1519     getUiModeNightName(uiModeNight.string(), &params);
   1520     getDensityName(density.string(), &params);
   1521     getTouchscreenName(touchscreen.string(), &params);
   1522     getKeysHiddenName(keysHidden.string(), &params);
   1523     getKeyboardName(keyboard.string(), &params);
   1524     getNavHiddenName(navHidden.string(), &params);
   1525     getNavigationName(navigation.string(), &params);
   1526     getScreenSizeName(screenSize.string(), &params);
   1527     getVersionName(version.string(), &params);
   1528 
   1529     // Fix up version number based on specified parameters.
   1530     int minSdk = 0;
   1531     if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
   1532             || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
   1533             || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
   1534         minSdk = SDK_HONEYCOMB_MR2;
   1535     } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
   1536                 != ResTable_config::UI_MODE_TYPE_ANY
   1537             ||  (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
   1538                 != ResTable_config::UI_MODE_NIGHT_ANY) {
   1539         minSdk = SDK_FROYO;
   1540     } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
   1541                 != ResTable_config::SCREENSIZE_ANY
   1542             ||  (params.screenLayout&ResTable_config::MASK_SCREENLONG)
   1543                 != ResTable_config::SCREENLONG_ANY
   1544             || params.density != ResTable_config::DENSITY_DEFAULT) {
   1545         minSdk = SDK_DONUT;
   1546     }
   1547 
   1548     if (minSdk > params.sdkVersion) {
   1549         params.sdkVersion = minSdk;
   1550     }
   1551 
   1552     return params;
   1553 }
   1554 
   1555 // =========================================================================
   1556 // =========================================================================
   1557 // =========================================================================
   1558 
   1559 void* AaptFile::editData(size_t size)
   1560 {
   1561     if (size <= mBufferSize) {
   1562         mDataSize = size;
   1563         return mData;
   1564     }
   1565     size_t allocSize = (size*3)/2;
   1566     void* buf = realloc(mData, allocSize);
   1567     if (buf == NULL) {
   1568         return NULL;
   1569     }
   1570     mData = buf;
   1571     mDataSize = size;
   1572     mBufferSize = allocSize;
   1573     return buf;
   1574 }
   1575 
   1576 void* AaptFile::editData(size_t* outSize)
   1577 {
   1578     if (outSize) {
   1579         *outSize = mDataSize;
   1580     }
   1581     return mData;
   1582 }
   1583 
   1584 void* AaptFile::padData(size_t wordSize)
   1585 {
   1586     const size_t extra = mDataSize%wordSize;
   1587     if (extra == 0) {
   1588         return mData;
   1589     }
   1590 
   1591     size_t initial = mDataSize;
   1592     void* data = editData(initial+(wordSize-extra));
   1593     if (data != NULL) {
   1594         memset(((uint8_t*)data) + initial, 0, wordSize-extra);
   1595     }
   1596     return data;
   1597 }
   1598 
   1599 status_t AaptFile::writeData(const void* data, size_t size)
   1600 {
   1601     size_t end = mDataSize;
   1602     size_t total = size + end;
   1603     void* buf = editData(total);
   1604     if (buf == NULL) {
   1605         return UNKNOWN_ERROR;
   1606     }
   1607     memcpy(((char*)buf)+end, data, size);
   1608     return NO_ERROR;
   1609 }
   1610 
   1611 void AaptFile::clearData()
   1612 {
   1613     if (mData != NULL) free(mData);
   1614     mData = NULL;
   1615     mDataSize = 0;
   1616     mBufferSize = 0;
   1617 }
   1618 
   1619 String8 AaptFile::getPrintableSource() const
   1620 {
   1621     if (hasData()) {
   1622         String8 name(mGroupEntry.toDirName(String8()));
   1623         name.appendPath(mPath);
   1624         name.append(" #generated");
   1625         return name;
   1626     }
   1627     return mSourceFile;
   1628 }
   1629 
   1630 // =========================================================================
   1631 // =========================================================================
   1632 // =========================================================================
   1633 
   1634 status_t AaptGroup::addFile(const sp<AaptFile>& file)
   1635 {
   1636     if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
   1637         file->mPath = mPath;
   1638         mFiles.add(file->getGroupEntry(), file);
   1639         return NO_ERROR;
   1640     }
   1641 
   1642 #if 0
   1643     printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
   1644             file->getSourceFile().string(),
   1645             file->getGroupEntry().toDirName(String8()).string(),
   1646             mLeaf.string(), mPath.string());
   1647 #endif
   1648 
   1649     SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
   1650                                                getPrintableSource().string());
   1651     return UNKNOWN_ERROR;
   1652 }
   1653 
   1654 void AaptGroup::removeFile(size_t index)
   1655 {
   1656 	mFiles.removeItemsAt(index);
   1657 }
   1658 
   1659 void AaptGroup::print(const String8& prefix) const
   1660 {
   1661     printf("%s%s\n", prefix.string(), getPath().string());
   1662     const size_t N=mFiles.size();
   1663     size_t i;
   1664     for (i=0; i<N; i++) {
   1665         sp<AaptFile> file = mFiles.valueAt(i);
   1666         const AaptGroupEntry& e = file->getGroupEntry();
   1667         if (file->hasData()) {
   1668             printf("%s  Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
   1669                     (int)file->getSize());
   1670         } else {
   1671             printf("%s  Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
   1672                     file->getPrintableSource().string());
   1673         }
   1674         //printf("%s  File Group Entry: %s\n", prefix.string(),
   1675         //        file->getGroupEntry().toDirName(String8()).string());
   1676     }
   1677 }
   1678 
   1679 String8 AaptGroup::getPrintableSource() const
   1680 {
   1681     if (mFiles.size() > 0) {
   1682         // Arbitrarily pull the first source file out of the list.
   1683         return mFiles.valueAt(0)->getPrintableSource();
   1684     }
   1685 
   1686     // Should never hit this case, but to be safe...
   1687     return getPath();
   1688 
   1689 }
   1690 
   1691 // =========================================================================
   1692 // =========================================================================
   1693 // =========================================================================
   1694 
   1695 status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
   1696 {
   1697     if (mFiles.indexOfKey(name) >= 0) {
   1698         return ALREADY_EXISTS;
   1699     }
   1700     mFiles.add(name, file);
   1701     return NO_ERROR;
   1702 }
   1703 
   1704 status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
   1705 {
   1706     if (mDirs.indexOfKey(name) >= 0) {
   1707         return ALREADY_EXISTS;
   1708     }
   1709     mDirs.add(name, dir);
   1710     return NO_ERROR;
   1711 }
   1712 
   1713 sp<AaptDir> AaptDir::makeDir(const String8& path)
   1714 {
   1715     String8 name;
   1716     String8 remain = path;
   1717 
   1718     sp<AaptDir> subdir = this;
   1719     while (name = remain.walkPath(&remain), remain != "") {
   1720         subdir = subdir->makeDir(name);
   1721     }
   1722 
   1723     ssize_t i = subdir->mDirs.indexOfKey(name);
   1724     if (i >= 0) {
   1725         return subdir->mDirs.valueAt(i);
   1726     }
   1727     sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
   1728     subdir->mDirs.add(name, dir);
   1729     return dir;
   1730 }
   1731 
   1732 void AaptDir::removeFile(const String8& name)
   1733 {
   1734     mFiles.removeItem(name);
   1735 }
   1736 
   1737 void AaptDir::removeDir(const String8& name)
   1738 {
   1739     mDirs.removeItem(name);
   1740 }
   1741 
   1742 status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
   1743 {
   1744     sp<AaptGroup> group;
   1745     if (mFiles.indexOfKey(leafName) >= 0) {
   1746         group = mFiles.valueFor(leafName);
   1747     } else {
   1748         group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
   1749         mFiles.add(leafName, group);
   1750     }
   1751 
   1752     return group->addFile(file);
   1753 }
   1754 
   1755 ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
   1756                             const AaptGroupEntry& kind, const String8& resType,
   1757                             sp<FilePathStore>& fullResPaths)
   1758 {
   1759     Vector<String8> fileNames;
   1760     {
   1761         DIR* dir = NULL;
   1762 
   1763         dir = opendir(srcDir.string());
   1764         if (dir == NULL) {
   1765             fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
   1766             return UNKNOWN_ERROR;
   1767         }
   1768 
   1769         /*
   1770          * Slurp the filenames out of the directory.
   1771          */
   1772         while (1) {
   1773             struct dirent* entry;
   1774 
   1775             entry = readdir(dir);
   1776             if (entry == NULL)
   1777                 break;
   1778 
   1779             if (isHidden(srcDir.string(), entry->d_name))
   1780                 continue;
   1781 
   1782             String8 name(entry->d_name);
   1783             fileNames.add(name);
   1784             // Add fully qualified path for dependency purposes
   1785             // if we're collecting them
   1786             if (fullResPaths != NULL) {
   1787                 fullResPaths->add(srcDir.appendPathCopy(name));
   1788             }
   1789         }
   1790         closedir(dir);
   1791     }
   1792 
   1793     ssize_t count = 0;
   1794 
   1795     /*
   1796      * Stash away the files and recursively descend into subdirectories.
   1797      */
   1798     const size_t N = fileNames.size();
   1799     size_t i;
   1800     for (i = 0; i < N; i++) {
   1801         String8 pathName(srcDir);
   1802         FileType type;
   1803 
   1804         pathName.appendPath(fileNames[i].string());
   1805         type = getFileType(pathName.string());
   1806         if (type == kFileTypeDirectory) {
   1807             sp<AaptDir> subdir;
   1808             bool notAdded = false;
   1809             if (mDirs.indexOfKey(fileNames[i]) >= 0) {
   1810                 subdir = mDirs.valueFor(fileNames[i]);
   1811             } else {
   1812                 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
   1813                 notAdded = true;
   1814             }
   1815             ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
   1816                                                 resType, fullResPaths);
   1817             if (res < NO_ERROR) {
   1818                 return res;
   1819             }
   1820             if (res > 0 && notAdded) {
   1821                 mDirs.add(fileNames[i], subdir);
   1822             }
   1823             count += res;
   1824         } else if (type == kFileTypeRegular) {
   1825             sp<AaptFile> file = new AaptFile(pathName, kind, resType);
   1826             status_t err = addLeafFile(fileNames[i], file);
   1827             if (err != NO_ERROR) {
   1828                 return err;
   1829             }
   1830 
   1831             count++;
   1832 
   1833         } else {
   1834             if (bundle->getVerbose())
   1835                 printf("   (ignoring non-file/dir '%s')\n", pathName.string());
   1836         }
   1837     }
   1838 
   1839     return count;
   1840 }
   1841 
   1842 status_t AaptDir::validate() const
   1843 {
   1844     const size_t NF = mFiles.size();
   1845     const size_t ND = mDirs.size();
   1846     size_t i;
   1847     for (i = 0; i < NF; i++) {
   1848         if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
   1849             SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
   1850                     "Invalid filename.  Unable to add.");
   1851             return UNKNOWN_ERROR;
   1852         }
   1853 
   1854         size_t j;
   1855         for (j = i+1; j < NF; j++) {
   1856             if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
   1857                            mFiles.valueAt(j)->getLeaf().string()) == 0) {
   1858                 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
   1859                         "File is case-insensitive equivalent to: %s",
   1860                         mFiles.valueAt(j)->getPrintableSource().string());
   1861                 return UNKNOWN_ERROR;
   1862             }
   1863 
   1864             // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
   1865             // (this is mostly caught by the "marked" stuff, below)
   1866         }
   1867 
   1868         for (j = 0; j < ND; j++) {
   1869             if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
   1870                            mDirs.valueAt(j)->getLeaf().string()) == 0) {
   1871                 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
   1872                         "File conflicts with dir from: %s",
   1873                         mDirs.valueAt(j)->getPrintableSource().string());
   1874                 return UNKNOWN_ERROR;
   1875             }
   1876         }
   1877     }
   1878 
   1879     for (i = 0; i < ND; i++) {
   1880         if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
   1881             SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
   1882                     "Invalid directory name, unable to add.");
   1883             return UNKNOWN_ERROR;
   1884         }
   1885 
   1886         size_t j;
   1887         for (j = i+1; j < ND; j++) {
   1888             if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
   1889                            mDirs.valueAt(j)->getLeaf().string()) == 0) {
   1890                 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
   1891                         "Directory is case-insensitive equivalent to: %s",
   1892                         mDirs.valueAt(j)->getPrintableSource().string());
   1893                 return UNKNOWN_ERROR;
   1894             }
   1895         }
   1896 
   1897         status_t err = mDirs.valueAt(i)->validate();
   1898         if (err != NO_ERROR) {
   1899             return err;
   1900         }
   1901     }
   1902 
   1903     return NO_ERROR;
   1904 }
   1905 
   1906 void AaptDir::print(const String8& prefix) const
   1907 {
   1908     const size_t ND=getDirs().size();
   1909     size_t i;
   1910     for (i=0; i<ND; i++) {
   1911         getDirs().valueAt(i)->print(prefix);
   1912     }
   1913 
   1914     const size_t NF=getFiles().size();
   1915     for (i=0; i<NF; i++) {
   1916         getFiles().valueAt(i)->print(prefix);
   1917     }
   1918 }
   1919 
   1920 String8 AaptDir::getPrintableSource() const
   1921 {
   1922     if (mFiles.size() > 0) {
   1923         // Arbitrarily pull the first file out of the list as the source dir.
   1924         return mFiles.valueAt(0)->getPrintableSource().getPathDir();
   1925     }
   1926     if (mDirs.size() > 0) {
   1927         // Or arbitrarily pull the first dir out of the list as the source dir.
   1928         return mDirs.valueAt(0)->getPrintableSource().getPathDir();
   1929     }
   1930 
   1931     // Should never hit this case, but to be safe...
   1932     return mPath;
   1933 
   1934 }
   1935 
   1936 // =========================================================================
   1937 // =========================================================================
   1938 // =========================================================================
   1939 
   1940 status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
   1941 {
   1942     status_t err = NO_ERROR;
   1943     size_t N = javaSymbols->mSymbols.size();
   1944     for (size_t i=0; i<N; i++) {
   1945         const String8& name = javaSymbols->mSymbols.keyAt(i);
   1946         const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
   1947         ssize_t pos = mSymbols.indexOfKey(name);
   1948         if (pos < 0) {
   1949             entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
   1950             err = UNKNOWN_ERROR;
   1951             continue;
   1952         }
   1953         //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
   1954         //        i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
   1955         mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
   1956     }
   1957 
   1958     N = javaSymbols->mNestedSymbols.size();
   1959     for (size_t i=0; i<N; i++) {
   1960         const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
   1961         const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
   1962         ssize_t pos = mNestedSymbols.indexOfKey(name);
   1963         if (pos < 0) {
   1964             SourcePos pos;
   1965             pos.error("Java symbol dir %s not defined\n", name.string());
   1966             err = UNKNOWN_ERROR;
   1967             continue;
   1968         }
   1969         //printf("**** applying java symbols in dir %s\n", name.string());
   1970         status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
   1971         if (myerr != NO_ERROR) {
   1972             err = myerr;
   1973         }
   1974     }
   1975 
   1976     return err;
   1977 }
   1978 
   1979 // =========================================================================
   1980 // =========================================================================
   1981 // =========================================================================
   1982 
   1983 AaptAssets::AaptAssets()
   1984     : AaptDir(String8(), String8()),
   1985       mChanged(false), mHaveIncludedAssets(false), mRes(NULL)
   1986 {
   1987 }
   1988 
   1989 const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
   1990     if (mChanged) {
   1991     }
   1992     return mGroupEntries;
   1993 }
   1994 
   1995 status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
   1996 {
   1997     mChanged = true;
   1998     return AaptDir::addFile(name, file);
   1999 }
   2000 
   2001 sp<AaptFile> AaptAssets::addFile(
   2002         const String8& filePath, const AaptGroupEntry& entry,
   2003         const String8& srcDir, sp<AaptGroup>* outGroup,
   2004         const String8& resType)
   2005 {
   2006     sp<AaptDir> dir = this;
   2007     sp<AaptGroup> group;
   2008     sp<AaptFile> file;
   2009     String8 root, remain(filePath), partialPath;
   2010     while (remain.length() > 0) {
   2011         root = remain.walkPath(&remain);
   2012         partialPath.appendPath(root);
   2013 
   2014         const String8 rootStr(root);
   2015 
   2016         if (remain.length() == 0) {
   2017             ssize_t i = dir->getFiles().indexOfKey(rootStr);
   2018             if (i >= 0) {
   2019                 group = dir->getFiles().valueAt(i);
   2020             } else {
   2021                 group = new AaptGroup(rootStr, filePath);
   2022                 status_t res = dir->addFile(rootStr, group);
   2023                 if (res != NO_ERROR) {
   2024                     return NULL;
   2025                 }
   2026             }
   2027             file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
   2028             status_t res = group->addFile(file);
   2029             if (res != NO_ERROR) {
   2030                 return NULL;
   2031             }
   2032             break;
   2033 
   2034         } else {
   2035             ssize_t i = dir->getDirs().indexOfKey(rootStr);
   2036             if (i >= 0) {
   2037                 dir = dir->getDirs().valueAt(i);
   2038             } else {
   2039                 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
   2040                 status_t res = dir->addDir(rootStr, subdir);
   2041                 if (res != NO_ERROR) {
   2042                     return NULL;
   2043                 }
   2044                 dir = subdir;
   2045             }
   2046         }
   2047     }
   2048 
   2049     mGroupEntries.add(entry);
   2050     if (outGroup) *outGroup = group;
   2051     return file;
   2052 }
   2053 
   2054 void AaptAssets::addResource(const String8& leafName, const String8& path,
   2055                 const sp<AaptFile>& file, const String8& resType)
   2056 {
   2057     sp<AaptDir> res = AaptDir::makeDir(kResString);
   2058     String8 dirname = file->getGroupEntry().toDirName(resType);
   2059     sp<AaptDir> subdir = res->makeDir(dirname);
   2060     sp<AaptGroup> grr = new AaptGroup(leafName, path);
   2061     grr->addFile(file);
   2062 
   2063     subdir->addFile(leafName, grr);
   2064 }
   2065 
   2066 
   2067 ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
   2068 {
   2069     int count;
   2070     int totalCount = 0;
   2071     FileType type;
   2072     const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
   2073     const size_t dirCount =resDirs.size();
   2074     sp<AaptAssets> current = this;
   2075 
   2076     const int N = bundle->getFileSpecCount();
   2077 
   2078     /*
   2079      * If a package manifest was specified, include that first.
   2080      */
   2081     if (bundle->getAndroidManifestFile() != NULL) {
   2082         // place at root of zip.
   2083         String8 srcFile(bundle->getAndroidManifestFile());
   2084         addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
   2085                 NULL, String8());
   2086         totalCount++;
   2087     }
   2088 
   2089     /*
   2090      * If a directory of custom assets was supplied, slurp 'em up.
   2091      */
   2092     if (bundle->getAssetSourceDir()) {
   2093         const char* assetDir = bundle->getAssetSourceDir();
   2094 
   2095         FileType type = getFileType(assetDir);
   2096         if (type == kFileTypeNonexistent) {
   2097             fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
   2098             return UNKNOWN_ERROR;
   2099         }
   2100         if (type != kFileTypeDirectory) {
   2101             fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
   2102             return UNKNOWN_ERROR;
   2103         }
   2104 
   2105         String8 assetRoot(assetDir);
   2106         sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
   2107         AaptGroupEntry group;
   2108         count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
   2109                                             String8(), mFullAssetPaths);
   2110         if (count < 0) {
   2111             totalCount = count;
   2112             goto bail;
   2113         }
   2114         if (count > 0) {
   2115             mGroupEntries.add(group);
   2116         }
   2117         totalCount += count;
   2118 
   2119         if (bundle->getVerbose())
   2120             printf("Found %d custom asset file%s in %s\n",
   2121                    count, (count==1) ? "" : "s", assetDir);
   2122     }
   2123 
   2124     /*
   2125      * If a directory of resource-specific assets was supplied, slurp 'em up.
   2126      */
   2127     for (size_t i=0; i<dirCount; i++) {
   2128         const char *res = resDirs[i];
   2129         if (res) {
   2130             type = getFileType(res);
   2131             if (type == kFileTypeNonexistent) {
   2132                 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
   2133                 return UNKNOWN_ERROR;
   2134             }
   2135             if (type == kFileTypeDirectory) {
   2136                 if (i>0) {
   2137                     sp<AaptAssets> nextOverlay = new AaptAssets();
   2138                     current->setOverlay(nextOverlay);
   2139                     current = nextOverlay;
   2140                     current->setFullResPaths(mFullResPaths);
   2141                 }
   2142                 count = current->slurpResourceTree(bundle, String8(res));
   2143 
   2144                 if (count < 0) {
   2145                     totalCount = count;
   2146                     goto bail;
   2147                 }
   2148                 totalCount += count;
   2149             }
   2150             else {
   2151                 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
   2152                 return UNKNOWN_ERROR;
   2153             }
   2154         }
   2155 
   2156     }
   2157     /*
   2158      * Now do any additional raw files.
   2159      */
   2160     for (int arg=0; arg<N; arg++) {
   2161         const char* assetDir = bundle->getFileSpecEntry(arg);
   2162 
   2163         FileType type = getFileType(assetDir);
   2164         if (type == kFileTypeNonexistent) {
   2165             fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
   2166             return UNKNOWN_ERROR;
   2167         }
   2168         if (type != kFileTypeDirectory) {
   2169             fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
   2170             return UNKNOWN_ERROR;
   2171         }
   2172 
   2173         String8 assetRoot(assetDir);
   2174 
   2175         if (bundle->getVerbose())
   2176             printf("Processing raw dir '%s'\n", (const char*) assetDir);
   2177 
   2178         /*
   2179          * Do a recursive traversal of subdir tree.  We don't make any
   2180          * guarantees about ordering, so we're okay with an inorder search
   2181          * using whatever order the OS happens to hand back to us.
   2182          */
   2183         count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
   2184         if (count < 0) {
   2185             /* failure; report error and remove archive */
   2186             totalCount = count;
   2187             goto bail;
   2188         }
   2189         totalCount += count;
   2190 
   2191         if (bundle->getVerbose())
   2192             printf("Found %d asset file%s in %s\n",
   2193                    count, (count==1) ? "" : "s", assetDir);
   2194     }
   2195 
   2196     count = validate();
   2197     if (count != NO_ERROR) {
   2198         totalCount = count;
   2199         goto bail;
   2200     }
   2201 
   2202     count = filter(bundle);
   2203     if (count != NO_ERROR) {
   2204         totalCount = count;
   2205         goto bail;
   2206     }
   2207 
   2208 bail:
   2209     return totalCount;
   2210 }
   2211 
   2212 ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
   2213                                     const AaptGroupEntry& kind,
   2214                                     const String8& resType,
   2215                                     sp<FilePathStore>& fullResPaths)
   2216 {
   2217     ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
   2218     if (res > 0) {
   2219         mGroupEntries.add(kind);
   2220     }
   2221 
   2222     return res;
   2223 }
   2224 
   2225 ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
   2226 {
   2227     ssize_t err = 0;
   2228 
   2229     DIR* dir = opendir(srcDir.string());
   2230     if (dir == NULL) {
   2231         fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
   2232         return UNKNOWN_ERROR;
   2233     }
   2234 
   2235     status_t count = 0;
   2236 
   2237     /*
   2238      * Run through the directory, looking for dirs that match the
   2239      * expected pattern.
   2240      */
   2241     while (1) {
   2242         struct dirent* entry = readdir(dir);
   2243         if (entry == NULL) {
   2244             break;
   2245         }
   2246 
   2247         if (isHidden(srcDir.string(), entry->d_name)) {
   2248             continue;
   2249         }
   2250 
   2251         String8 subdirName(srcDir);
   2252         subdirName.appendPath(entry->d_name);
   2253 
   2254         AaptGroupEntry group;
   2255         String8 resType;
   2256         bool b = group.initFromDirName(entry->d_name, &resType);
   2257         if (!b) {
   2258             fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
   2259                     entry->d_name);
   2260             err = -1;
   2261             continue;
   2262         }
   2263 
   2264         if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
   2265             int maxResInt = atoi(bundle->getMaxResVersion());
   2266             const char *verString = group.getVersionString().string();
   2267             int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
   2268             if (dirVersionInt > maxResInt) {
   2269               fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
   2270               continue;
   2271             }
   2272         }
   2273 
   2274         FileType type = getFileType(subdirName.string());
   2275 
   2276         if (type == kFileTypeDirectory) {
   2277             sp<AaptDir> dir = makeDir(resType);
   2278             ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
   2279                                                 resType, mFullResPaths);
   2280             if (res < 0) {
   2281                 count = res;
   2282                 goto bail;
   2283             }
   2284             if (res > 0) {
   2285                 mGroupEntries.add(group);
   2286                 count += res;
   2287             }
   2288 
   2289             // Only add this directory if we don't already have a resource dir
   2290             // for the current type.  This ensures that we only add the dir once
   2291             // for all configs.
   2292             sp<AaptDir> rdir = resDir(resType);
   2293             if (rdir == NULL) {
   2294                 mResDirs.add(dir);
   2295             }
   2296         } else {
   2297             if (bundle->getVerbose()) {
   2298                 fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
   2299             }
   2300         }
   2301     }
   2302 
   2303 bail:
   2304     closedir(dir);
   2305     dir = NULL;
   2306 
   2307     if (err != 0) {
   2308         return err;
   2309     }
   2310     return count;
   2311 }
   2312 
   2313 ssize_t
   2314 AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
   2315 {
   2316     int count = 0;
   2317     SortedVector<AaptGroupEntry> entries;
   2318 
   2319     ZipFile* zip = new ZipFile;
   2320     status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
   2321     if (err != NO_ERROR) {
   2322         fprintf(stderr, "error opening zip file %s\n", filename);
   2323         count = err;
   2324         delete zip;
   2325         return -1;
   2326     }
   2327 
   2328     const int N = zip->getNumEntries();
   2329     for (int i=0; i<N; i++) {
   2330         ZipEntry* entry = zip->getEntryByIndex(i);
   2331         if (entry->getDeleted()) {
   2332             continue;
   2333         }
   2334 
   2335         String8 entryName(entry->getFileName());
   2336 
   2337         String8 dirName = entryName.getPathDir();
   2338         sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
   2339 
   2340         String8 resType;
   2341         AaptGroupEntry kind;
   2342 
   2343         String8 remain;
   2344         if (entryName.walkPath(&remain) == kResourceDir) {
   2345             // these are the resources, pull their type out of the directory name
   2346             kind.initFromDirName(remain.walkPath().string(), &resType);
   2347         } else {
   2348             // these are untyped and don't have an AaptGroupEntry
   2349         }
   2350         if (entries.indexOf(kind) < 0) {
   2351             entries.add(kind);
   2352             mGroupEntries.add(kind);
   2353         }
   2354 
   2355         // use the one from the zip file if they both exist.
   2356         dir->removeFile(entryName.getPathLeaf());
   2357 
   2358         sp<AaptFile> file = new AaptFile(entryName, kind, resType);
   2359         status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
   2360         if (err != NO_ERROR) {
   2361             fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
   2362             count = err;
   2363             goto bail;
   2364         }
   2365         file->setCompressionMethod(entry->getCompressionMethod());
   2366 
   2367 #if 0
   2368         if (entryName == "AndroidManifest.xml") {
   2369             printf("AndroidManifest.xml\n");
   2370         }
   2371         printf("\n\nfile: %s\n", entryName.string());
   2372 #endif
   2373 
   2374         size_t len = entry->getUncompressedLen();
   2375         void* data = zip->uncompress(entry);
   2376         void* buf = file->editData(len);
   2377         memcpy(buf, data, len);
   2378 
   2379 #if 0
   2380         const int OFF = 0;
   2381         const unsigned char* p = (unsigned char*)data;
   2382         const unsigned char* end = p+len;
   2383         p += OFF;
   2384         for (int i=0; i<32 && p < end; i++) {
   2385             printf("0x%03x ", i*0x10 + OFF);
   2386             for (int j=0; j<0x10 && p < end; j++) {
   2387                 printf(" %02x", *p);
   2388                 p++;
   2389             }
   2390             printf("\n");
   2391         }
   2392 #endif
   2393 
   2394         free(data);
   2395 
   2396         count++;
   2397     }
   2398 
   2399 bail:
   2400     delete zip;
   2401     return count;
   2402 }
   2403 
   2404 status_t AaptAssets::filter(Bundle* bundle)
   2405 {
   2406     ResourceFilter reqFilter;
   2407     status_t err = reqFilter.parse(bundle->getConfigurations());
   2408     if (err != NO_ERROR) {
   2409         return err;
   2410     }
   2411 
   2412     ResourceFilter prefFilter;
   2413     err = prefFilter.parse(bundle->getPreferredConfigurations());
   2414     if (err != NO_ERROR) {
   2415         return err;
   2416     }
   2417 
   2418     if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
   2419         return NO_ERROR;
   2420     }
   2421 
   2422     if (bundle->getVerbose()) {
   2423         if (!reqFilter.isEmpty()) {
   2424             printf("Applying required filter: %s\n",
   2425                     bundle->getConfigurations());
   2426         }
   2427         if (!prefFilter.isEmpty()) {
   2428             printf("Applying preferred filter: %s\n",
   2429                     bundle->getPreferredConfigurations());
   2430         }
   2431     }
   2432 
   2433     const Vector<sp<AaptDir> >& resdirs = mResDirs;
   2434     const size_t ND = resdirs.size();
   2435     for (size_t i=0; i<ND; i++) {
   2436         const sp<AaptDir>& dir = resdirs.itemAt(i);
   2437         if (dir->getLeaf() == kValuesDir) {
   2438             // The "value" dir is special since a single file defines
   2439             // multiple resources, so we can not do filtering on the
   2440             // files themselves.
   2441             continue;
   2442         }
   2443         if (dir->getLeaf() == kMipmapDir) {
   2444             // We also skip the "mipmap" directory, since the point of this
   2445             // is to include all densities without stripping.  If you put
   2446             // other configurations in here as well they won't be stripped
   2447             // either...  So don't do that.  Seriously.  What is wrong with you?
   2448             continue;
   2449         }
   2450 
   2451         const size_t NG = dir->getFiles().size();
   2452         for (size_t j=0; j<NG; j++) {
   2453             sp<AaptGroup> grp = dir->getFiles().valueAt(j);
   2454 
   2455             // First remove any configurations we know we don't need.
   2456             for (size_t k=0; k<grp->getFiles().size(); k++) {
   2457                 sp<AaptFile> file = grp->getFiles().valueAt(k);
   2458                 if (k == 0 && grp->getFiles().size() == 1) {
   2459                     // If this is the only file left, we need to keep it.
   2460                     // Otherwise the resource IDs we are using will be inconsistent
   2461                     // with what we get when not stripping.  Sucky, but at least
   2462                     // for now we can rely on the back-end doing another filtering
   2463                     // pass to take this out and leave us with this resource name
   2464                     // containing no entries.
   2465                     continue;
   2466                 }
   2467                 if (file->getPath().getPathExtension() == ".xml") {
   2468                     // We can't remove .xml files at this point, because when
   2469                     // we parse them they may add identifier resources, so
   2470                     // removing them can cause our resource identifiers to
   2471                     // become inconsistent.
   2472                     continue;
   2473                 }
   2474                 const ResTable_config& config(file->getGroupEntry().toParams());
   2475                 if (!reqFilter.match(config)) {
   2476                     if (bundle->getVerbose()) {
   2477                         printf("Pruning unneeded resource: %s\n",
   2478                                 file->getPrintableSource().string());
   2479                     }
   2480                     grp->removeFile(k);
   2481                     k--;
   2482                 }
   2483             }
   2484 
   2485             // Quick check: no preferred filters, nothing more to do.
   2486             if (prefFilter.isEmpty()) {
   2487                 continue;
   2488             }
   2489 
   2490             // Get the preferred density if there is one. We do not match exactly for density.
   2491             // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
   2492             // pick xhdpi.
   2493             uint32_t preferredDensity = 0;
   2494             const SortedVector<uint32_t>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
   2495             if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
   2496                 preferredDensity = (*preferredConfigs)[0];
   2497             }
   2498 
   2499             // Now deal with preferred configurations.
   2500             for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
   2501                 for (size_t k=0; k<grp->getFiles().size(); k++) {
   2502                     sp<AaptFile> file = grp->getFiles().valueAt(k);
   2503                     if (k == 0 && grp->getFiles().size() == 1) {
   2504                         // If this is the only file left, we need to keep it.
   2505                         // Otherwise the resource IDs we are using will be inconsistent
   2506                         // with what we get when not stripping.  Sucky, but at least
   2507                         // for now we can rely on the back-end doing another filtering
   2508                         // pass to take this out and leave us with this resource name
   2509                         // containing no entries.
   2510                         continue;
   2511                     }
   2512                     if (file->getPath().getPathExtension() == ".xml") {
   2513                         // We can't remove .xml files at this point, because when
   2514                         // we parse them they may add identifier resources, so
   2515                         // removing them can cause our resource identifiers to
   2516                         // become inconsistent.
   2517                         continue;
   2518                     }
   2519                     const ResTable_config& config(file->getGroupEntry().toParams());
   2520                     if (!prefFilter.match(axis, config)) {
   2521                         // This is a resource we would prefer not to have.  Check
   2522                         // to see if have a similar variation that we would like
   2523                         // to have and, if so, we can drop it.
   2524 
   2525                         uint32_t bestDensity = config.density;
   2526 
   2527                         for (size_t m=0; m<grp->getFiles().size(); m++) {
   2528                             if (m == k) continue;
   2529                             sp<AaptFile> mfile = grp->getFiles().valueAt(m);
   2530                             const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
   2531                             if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
   2532                                 if (axis == AXIS_DENSITY && preferredDensity > 0) {
   2533                                     // See if there is a better density resource
   2534                                     if (mconfig.density < bestDensity &&
   2535                                             mconfig.density > preferredDensity &&
   2536                                             bestDensity > preferredDensity) {
   2537                                         // This density is between our best density and
   2538                                         // the preferred density, therefore it is better.
   2539                                         bestDensity = mconfig.density;
   2540                                     } else if (mconfig.density > bestDensity &&
   2541                                             bestDensity < preferredDensity) {
   2542                                         // This density is better than our best density and
   2543                                         // our best density was smaller than our preferred
   2544                                         // density, so it is better.
   2545                                         bestDensity = mconfig.density;
   2546                                     }
   2547                                 } else if (prefFilter.match(axis, mconfig)) {
   2548                                     if (bundle->getVerbose()) {
   2549                                         printf("Pruning unneeded resource: %s\n",
   2550                                                 file->getPrintableSource().string());
   2551                                     }
   2552                                     grp->removeFile(k);
   2553                                     k--;
   2554                                     break;
   2555                                 }
   2556                             }
   2557                         }
   2558 
   2559                         if (axis == AXIS_DENSITY && preferredDensity > 0 &&
   2560                                 bestDensity != config.density) {
   2561                             if (bundle->getVerbose()) {
   2562                                 printf("Pruning unneeded resource: %s\n",
   2563                                         file->getPrintableSource().string());
   2564                             }
   2565                             grp->removeFile(k);
   2566                             k--;
   2567                         }
   2568                     }
   2569                 }
   2570             }
   2571         }
   2572     }
   2573 
   2574     return NO_ERROR;
   2575 }
   2576 
   2577 sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
   2578 {
   2579     sp<AaptSymbols> sym = mSymbols.valueFor(name);
   2580     if (sym == NULL) {
   2581         sym = new AaptSymbols();
   2582         mSymbols.add(name, sym);
   2583     }
   2584     return sym;
   2585 }
   2586 
   2587 sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
   2588 {
   2589     sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
   2590     if (sym == NULL) {
   2591         sym = new AaptSymbols();
   2592         mJavaSymbols.add(name, sym);
   2593     }
   2594     return sym;
   2595 }
   2596 
   2597 status_t AaptAssets::applyJavaSymbols()
   2598 {
   2599     size_t N = mJavaSymbols.size();
   2600     for (size_t i=0; i<N; i++) {
   2601         const String8& name = mJavaSymbols.keyAt(i);
   2602         const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
   2603         ssize_t pos = mSymbols.indexOfKey(name);
   2604         if (pos < 0) {
   2605             SourcePos pos;
   2606             pos.error("Java symbol dir %s not defined\n", name.string());
   2607             return UNKNOWN_ERROR;
   2608         }
   2609         //printf("**** applying java symbols in dir %s\n", name.string());
   2610         status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
   2611         if (err != NO_ERROR) {
   2612             return err;
   2613         }
   2614     }
   2615 
   2616     return NO_ERROR;
   2617 }
   2618 
   2619 bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
   2620     //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
   2621     //        sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
   2622     //        sym.isJavaSymbol ? 1 : 0);
   2623     if (!mHavePrivateSymbols) return true;
   2624     if (sym.isPublic) return true;
   2625     if (includePrivate && sym.isJavaSymbol) return true;
   2626     return false;
   2627 }
   2628 
   2629 status_t AaptAssets::buildIncludedResources(Bundle* bundle)
   2630 {
   2631     if (!mHaveIncludedAssets) {
   2632         // Add in all includes.
   2633         const Vector<const char*>& incl = bundle->getPackageIncludes();
   2634         const size_t N=incl.size();
   2635         for (size_t i=0; i<N; i++) {
   2636             if (bundle->getVerbose())
   2637                 printf("Including resources from package: %s\n", incl[i]);
   2638             if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
   2639                 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
   2640                         incl[i]);
   2641                 return UNKNOWN_ERROR;
   2642             }
   2643         }
   2644         mHaveIncludedAssets = true;
   2645     }
   2646 
   2647     return NO_ERROR;
   2648 }
   2649 
   2650 status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
   2651 {
   2652     const ResTable& res = getIncludedResources();
   2653     // XXX dirty!
   2654     return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
   2655 }
   2656 
   2657 const ResTable& AaptAssets::getIncludedResources() const
   2658 {
   2659     return mIncludedAssets.getResources(false);
   2660 }
   2661 
   2662 void AaptAssets::print(const String8& prefix) const
   2663 {
   2664     String8 innerPrefix(prefix);
   2665     innerPrefix.append("  ");
   2666     String8 innerInnerPrefix(innerPrefix);
   2667     innerInnerPrefix.append("  ");
   2668     printf("%sConfigurations:\n", prefix.string());
   2669     const size_t N=mGroupEntries.size();
   2670     for (size_t i=0; i<N; i++) {
   2671         String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
   2672         printf("%s %s\n", prefix.string(),
   2673                 cname != "" ? cname.string() : "(default)");
   2674     }
   2675 
   2676     printf("\n%sFiles:\n", prefix.string());
   2677     AaptDir::print(innerPrefix);
   2678 
   2679     printf("\n%sResource Dirs:\n", prefix.string());
   2680     const Vector<sp<AaptDir> >& resdirs = mResDirs;
   2681     const size_t NR = resdirs.size();
   2682     for (size_t i=0; i<NR; i++) {
   2683         const sp<AaptDir>& d = resdirs.itemAt(i);
   2684         printf("%s  Type %s\n", prefix.string(), d->getLeaf().string());
   2685         d->print(innerInnerPrefix);
   2686     }
   2687 }
   2688 
   2689 sp<AaptDir> AaptAssets::resDir(const String8& name) const
   2690 {
   2691     const Vector<sp<AaptDir> >& resdirs = mResDirs;
   2692     const size_t N = resdirs.size();
   2693     for (size_t i=0; i<N; i++) {
   2694         const sp<AaptDir>& d = resdirs.itemAt(i);
   2695         if (d->getLeaf() == name) {
   2696             return d;
   2697         }
   2698     }
   2699     return NULL;
   2700 }
   2701 
   2702 bool
   2703 valid_symbol_name(const String8& symbol)
   2704 {
   2705     static char const * const KEYWORDS[] = {
   2706         "abstract", "assert", "boolean", "break",
   2707         "byte", "case", "catch", "char", "class", "const", "continue",
   2708         "default", "do", "double", "else", "enum", "extends", "final",
   2709         "finally", "float", "for", "goto", "if", "implements", "import",
   2710         "instanceof", "int", "interface", "long", "native", "new", "package",
   2711         "private", "protected", "public", "return", "short", "static",
   2712         "strictfp", "super", "switch", "synchronized", "this", "throw",
   2713         "throws", "transient", "try", "void", "volatile", "while",
   2714         "true", "false", "null",
   2715         NULL
   2716     };
   2717     const char*const* k = KEYWORDS;
   2718     const char*const s = symbol.string();
   2719     while (*k) {
   2720         if (0 == strcmp(s, *k)) {
   2721             return false;
   2722         }
   2723         k++;
   2724     }
   2725     return true;
   2726 }
   2727