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