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