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