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