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 
27 namespace aapt {
28 
29 using android::ResTable_config;
30 
31 static const char* kWildcardName = "any";
32 
defaultConfig()33 const ConfigDescription& ConfigDescription::defaultConfig() {
34     static ConfigDescription config = {};
35     return config;
36 }
37 
parseMcc(const char * name,ResTable_config * out)38 static 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 
parseMnc(const char * name,ResTable_config * out)68 static 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 
parseLayoutDirection(const char * name,ResTable_config * out)99 static 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 
parseScreenLayoutSize(const char * name,ResTable_config * out)120 static 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 
parseScreenLayoutLong(const char * name,ResTable_config * out)151 static 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 
parseScreenRound(const char * name,ResTable_config * out)172 static 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 
parseOrientation(const char * name,ResTable_config * out)192 static 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 
parseUiModeType(const char * name,ResTable_config * out)210 static 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 
parseUiModeNight(const char * name,ResTable_config * out)246 static 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 
parseDensity(const char * name,ResTable_config * out)267 static 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 
parseTouchscreen(const char * name,ResTable_config * out)347 static 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 
parseKeysHidden(const char * name,ResTable_config * out)365 static 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 
parseKeyboard(const char * name,ResTable_config * out)390 static 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 
parseNavHidden(const char * name,ResTable_config * out)408 static 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 
parseNavigation(const char * name,ResTable_config * out)430 static 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 
parseScreenSize(const char * name,ResTable_config * out)451 static 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 
parseSmallestScreenWidthDp(const char * name,ResTable_config * out)485 static 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 
parseScreenWidthDp(const char * name,ResTable_config * out)509 static 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 
parseScreenHeightDp(const char * name,ResTable_config * out)531 static 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 
parseVersion(const char * name,ResTable_config * out)553 static 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 
parse(const StringPiece & str,ConfigDescription * out)580 bool 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 
750 success:
751     if (out != NULL) {
752         applyVersionForCompatibility(&config);
753         *out = config;
754     }
755     return true;
756 }
757 
applyVersionForCompatibility(ConfigDescription * config)758 void 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