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