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
26using android::String8;
27using android::Vector;
28using android::ResTable_config;
29
30namespace AaptConfig {
31
32static const char* kWildcardName = "any";
33
34bool 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
241success:
242    if (out != NULL) {
243        applyVersionForCompatibility(&config);
244        *out = config;
245    }
246    return true;
247}
248
249bool 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
262void 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
299bool 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
329bool 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
360bool 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
381bool 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
412bool 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
432bool 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
452bool 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
472bool 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
492bool 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
510bool 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
551bool 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
572bool 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
652bool 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
670bool 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
695bool 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
713bool 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
735bool 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
756bool 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
790bool 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
814bool 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
836bool 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
858bool 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
885String8 getVersion(const ResTable_config& config) {
886    return String8::format("v%u", config.sdkVersion);
887}
888
889bool isSameExcept(const ResTable_config& a, const ResTable_config& b, int axisMask) {
890    return a.diff(b) == axisMask;
891}
892
893bool 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