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 (parseScreenRound(part, &config)) {
    127         index++;
    128         if (index == N) {
    129             goto success;
    130         }
    131         part = parts[index].string();
    132     }
    133 
    134     if (parseWideColorGamut(part, &config)) {
    135         index++;
    136         if (index == N) {
    137             goto success;
    138         }
    139         part = parts[index].string();
    140     }
    141 
    142     if (parseHdr(part, &config)) {
    143         index++;
    144         if (index == N) {
    145             goto success;
    146         }
    147         part = parts[index].string();
    148     }
    149 
    150     if (parseOrientation(part, &config)) {
    151         index++;
    152         if (index == N) {
    153             goto success;
    154         }
    155         part = parts[index].string();
    156     }
    157 
    158     if (parseUiModeType(part, &config)) {
    159         index++;
    160         if (index == N) {
    161             goto success;
    162         }
    163         part = parts[index].string();
    164     }
    165 
    166     if (parseUiModeNight(part, &config)) {
    167         index++;
    168         if (index == N) {
    169             goto success;
    170         }
    171         part = parts[index].string();
    172     }
    173 
    174     if (parseDensity(part, &config)) {
    175         index++;
    176         if (index == N) {
    177             goto success;
    178         }
    179         part = parts[index].string();
    180     }
    181 
    182     if (parseTouchscreen(part, &config)) {
    183         index++;
    184         if (index == N) {
    185             goto success;
    186         }
    187         part = parts[index].string();
    188     }
    189 
    190     if (parseKeysHidden(part, &config)) {
    191         index++;
    192         if (index == N) {
    193             goto success;
    194         }
    195         part = parts[index].string();
    196     }
    197 
    198     if (parseKeyboard(part, &config)) {
    199         index++;
    200         if (index == N) {
    201             goto success;
    202         }
    203         part = parts[index].string();
    204     }
    205 
    206     if (parseNavHidden(part, &config)) {
    207         index++;
    208         if (index == N) {
    209             goto success;
    210         }
    211         part = parts[index].string();
    212     }
    213 
    214     if (parseNavigation(part, &config)) {
    215         index++;
    216         if (index == N) {
    217             goto success;
    218         }
    219         part = parts[index].string();
    220     }
    221 
    222     if (parseScreenSize(part, &config)) {
    223         index++;
    224         if (index == N) {
    225             goto success;
    226         }
    227         part = parts[index].string();
    228     }
    229 
    230     if (parseVersion(part, &config)) {
    231         index++;
    232         if (index == N) {
    233             goto success;
    234         }
    235         part = parts[index].string();
    236     }
    237 
    238     // Unrecognized.
    239     return false;
    240 
    241 success:
    242     if (out != NULL) {
    243         applyVersionForCompatibility(&config);
    244         *out = config;
    245     }
    246     return true;
    247 }
    248 
    249 bool parseCommaSeparatedList(const String8& str, std::set<ConfigDescription>* outSet) {
    250     Vector<String8> parts = AaptUtil::splitAndLowerCase(str, ',');
    251     const size_t N = parts.size();
    252     for (size_t i = 0; i < N; i++) {
    253         ConfigDescription config;
    254         if (!parse(parts[i], &config)) {
    255             return false;
    256         }
    257         outSet->insert(config);
    258     }
    259     return true;
    260 }
    261 
    262 void applyVersionForCompatibility(ConfigDescription* config) {
    263     if (config == NULL) {
    264         return;
    265     }
    266 
    267     uint16_t minSdk = 0;
    268     if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE)
    269                 == ResTable_config::UI_MODE_TYPE_VR_HEADSET
    270             || config->colorMode & ResTable_config::MASK_WIDE_COLOR_GAMUT
    271             || config->colorMode & ResTable_config::MASK_HDR) {
    272         minSdk = SDK_O;
    273     } else if (config->screenLayout2 & ResTable_config::MASK_SCREENROUND) {
    274         minSdk = SDK_MNC;
    275     } else if (config->density == ResTable_config::DENSITY_ANY) {
    276         minSdk = SDK_LOLLIPOP;
    277     } else if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
    278             || config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY
    279             || config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
    280         minSdk = SDK_HONEYCOMB_MR2;
    281     } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE)
    282                 != ResTable_config::UI_MODE_TYPE_ANY
    283             ||  (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT)
    284                 != ResTable_config::UI_MODE_NIGHT_ANY) {
    285         minSdk = SDK_FROYO;
    286     } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE)
    287                 != ResTable_config::SCREENSIZE_ANY
    288             ||  (config->screenLayout & ResTable_config::MASK_SCREENLONG)
    289                 != ResTable_config::SCREENLONG_ANY
    290             || config->density != ResTable_config::DENSITY_DEFAULT) {
    291         minSdk = SDK_DONUT;
    292     }
    293 
    294     if (minSdk > config->sdkVersion) {
    295         config->sdkVersion = minSdk;
    296     }
    297 }
    298 
    299 bool parseMcc(const char* name, ResTable_config* out) {
    300     if (strcmp(name, kWildcardName) == 0) {
    301         if (out) out->mcc = 0;
    302         return true;
    303     }
    304     const char* c = name;
    305     if (tolower(*c) != 'm') return false;
    306     c++;
    307     if (tolower(*c) != 'c') return false;
    308     c++;
    309     if (tolower(*c) != 'c') return false;
    310     c++;
    311 
    312     const char* val = c;
    313 
    314     while (*c >= '0' && *c <= '9') {
    315         c++;
    316     }
    317     if (*c != 0) return false;
    318     if (c-val != 3) return false;
    319 
    320     int d = atoi(val);
    321     if (d != 0) {
    322         if (out) out->mcc = d;
    323         return true;
    324     }
    325 
    326     return false;
    327 }
    328 
    329 bool parseMnc(const char* name, ResTable_config* out) {
    330     if (strcmp(name, kWildcardName) == 0) {
    331         if (out) out->mcc = 0;
    332         return true;
    333     }
    334     const char* c = name;
    335     if (tolower(*c) != 'm') return false;
    336     c++;
    337     if (tolower(*c) != 'n') return false;
    338     c++;
    339     if (tolower(*c) != 'c') return false;
    340     c++;
    341 
    342     const char* val = c;
    343 
    344     while (*c >= '0' && *c <= '9') {
    345         c++;
    346     }
    347     if (*c != 0) return false;
    348     if (c-val == 0 || c-val > 3) return false;
    349 
    350     if (out) {
    351         out->mnc = atoi(val);
    352         if (out->mnc == 0) {
    353             out->mnc = ACONFIGURATION_MNC_ZERO;
    354         }
    355     }
    356 
    357     return true;
    358 }
    359 
    360 bool parseLayoutDirection(const char* name, ResTable_config* out) {
    361     if (strcmp(name, kWildcardName) == 0) {
    362         if (out) out->screenLayout =
    363                 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
    364                 | ResTable_config::LAYOUTDIR_ANY;
    365         return true;
    366     } else if (strcmp(name, "ldltr") == 0) {
    367         if (out) out->screenLayout =
    368                 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
    369                 | ResTable_config::LAYOUTDIR_LTR;
    370         return true;
    371     } else if (strcmp(name, "ldrtl") == 0) {
    372         if (out) out->screenLayout =
    373                 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
    374                 | ResTable_config::LAYOUTDIR_RTL;
    375         return true;
    376     }
    377 
    378     return false;
    379 }
    380 
    381 bool parseScreenLayoutSize(const char* name, ResTable_config* out) {
    382     if (strcmp(name, kWildcardName) == 0) {
    383         if (out) out->screenLayout =
    384                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
    385                 | ResTable_config::SCREENSIZE_ANY;
    386         return true;
    387     } else if (strcmp(name, "small") == 0) {
    388         if (out) out->screenLayout =
    389                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
    390                 | ResTable_config::SCREENSIZE_SMALL;
    391         return true;
    392     } else if (strcmp(name, "normal") == 0) {
    393         if (out) out->screenLayout =
    394                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
    395                 | ResTable_config::SCREENSIZE_NORMAL;
    396         return true;
    397     } else if (strcmp(name, "large") == 0) {
    398         if (out) out->screenLayout =
    399                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
    400                 | ResTable_config::SCREENSIZE_LARGE;
    401         return true;
    402     } else if (strcmp(name, "xlarge") == 0) {
    403         if (out) out->screenLayout =
    404                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
    405                 | ResTable_config::SCREENSIZE_XLARGE;
    406         return true;
    407     }
    408 
    409     return false;
    410 }
    411 
    412 bool parseScreenLayoutLong(const char* name, ResTable_config* out) {
    413     if (strcmp(name, kWildcardName) == 0) {
    414         if (out) out->screenLayout =
    415                 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
    416                 | ResTable_config::SCREENLONG_ANY;
    417         return true;
    418     } else if (strcmp(name, "long") == 0) {
    419         if (out) out->screenLayout =
    420                 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
    421                 | ResTable_config::SCREENLONG_YES;
    422         return true;
    423     } else if (strcmp(name, "notlong") == 0) {
    424         if (out) out->screenLayout =
    425                 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
    426                 | ResTable_config::SCREENLONG_NO;
    427         return true;
    428     }
    429     return false;
    430 }
    431 
    432 bool parseScreenRound(const char* name, ResTable_config* out) {
    433     if (strcmp(name, kWildcardName) == 0) {
    434         if (out) out->screenLayout2 =
    435                 (out->screenLayout2&~ResTable_config::MASK_SCREENROUND)
    436                 | ResTable_config::SCREENROUND_ANY;
    437         return true;
    438     } else if (strcmp(name, "round") == 0) {
    439         if (out) out->screenLayout2 =
    440                 (out->screenLayout2&~ResTable_config::MASK_SCREENROUND)
    441                 | ResTable_config::SCREENROUND_YES;
    442         return true;
    443     } else if (strcmp(name, "notround") == 0) {
    444         if (out) out->screenLayout2 =
    445                 (out->screenLayout2&~ResTable_config::MASK_SCREENROUND)
    446                 | ResTable_config::SCREENROUND_NO;
    447         return true;
    448     }
    449     return false;
    450 }
    451 
    452 bool parseWideColorGamut(const char* name, ResTable_config* out) {
    453     if (strcmp(name, kWildcardName) == 0) {
    454         if (out) out->colorMode =
    455                 (out->colorMode&~ResTable_config::MASK_WIDE_COLOR_GAMUT)
    456                 | ResTable_config::WIDE_COLOR_GAMUT_ANY;
    457         return true;
    458     } else if (strcmp(name, "widecg") == 0) {
    459         if (out) out->colorMode =
    460                 (out->colorMode&~ResTable_config::MASK_WIDE_COLOR_GAMUT)
    461                 | ResTable_config::WIDE_COLOR_GAMUT_YES;
    462         return true;
    463     } else if (strcmp(name, "nowidecg") == 0) {
    464         if (out) out->colorMode =
    465                 (out->colorMode&~ResTable_config::MASK_WIDE_COLOR_GAMUT)
    466                 | ResTable_config::WIDE_COLOR_GAMUT_NO;
    467         return true;
    468     }
    469     return false;
    470 }
    471 
    472 bool parseHdr(const char* name, ResTable_config* out) {
    473     if (strcmp(name, kWildcardName) == 0) {
    474         if (out) out->colorMode =
    475                 (out->colorMode&~ResTable_config::MASK_HDR)
    476                 | ResTable_config::HDR_ANY;
    477         return true;
    478     } else if (strcmp(name, "highdr") == 0) {
    479         if (out) out->colorMode =
    480                 (out->colorMode&~ResTable_config::MASK_HDR)
    481                 | ResTable_config::HDR_YES;
    482         return true;
    483     } else if (strcmp(name, "lowdr") == 0) {
    484         if (out) out->colorMode =
    485                 (out->colorMode&~ResTable_config::MASK_HDR)
    486                 | ResTable_config::HDR_NO;
    487         return true;
    488     }
    489     return false;
    490 }
    491 
    492 bool parseOrientation(const char* name, ResTable_config* out) {
    493     if (strcmp(name, kWildcardName) == 0) {
    494         if (out) out->orientation = out->ORIENTATION_ANY;
    495         return true;
    496     } else if (strcmp(name, "port") == 0) {
    497         if (out) out->orientation = out->ORIENTATION_PORT;
    498         return true;
    499     } else if (strcmp(name, "land") == 0) {
    500         if (out) out->orientation = out->ORIENTATION_LAND;
    501         return true;
    502     } else if (strcmp(name, "square") == 0) {
    503         if (out) out->orientation = out->ORIENTATION_SQUARE;
    504         return true;
    505     }
    506 
    507     return false;
    508 }
    509 
    510 bool parseUiModeType(const char* name, ResTable_config* out) {
    511     if (strcmp(name, kWildcardName) == 0) {
    512         if (out) out->uiMode =
    513                 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
    514                 | ResTable_config::UI_MODE_TYPE_ANY;
    515         return true;
    516     } else if (strcmp(name, "desk") == 0) {
    517       if (out) out->uiMode =
    518               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
    519               | ResTable_config::UI_MODE_TYPE_DESK;
    520         return true;
    521     } else if (strcmp(name, "car") == 0) {
    522       if (out) out->uiMode =
    523               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
    524               | ResTable_config::UI_MODE_TYPE_CAR;
    525         return true;
    526     } else if (strcmp(name, "television") == 0) {
    527       if (out) out->uiMode =
    528               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
    529               | ResTable_config::UI_MODE_TYPE_TELEVISION;
    530         return true;
    531     } else if (strcmp(name, "appliance") == 0) {
    532       if (out) out->uiMode =
    533               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
    534               | ResTable_config::UI_MODE_TYPE_APPLIANCE;
    535         return true;
    536     } else if (strcmp(name, "watch") == 0) {
    537       if (out) out->uiMode =
    538               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
    539               | ResTable_config::UI_MODE_TYPE_WATCH;
    540         return true;
    541     } else if (strcmp(name, "vrheadset") == 0) {
    542       if (out) out->uiMode =
    543               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
    544               | ResTable_config::UI_MODE_TYPE_VR_HEADSET;
    545         return true;
    546     }
    547 
    548     return false;
    549 }
    550 
    551 bool parseUiModeNight(const char* name, ResTable_config* out) {
    552     if (strcmp(name, kWildcardName) == 0) {
    553         if (out) out->uiMode =
    554                 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
    555                 | ResTable_config::UI_MODE_NIGHT_ANY;
    556         return true;
    557     } else if (strcmp(name, "night") == 0) {
    558         if (out) out->uiMode =
    559                 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
    560                 | ResTable_config::UI_MODE_NIGHT_YES;
    561         return true;
    562     } else if (strcmp(name, "notnight") == 0) {
    563       if (out) out->uiMode =
    564               (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
    565               | ResTable_config::UI_MODE_NIGHT_NO;
    566         return true;
    567     }
    568 
    569     return false;
    570 }
    571 
    572 bool parseDensity(const char* name, ResTable_config* out) {
    573     if (strcmp(name, kWildcardName) == 0) {
    574         if (out) out->density = ResTable_config::DENSITY_DEFAULT;
    575         return true;
    576     }
    577 
    578     if (strcmp(name, "anydpi") == 0) {
    579         if (out) out->density = ResTable_config::DENSITY_ANY;
    580         return true;
    581     }
    582 
    583     if (strcmp(name, "nodpi") == 0) {
    584         if (out) out->density = ResTable_config::DENSITY_NONE;
    585         return true;
    586     }
    587 
    588     if (strcmp(name, "ldpi") == 0) {
    589         if (out) out->density = ResTable_config::DENSITY_LOW;
    590         return true;
    591     }
    592 
    593     if (strcmp(name, "mdpi") == 0) {
    594         if (out) out->density = ResTable_config::DENSITY_MEDIUM;
    595         return true;
    596     }
    597 
    598     if (strcmp(name, "tvdpi") == 0) {
    599         if (out) out->density = ResTable_config::DENSITY_TV;
    600         return true;
    601     }
    602 
    603     if (strcmp(name, "hdpi") == 0) {
    604         if (out) out->density = ResTable_config::DENSITY_HIGH;
    605         return true;
    606     }
    607 
    608     if (strcmp(name, "xhdpi") == 0) {
    609         if (out) out->density = ResTable_config::DENSITY_XHIGH;
    610         return true;
    611     }
    612 
    613     if (strcmp(name, "xxhdpi") == 0) {
    614         if (out) out->density = ResTable_config::DENSITY_XXHIGH;
    615         return true;
    616     }
    617 
    618     if (strcmp(name, "xxxhdpi") == 0) {
    619         if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
    620         return true;
    621     }
    622 
    623     char* c = (char*)name;
    624     while (*c >= '0' && *c <= '9') {
    625         c++;
    626     }
    627 
    628     // check that we have 'dpi' after the last digit.
    629     if (toupper(c[0]) != 'D' ||
    630             toupper(c[1]) != 'P' ||
    631             toupper(c[2]) != 'I' ||
    632             c[3] != 0) {
    633         return false;
    634     }
    635 
    636     // temporarily replace the first letter with \0 to
    637     // use atoi.
    638     char tmp = c[0];
    639     c[0] = '\0';
    640 
    641     int d = atoi(name);
    642     c[0] = tmp;
    643 
    644     if (d != 0) {
    645         if (out) out->density = d;
    646         return true;
    647     }
    648 
    649     return false;
    650 }
    651 
    652 bool parseTouchscreen(const char* name, ResTable_config* out) {
    653     if (strcmp(name, kWildcardName) == 0) {
    654         if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
    655         return true;
    656     } else if (strcmp(name, "notouch") == 0) {
    657         if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
    658         return true;
    659     } else if (strcmp(name, "stylus") == 0) {
    660         if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
    661         return true;
    662     } else if (strcmp(name, "finger") == 0) {
    663         if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
    664         return true;
    665     }
    666 
    667     return false;
    668 }
    669 
    670 bool parseKeysHidden(const char* name, ResTable_config* out) {
    671     uint8_t mask = 0;
    672     uint8_t value = 0;
    673     if (strcmp(name, kWildcardName) == 0) {
    674         mask = ResTable_config::MASK_KEYSHIDDEN;
    675         value = ResTable_config::KEYSHIDDEN_ANY;
    676     } else if (strcmp(name, "keysexposed") == 0) {
    677         mask = ResTable_config::MASK_KEYSHIDDEN;
    678         value = ResTable_config::KEYSHIDDEN_NO;
    679     } else if (strcmp(name, "keyshidden") == 0) {
    680         mask = ResTable_config::MASK_KEYSHIDDEN;
    681         value = ResTable_config::KEYSHIDDEN_YES;
    682     } else if (strcmp(name, "keyssoft") == 0) {
    683         mask = ResTable_config::MASK_KEYSHIDDEN;
    684         value = ResTable_config::KEYSHIDDEN_SOFT;
    685     }
    686 
    687     if (mask != 0) {
    688         if (out) out->inputFlags = (out->inputFlags&~mask) | value;
    689         return true;
    690     }
    691 
    692     return false;
    693 }
    694 
    695 bool parseKeyboard(const char* name, ResTable_config* out) {
    696     if (strcmp(name, kWildcardName) == 0) {
    697         if (out) out->keyboard = out->KEYBOARD_ANY;
    698         return true;
    699     } else if (strcmp(name, "nokeys") == 0) {
    700         if (out) out->keyboard = out->KEYBOARD_NOKEYS;
    701         return true;
    702     } else if (strcmp(name, "qwerty") == 0) {
    703         if (out) out->keyboard = out->KEYBOARD_QWERTY;
    704         return true;
    705     } else if (strcmp(name, "12key") == 0) {
    706         if (out) out->keyboard = out->KEYBOARD_12KEY;
    707         return true;
    708     }
    709 
    710     return false;
    711 }
    712 
    713 bool parseNavHidden(const char* name, ResTable_config* out) {
    714     uint8_t mask = 0;
    715     uint8_t value = 0;
    716     if (strcmp(name, kWildcardName) == 0) {
    717         mask = ResTable_config::MASK_NAVHIDDEN;
    718         value = ResTable_config::NAVHIDDEN_ANY;
    719     } else if (strcmp(name, "navexposed") == 0) {
    720         mask = ResTable_config::MASK_NAVHIDDEN;
    721         value = ResTable_config::NAVHIDDEN_NO;
    722     } else if (strcmp(name, "navhidden") == 0) {
    723         mask = ResTable_config::MASK_NAVHIDDEN;
    724         value = ResTable_config::NAVHIDDEN_YES;
    725     }
    726 
    727     if (mask != 0) {
    728         if (out) out->inputFlags = (out->inputFlags&~mask) | value;
    729         return true;
    730     }
    731 
    732     return false;
    733 }
    734 
    735 bool parseNavigation(const char* name, ResTable_config* out) {
    736     if (strcmp(name, kWildcardName) == 0) {
    737         if (out) out->navigation = out->NAVIGATION_ANY;
    738         return true;
    739     } else if (strcmp(name, "nonav") == 0) {
    740         if (out) out->navigation = out->NAVIGATION_NONAV;
    741         return true;
    742     } else if (strcmp(name, "dpad") == 0) {
    743         if (out) out->navigation = out->NAVIGATION_DPAD;
    744         return true;
    745     } else if (strcmp(name, "trackball") == 0) {
    746         if (out) out->navigation = out->NAVIGATION_TRACKBALL;
    747         return true;
    748     } else if (strcmp(name, "wheel") == 0) {
    749         if (out) out->navigation = out->NAVIGATION_WHEEL;
    750         return true;
    751     }
    752 
    753     return false;
    754 }
    755 
    756 bool parseScreenSize(const char* name, ResTable_config* out) {
    757     if (strcmp(name, kWildcardName) == 0) {
    758         if (out) {
    759             out->screenWidth = out->SCREENWIDTH_ANY;
    760             out->screenHeight = out->SCREENHEIGHT_ANY;
    761         }
    762         return true;
    763     }
    764 
    765     const char* x = name;
    766     while (*x >= '0' && *x <= '9') x++;
    767     if (x == name || *x != 'x') return false;
    768     String8 xName(name, x-name);
    769     x++;
    770 
    771     const char* y = x;
    772     while (*y >= '0' && *y <= '9') y++;
    773     if (y == name || *y != 0) return false;
    774     String8 yName(x, y-x);
    775 
    776     uint16_t w = (uint16_t)atoi(xName.string());
    777     uint16_t h = (uint16_t)atoi(yName.string());
    778     if (w < h) {
    779         return false;
    780     }
    781 
    782     if (out) {
    783         out->screenWidth = w;
    784         out->screenHeight = h;
    785     }
    786 
    787     return true;
    788 }
    789 
    790 bool parseSmallestScreenWidthDp(const char* name, ResTable_config* out) {
    791     if (strcmp(name, kWildcardName) == 0) {
    792         if (out) {
    793             out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
    794         }
    795         return true;
    796     }
    797 
    798     if (*name != 's') return false;
    799     name++;
    800     if (*name != 'w') return false;
    801     name++;
    802     const char* x = name;
    803     while (*x >= '0' && *x <= '9') x++;
    804     if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
    805     String8 xName(name, x-name);
    806 
    807     if (out) {
    808         out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
    809     }
    810 
    811     return true;
    812 }
    813 
    814 bool parseScreenWidthDp(const char* name, ResTable_config* out) {
    815     if (strcmp(name, kWildcardName) == 0) {
    816         if (out) {
    817             out->screenWidthDp = out->SCREENWIDTH_ANY;
    818         }
    819         return true;
    820     }
    821 
    822     if (*name != 'w') return false;
    823     name++;
    824     const char* x = name;
    825     while (*x >= '0' && *x <= '9') x++;
    826     if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
    827     String8 xName(name, x-name);
    828 
    829     if (out) {
    830         out->screenWidthDp = (uint16_t)atoi(xName.string());
    831     }
    832 
    833     return true;
    834 }
    835 
    836 bool parseScreenHeightDp(const char* name, ResTable_config* out) {
    837     if (strcmp(name, kWildcardName) == 0) {
    838         if (out) {
    839             out->screenHeightDp = out->SCREENWIDTH_ANY;
    840         }
    841         return true;
    842     }
    843 
    844     if (*name != 'h') return false;
    845     name++;
    846     const char* x = name;
    847     while (*x >= '0' && *x <= '9') x++;
    848     if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
    849     String8 xName(name, x-name);
    850 
    851     if (out) {
    852         out->screenHeightDp = (uint16_t)atoi(xName.string());
    853     }
    854 
    855     return true;
    856 }
    857 
    858 bool parseVersion(const char* name, ResTable_config* out) {
    859     if (strcmp(name, kWildcardName) == 0) {
    860         if (out) {
    861             out->sdkVersion = out->SDKVERSION_ANY;
    862             out->minorVersion = out->MINORVERSION_ANY;
    863         }
    864         return true;
    865     }
    866 
    867     if (*name != 'v') {
    868         return false;
    869     }
    870 
    871     name++;
    872     const char* s = name;
    873     while (*s >= '0' && *s <= '9') s++;
    874     if (s == name || *s != 0) return false;
    875     String8 sdkName(name, s-name);
    876 
    877     if (out) {
    878         out->sdkVersion = (uint16_t)atoi(sdkName.string());
    879         out->minorVersion = 0;
    880     }
    881 
    882     return true;
    883 }
    884 
    885 String8 getVersion(const ResTable_config& config) {
    886     return String8::format("v%u", config.sdkVersion);
    887 }
    888 
    889 bool isSameExcept(const ResTable_config& a, const ResTable_config& b, int axisMask) {
    890     return a.diff(b) == axisMask;
    891 }
    892 
    893 bool isDensityOnly(const ResTable_config& config) {
    894     if (config.density == ResTable_config::DENSITY_DEFAULT) {
    895         return false;
    896     }
    897 
    898     if (config.density == ResTable_config::DENSITY_ANY) {
    899         if (config.sdkVersion != SDK_LOLLIPOP) {
    900             // Someone modified the sdkVersion from the default, this is not safe to assume.
    901             return false;
    902         }
    903     } else if (config.sdkVersion != SDK_DONUT) {
    904         return false;
    905     }
    906 
    907     const uint32_t mask = ResTable_config::CONFIG_DENSITY | ResTable_config::CONFIG_VERSION;
    908     const ConfigDescription nullConfig;
    909     return (nullConfig.diff(config) & ~mask) == 0;
    910 }
    911 
    912 } // namespace AaptConfig
    913