Home | History | Annotate | Download | only in aapt
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <androidfw/ResourceTypes.h>
     18 #include <ctype.h>
     19 
     20 #include "AaptConfig.h"
     21 #include "AaptAssets.h"
     22 #include "AaptUtil.h"
     23 #include "ResourceFilter.h"
     24 #include "SdkConstants.h"
     25 
     26 using android::String8;
     27 using android::Vector;
     28 using android::ResTable_config;
     29 
     30 namespace AaptConfig {
     31 
     32 static const char* kWildcardName = "any";
     33 
     34 bool parse(const String8& str, ConfigDescription* out) {
     35     Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '-');
     36 
     37     ConfigDescription config;
     38     AaptLocaleValue locale;
     39     ssize_t index = 0;
     40     ssize_t localeIndex = 0;
     41     const ssize_t N = parts.size();
     42     const char* part = parts[index].string();
     43 
     44     if (str.length() == 0) {
     45         goto success;
     46     }
     47 
     48     if (parseMcc(part, &config)) {
     49         index++;
     50         if (index == N) {
     51             goto success;
     52         }
     53         part = parts[index].string();
     54     }
     55 
     56     if (parseMnc(part, &config)) {
     57         index++;
     58         if (index == N) {
     59             goto success;
     60         }
     61         part = parts[index].string();
     62     }
     63 
     64     // Locale spans a few '-' separators, so we let it
     65     // control the index.
     66     localeIndex = locale.initFromDirName(parts, index);
     67     if (localeIndex < 0) {
     68         return false;
     69     } else if (localeIndex > index) {
     70         locale.writeTo(&config);
     71         index = localeIndex;
     72         if (index >= N) {
     73             goto success;
     74         }
     75         part = parts[index].string();
     76     }
     77 
     78     if (parseLayoutDirection(part, &config)) {
     79         index++;
     80         if (index == N) {
     81             goto success;
     82         }
     83         part = parts[index].string();
     84     }
     85 
     86     if (parseSmallestScreenWidthDp(part, &config)) {
     87         index++;
     88         if (index == N) {
     89             goto success;
     90         }
     91         part = parts[index].string();
     92     }
     93 
     94     if (parseScreenWidthDp(part, &config)) {
     95         index++;
     96         if (index == N) {
     97             goto success;
     98         }
     99         part = parts[index].string();
    100     }
    101 
    102     if (parseScreenHeightDp(part, &config)) {
    103         index++;
    104         if (index == N) {
    105             goto success;
    106         }
    107         part = parts[index].string();
    108     }
    109 
    110     if (parseScreenLayoutSize(part, &config)) {
    111         index++;
    112         if (index == N) {
    113             goto success;
    114         }
    115         part = parts[index].string();
    116     }
    117 
    118     if (parseScreenLayoutLong(part, &config)) {
    119         index++;
    120         if (index == N) {
    121             goto success;
    122         }
    123         part = parts[index].string();
    124     }
    125 
    126     if (parseOrientation(part, &config)) {
    127         index++;
    128         if (index == N) {
    129             goto success;
    130         }
    131         part = parts[index].string();
    132     }
    133 
    134     if (parseUiModeType(part, &config)) {
    135         index++;
    136         if (index == N) {
    137             goto success;
    138         }
    139         part = parts[index].string();
    140     }
    141 
    142     if (parseUiModeNight(part, &config)) {
    143         index++;
    144         if (index == N) {
    145             goto success;
    146         }
    147         part = parts[index].string();
    148     }
    149 
    150     if (parseDensity(part, &config)) {
    151         index++;
    152         if (index == N) {
    153             goto success;
    154         }
    155         part = parts[index].string();
    156     }
    157 
    158     if (parseTouchscreen(part, &config)) {
    159         index++;
    160         if (index == N) {
    161             goto success;
    162         }
    163         part = parts[index].string();
    164     }
    165 
    166     if (parseKeysHidden(part, &config)) {
    167         index++;
    168         if (index == N) {
    169             goto success;
    170         }
    171         part = parts[index].string();
    172     }
    173 
    174     if (parseKeyboard(part, &config)) {
    175         index++;
    176         if (index == N) {
    177             goto success;
    178         }
    179         part = parts[index].string();
    180     }
    181 
    182     if (parseNavHidden(part, &config)) {
    183         index++;
    184         if (index == N) {
    185             goto success;
    186         }
    187         part = parts[index].string();
    188     }
    189 
    190     if (parseNavigation(part, &config)) {
    191         index++;
    192         if (index == N) {
    193             goto success;
    194         }
    195         part = parts[index].string();
    196     }
    197 
    198     if (parseScreenSize(part, &config)) {
    199         index++;
    200         if (index == N) {
    201             goto success;
    202         }
    203         part = parts[index].string();
    204     }
    205 
    206     if (parseVersion(part, &config)) {
    207         index++;
    208         if (index == N) {
    209             goto success;
    210         }
    211         part = parts[index].string();
    212     }
    213 
    214     // Unrecognized.
    215     return false;
    216 
    217 success:
    218     if (out != NULL) {
    219         applyVersionForCompatibility(&config);
    220         *out = config;
    221     }
    222     return true;
    223 }
    224 
    225 bool parseCommaSeparatedList(const String8& str, std::set<ConfigDescription>* outSet) {
    226     Vector<String8> parts = AaptUtil::splitAndLowerCase(str, ',');
    227     const size_t N = parts.size();
    228     for (size_t i = 0; i < N; i++) {
    229         ConfigDescription config;
    230         if (!parse(parts[i], &config)) {
    231             return false;
    232         }
    233         outSet->insert(config);
    234     }
    235     return true;
    236 }
    237 
    238 void applyVersionForCompatibility(ConfigDescription* config) {
    239     if (config == NULL) {
    240         return;
    241     }
    242 
    243     uint16_t minSdk = 0;
    244     if (config->density == ResTable_config::DENSITY_ANY) {
    245         minSdk = SDK_LOLLIPOP;
    246     } else if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
    247             || config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY
    248             || config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
    249         minSdk = SDK_HONEYCOMB_MR2;
    250     } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE)
    251                 != ResTable_config::UI_MODE_TYPE_ANY
    252             ||  (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT)
    253                 != ResTable_config::UI_MODE_NIGHT_ANY) {
    254         minSdk = SDK_FROYO;
    255     } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE)
    256                 != ResTable_config::SCREENSIZE_ANY
    257             ||  (config->screenLayout & ResTable_config::MASK_SCREENLONG)
    258                 != ResTable_config::SCREENLONG_ANY
    259             || config->density != ResTable_config::DENSITY_DEFAULT) {
    260         minSdk = SDK_DONUT;
    261     }
    262 
    263     if (minSdk > config->sdkVersion) {
    264         config->sdkVersion = minSdk;
    265     }
    266 }
    267 
    268 bool parseMcc(const char* name, ResTable_config* out) {
    269     if (strcmp(name, kWildcardName) == 0) {
    270         if (out) out->mcc = 0;
    271         return true;
    272     }
    273     const char* c = name;
    274     if (tolower(*c) != 'm') return false;
    275     c++;
    276     if (tolower(*c) != 'c') return false;
    277     c++;
    278     if (tolower(*c) != 'c') return false;
    279     c++;
    280 
    281     const char* val = c;
    282 
    283     while (*c >= '0' && *c <= '9') {
    284         c++;
    285     }
    286     if (*c != 0) return false;
    287     if (c-val != 3) return false;
    288 
    289     int d = atoi(val);
    290     if (d != 0) {
    291         if (out) out->mcc = d;
    292         return true;
    293     }
    294 
    295     return false;
    296 }
    297 
    298 bool parseMnc(const char* name, ResTable_config* out) {
    299     if (strcmp(name, kWildcardName) == 0) {
    300         if (out) out->mcc = 0;
    301         return true;
    302     }
    303     const char* c = name;
    304     if (tolower(*c) != 'm') return false;
    305     c++;
    306     if (tolower(*c) != 'n') return false;
    307     c++;
    308     if (tolower(*c) != 'c') return false;
    309     c++;
    310 
    311     const char* val = c;
    312 
    313     while (*c >= '0' && *c <= '9') {
    314         c++;
    315     }
    316     if (*c != 0) return false;
    317     if (c-val == 0 || c-val > 3) return false;
    318 
    319     if (out) {
    320         out->mnc = atoi(val);
    321         if (out->mnc == 0) {
    322             out->mnc = ACONFIGURATION_MNC_ZERO;
    323         }
    324     }
    325 
    326     return true;
    327 }
    328 
    329 bool parseLayoutDirection(const char* name, ResTable_config* out) {
    330     if (strcmp(name, kWildcardName) == 0) {
    331         if (out) out->screenLayout =
    332                 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
    333                 | ResTable_config::LAYOUTDIR_ANY;
    334         return true;
    335     } else if (strcmp(name, "ldltr") == 0) {
    336         if (out) out->screenLayout =
    337                 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
    338                 | ResTable_config::LAYOUTDIR_LTR;
    339         return true;
    340     } else if (strcmp(name, "ldrtl") == 0) {
    341         if (out) out->screenLayout =
    342                 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
    343                 | ResTable_config::LAYOUTDIR_RTL;
    344         return true;
    345     }
    346 
    347     return false;
    348 }
    349 
    350 bool parseScreenLayoutSize(const char* name, ResTable_config* out) {
    351     if (strcmp(name, kWildcardName) == 0) {
    352         if (out) out->screenLayout =
    353                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
    354                 | ResTable_config::SCREENSIZE_ANY;
    355         return true;
    356     } else if (strcmp(name, "small") == 0) {
    357         if (out) out->screenLayout =
    358                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
    359                 | ResTable_config::SCREENSIZE_SMALL;
    360         return true;
    361     } else if (strcmp(name, "normal") == 0) {
    362         if (out) out->screenLayout =
    363                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
    364                 | ResTable_config::SCREENSIZE_NORMAL;
    365         return true;
    366     } else if (strcmp(name, "large") == 0) {
    367         if (out) out->screenLayout =
    368                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
    369                 | ResTable_config::SCREENSIZE_LARGE;
    370         return true;
    371     } else if (strcmp(name, "xlarge") == 0) {
    372         if (out) out->screenLayout =
    373                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
    374                 | ResTable_config::SCREENSIZE_XLARGE;
    375         return true;
    376     }
    377 
    378     return false;
    379 }
    380 
    381 bool parseScreenLayoutLong(const char* name, ResTable_config* out) {
    382     if (strcmp(name, kWildcardName) == 0) {
    383         if (out) out->screenLayout =
    384                 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
    385                 | ResTable_config::SCREENLONG_ANY;
    386         return true;
    387     } else if (strcmp(name, "long") == 0) {
    388         if (out) out->screenLayout =
    389                 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
    390                 | ResTable_config::SCREENLONG_YES;
    391         return true;
    392     } else if (strcmp(name, "notlong") == 0) {
    393         if (out) out->screenLayout =
    394                 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
    395                 | ResTable_config::SCREENLONG_NO;
    396         return true;
    397     }
    398 
    399     return false;
    400 }
    401 
    402 bool parseOrientation(const char* name, ResTable_config* out) {
    403     if (strcmp(name, kWildcardName) == 0) {
    404         if (out) out->orientation = out->ORIENTATION_ANY;
    405         return true;
    406     } else if (strcmp(name, "port") == 0) {
    407         if (out) out->orientation = out->ORIENTATION_PORT;
    408         return true;
    409     } else if (strcmp(name, "land") == 0) {
    410         if (out) out->orientation = out->ORIENTATION_LAND;
    411         return true;
    412     } else if (strcmp(name, "square") == 0) {
    413         if (out) out->orientation = out->ORIENTATION_SQUARE;
    414         return true;
    415     }
    416 
    417     return false;
    418 }
    419 
    420 bool parseUiModeType(const char* name, ResTable_config* out) {
    421     if (strcmp(name, kWildcardName) == 0) {
    422         if (out) out->uiMode =
    423                 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
    424                 | ResTable_config::UI_MODE_TYPE_ANY;
    425         return true;
    426     } else if (strcmp(name, "desk") == 0) {
    427       if (out) out->uiMode =
    428               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
    429               | ResTable_config::UI_MODE_TYPE_DESK;
    430         return true;
    431     } else if (strcmp(name, "car") == 0) {
    432       if (out) out->uiMode =
    433               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
    434               | ResTable_config::UI_MODE_TYPE_CAR;
    435         return true;
    436     } else if (strcmp(name, "television") == 0) {
    437       if (out) out->uiMode =
    438               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
    439               | ResTable_config::UI_MODE_TYPE_TELEVISION;
    440         return true;
    441     } else if (strcmp(name, "appliance") == 0) {
    442       if (out) out->uiMode =
    443               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
    444               | ResTable_config::UI_MODE_TYPE_APPLIANCE;
    445         return true;
    446     } else if (strcmp(name, "watch") == 0) {
    447       if (out) out->uiMode =
    448               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
    449               | ResTable_config::UI_MODE_TYPE_WATCH;
    450         return true;
    451     }
    452 
    453     return false;
    454 }
    455 
    456 bool parseUiModeNight(const char* name, ResTable_config* out) {
    457     if (strcmp(name, kWildcardName) == 0) {
    458         if (out) out->uiMode =
    459                 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
    460                 | ResTable_config::UI_MODE_NIGHT_ANY;
    461         return true;
    462     } else if (strcmp(name, "night") == 0) {
    463         if (out) out->uiMode =
    464                 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
    465                 | ResTable_config::UI_MODE_NIGHT_YES;
    466         return true;
    467     } else if (strcmp(name, "notnight") == 0) {
    468       if (out) out->uiMode =
    469               (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
    470               | ResTable_config::UI_MODE_NIGHT_NO;
    471         return true;
    472     }
    473 
    474     return false;
    475 }
    476 
    477 bool parseDensity(const char* name, ResTable_config* out) {
    478     if (strcmp(name, kWildcardName) == 0) {
    479         if (out) out->density = ResTable_config::DENSITY_DEFAULT;
    480         return true;
    481     }
    482 
    483     if (strcmp(name, "anydpi") == 0) {
    484         if (out) out->density = ResTable_config::DENSITY_ANY;
    485         return true;
    486     }
    487 
    488     if (strcmp(name, "nodpi") == 0) {
    489         if (out) out->density = ResTable_config::DENSITY_NONE;
    490         return true;
    491     }
    492 
    493     if (strcmp(name, "ldpi") == 0) {
    494         if (out) out->density = ResTable_config::DENSITY_LOW;
    495         return true;
    496     }
    497 
    498     if (strcmp(name, "mdpi") == 0) {
    499         if (out) out->density = ResTable_config::DENSITY_MEDIUM;
    500         return true;
    501     }
    502 
    503     if (strcmp(name, "tvdpi") == 0) {
    504         if (out) out->density = ResTable_config::DENSITY_TV;
    505         return true;
    506     }
    507 
    508     if (strcmp(name, "hdpi") == 0) {
    509         if (out) out->density = ResTable_config::DENSITY_HIGH;
    510         return true;
    511     }
    512 
    513     if (strcmp(name, "xhdpi") == 0) {
    514         if (out) out->density = ResTable_config::DENSITY_XHIGH;
    515         return true;
    516     }
    517 
    518     if (strcmp(name, "xxhdpi") == 0) {
    519         if (out) out->density = ResTable_config::DENSITY_XXHIGH;
    520         return true;
    521     }
    522 
    523     if (strcmp(name, "xxxhdpi") == 0) {
    524         if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
    525         return true;
    526     }
    527 
    528     char* c = (char*)name;
    529     while (*c >= '0' && *c <= '9') {
    530         c++;
    531     }
    532 
    533     // check that we have 'dpi' after the last digit.
    534     if (toupper(c[0]) != 'D' ||
    535             toupper(c[1]) != 'P' ||
    536             toupper(c[2]) != 'I' ||
    537             c[3] != 0) {
    538         return false;
    539     }
    540 
    541     // temporarily replace the first letter with \0 to
    542     // use atoi.
    543     char tmp = c[0];
    544     c[0] = '\0';
    545 
    546     int d = atoi(name);
    547     c[0] = tmp;
    548 
    549     if (d != 0) {
    550         if (out) out->density = d;
    551         return true;
    552     }
    553 
    554     return false;
    555 }
    556 
    557 bool parseTouchscreen(const char* name, ResTable_config* out) {
    558     if (strcmp(name, kWildcardName) == 0) {
    559         if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
    560         return true;
    561     } else if (strcmp(name, "notouch") == 0) {
    562         if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
    563         return true;
    564     } else if (strcmp(name, "stylus") == 0) {
    565         if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
    566         return true;
    567     } else if (strcmp(name, "finger") == 0) {
    568         if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
    569         return true;
    570     }
    571 
    572     return false;
    573 }
    574 
    575 bool parseKeysHidden(const char* name, ResTable_config* out) {
    576     uint8_t mask = 0;
    577     uint8_t value = 0;
    578     if (strcmp(name, kWildcardName) == 0) {
    579         mask = ResTable_config::MASK_KEYSHIDDEN;
    580         value = ResTable_config::KEYSHIDDEN_ANY;
    581     } else if (strcmp(name, "keysexposed") == 0) {
    582         mask = ResTable_config::MASK_KEYSHIDDEN;
    583         value = ResTable_config::KEYSHIDDEN_NO;
    584     } else if (strcmp(name, "keyshidden") == 0) {
    585         mask = ResTable_config::MASK_KEYSHIDDEN;
    586         value = ResTable_config::KEYSHIDDEN_YES;
    587     } else if (strcmp(name, "keyssoft") == 0) {
    588         mask = ResTable_config::MASK_KEYSHIDDEN;
    589         value = ResTable_config::KEYSHIDDEN_SOFT;
    590     }
    591 
    592     if (mask != 0) {
    593         if (out) out->inputFlags = (out->inputFlags&~mask) | value;
    594         return true;
    595     }
    596 
    597     return false;
    598 }
    599 
    600 bool parseKeyboard(const char* name, ResTable_config* out) {
    601     if (strcmp(name, kWildcardName) == 0) {
    602         if (out) out->keyboard = out->KEYBOARD_ANY;
    603         return true;
    604     } else if (strcmp(name, "nokeys") == 0) {
    605         if (out) out->keyboard = out->KEYBOARD_NOKEYS;
    606         return true;
    607     } else if (strcmp(name, "qwerty") == 0) {
    608         if (out) out->keyboard = out->KEYBOARD_QWERTY;
    609         return true;
    610     } else if (strcmp(name, "12key") == 0) {
    611         if (out) out->keyboard = out->KEYBOARD_12KEY;
    612         return true;
    613     }
    614 
    615     return false;
    616 }
    617 
    618 bool parseNavHidden(const char* name, ResTable_config* out) {
    619     uint8_t mask = 0;
    620     uint8_t value = 0;
    621     if (strcmp(name, kWildcardName) == 0) {
    622         mask = ResTable_config::MASK_NAVHIDDEN;
    623         value = ResTable_config::NAVHIDDEN_ANY;
    624     } else if (strcmp(name, "navexposed") == 0) {
    625         mask = ResTable_config::MASK_NAVHIDDEN;
    626         value = ResTable_config::NAVHIDDEN_NO;
    627     } else if (strcmp(name, "navhidden") == 0) {
    628         mask = ResTable_config::MASK_NAVHIDDEN;
    629         value = ResTable_config::NAVHIDDEN_YES;
    630     }
    631 
    632     if (mask != 0) {
    633         if (out) out->inputFlags = (out->inputFlags&~mask) | value;
    634         return true;
    635     }
    636 
    637     return false;
    638 }
    639 
    640 bool parseNavigation(const char* name, ResTable_config* out) {
    641     if (strcmp(name, kWildcardName) == 0) {
    642         if (out) out->navigation = out->NAVIGATION_ANY;
    643         return true;
    644     } else if (strcmp(name, "nonav") == 0) {
    645         if (out) out->navigation = out->NAVIGATION_NONAV;
    646         return true;
    647     } else if (strcmp(name, "dpad") == 0) {
    648         if (out) out->navigation = out->NAVIGATION_DPAD;
    649         return true;
    650     } else if (strcmp(name, "trackball") == 0) {
    651         if (out) out->navigation = out->NAVIGATION_TRACKBALL;
    652         return true;
    653     } else if (strcmp(name, "wheel") == 0) {
    654         if (out) out->navigation = out->NAVIGATION_WHEEL;
    655         return true;
    656     }
    657 
    658     return false;
    659 }
    660 
    661 bool parseScreenSize(const char* name, ResTable_config* out) {
    662     if (strcmp(name, kWildcardName) == 0) {
    663         if (out) {
    664             out->screenWidth = out->SCREENWIDTH_ANY;
    665             out->screenHeight = out->SCREENHEIGHT_ANY;
    666         }
    667         return true;
    668     }
    669 
    670     const char* x = name;
    671     while (*x >= '0' && *x <= '9') x++;
    672     if (x == name || *x != 'x') return false;
    673     String8 xName(name, x-name);
    674     x++;
    675 
    676     const char* y = x;
    677     while (*y >= '0' && *y <= '9') y++;
    678     if (y == name || *y != 0) return false;
    679     String8 yName(x, y-x);
    680 
    681     uint16_t w = (uint16_t)atoi(xName.string());
    682     uint16_t h = (uint16_t)atoi(yName.string());
    683     if (w < h) {
    684         return false;
    685     }
    686 
    687     if (out) {
    688         out->screenWidth = w;
    689         out->screenHeight = h;
    690     }
    691 
    692     return true;
    693 }
    694 
    695 bool parseSmallestScreenWidthDp(const char* name, ResTable_config* out) {
    696     if (strcmp(name, kWildcardName) == 0) {
    697         if (out) {
    698             out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
    699         }
    700         return true;
    701     }
    702 
    703     if (*name != 's') return false;
    704     name++;
    705     if (*name != 'w') return false;
    706     name++;
    707     const char* x = name;
    708     while (*x >= '0' && *x <= '9') x++;
    709     if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
    710     String8 xName(name, x-name);
    711 
    712     if (out) {
    713         out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
    714     }
    715 
    716     return true;
    717 }
    718 
    719 bool parseScreenWidthDp(const char* name, ResTable_config* out) {
    720     if (strcmp(name, kWildcardName) == 0) {
    721         if (out) {
    722             out->screenWidthDp = out->SCREENWIDTH_ANY;
    723         }
    724         return true;
    725     }
    726 
    727     if (*name != 'w') return false;
    728     name++;
    729     const char* x = name;
    730     while (*x >= '0' && *x <= '9') x++;
    731     if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
    732     String8 xName(name, x-name);
    733 
    734     if (out) {
    735         out->screenWidthDp = (uint16_t)atoi(xName.string());
    736     }
    737 
    738     return true;
    739 }
    740 
    741 bool parseScreenHeightDp(const char* name, ResTable_config* out) {
    742     if (strcmp(name, kWildcardName) == 0) {
    743         if (out) {
    744             out->screenHeightDp = out->SCREENWIDTH_ANY;
    745         }
    746         return true;
    747     }
    748 
    749     if (*name != 'h') return false;
    750     name++;
    751     const char* x = name;
    752     while (*x >= '0' && *x <= '9') x++;
    753     if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
    754     String8 xName(name, x-name);
    755 
    756     if (out) {
    757         out->screenHeightDp = (uint16_t)atoi(xName.string());
    758     }
    759 
    760     return true;
    761 }
    762 
    763 bool parseVersion(const char* name, ResTable_config* out) {
    764     if (strcmp(name, kWildcardName) == 0) {
    765         if (out) {
    766             out->sdkVersion = out->SDKVERSION_ANY;
    767             out->minorVersion = out->MINORVERSION_ANY;
    768         }
    769         return true;
    770     }
    771 
    772     if (*name != 'v') {
    773         return false;
    774     }
    775 
    776     name++;
    777     const char* s = name;
    778     while (*s >= '0' && *s <= '9') s++;
    779     if (s == name || *s != 0) return false;
    780     String8 sdkName(name, s-name);
    781 
    782     if (out) {
    783         out->sdkVersion = (uint16_t)atoi(sdkName.string());
    784         out->minorVersion = 0;
    785     }
    786 
    787     return true;
    788 }
    789 
    790 String8 getVersion(const ResTable_config& config) {
    791     return String8::format("v%u", config.sdkVersion);
    792 }
    793 
    794 bool isSameExcept(const ResTable_config& a, const ResTable_config& b, int axisMask) {
    795     return a.diff(b) == axisMask;
    796 }
    797 
    798 bool isDensityOnly(const ResTable_config& config) {
    799     if (config.density == ResTable_config::DENSITY_DEFAULT) {
    800         return false;
    801     }
    802 
    803     if (config.density == ResTable_config::DENSITY_ANY) {
    804         if (config.sdkVersion != SDK_LOLLIPOP) {
    805             // Someone modified the sdkVersion from the default, this is not safe to assume.
    806             return false;
    807         }
    808     } else if (config.sdkVersion != SDK_DONUT) {
    809         return false;
    810     }
    811 
    812     const uint32_t mask = ResTable_config::CONFIG_DENSITY | ResTable_config::CONFIG_VERSION;
    813     const ConfigDescription nullConfig;
    814     return (nullConfig.diff(config) & ~mask) == 0;
    815 }
    816 
    817 } // namespace AaptConfig
    818