ConfigDescription.cpp revision 1a6acdbb86c3e72bdb0a4dcab3bda58cbc4ea34c
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  } else if (strcmp(name, "vrheadset") == 0) {
258    if (out)
259      out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
260                    ResTable_config::UI_MODE_TYPE_VR_HEADSET;
261    return true;
262  }
263
264  return false;
265}
266
267static bool parseUiModeNight(const char* name, ResTable_config* out) {
268  if (strcmp(name, kWildcardName) == 0) {
269    if (out)
270      out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_NIGHT) |
271                    ResTable_config::UI_MODE_NIGHT_ANY;
272    return true;
273  } else if (strcmp(name, "night") == 0) {
274    if (out)
275      out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_NIGHT) |
276                    ResTable_config::UI_MODE_NIGHT_YES;
277    return true;
278  } else if (strcmp(name, "notnight") == 0) {
279    if (out)
280      out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_NIGHT) |
281                    ResTable_config::UI_MODE_NIGHT_NO;
282    return true;
283  }
284
285  return false;
286}
287
288static bool parseDensity(const char* name, ResTable_config* out) {
289  if (strcmp(name, kWildcardName) == 0) {
290    if (out) out->density = ResTable_config::DENSITY_DEFAULT;
291    return true;
292  }
293
294  if (strcmp(name, "anydpi") == 0) {
295    if (out) out->density = ResTable_config::DENSITY_ANY;
296    return true;
297  }
298
299  if (strcmp(name, "nodpi") == 0) {
300    if (out) out->density = ResTable_config::DENSITY_NONE;
301    return true;
302  }
303
304  if (strcmp(name, "ldpi") == 0) {
305    if (out) out->density = ResTable_config::DENSITY_LOW;
306    return true;
307  }
308
309  if (strcmp(name, "mdpi") == 0) {
310    if (out) out->density = ResTable_config::DENSITY_MEDIUM;
311    return true;
312  }
313
314  if (strcmp(name, "tvdpi") == 0) {
315    if (out) out->density = ResTable_config::DENSITY_TV;
316    return true;
317  }
318
319  if (strcmp(name, "hdpi") == 0) {
320    if (out) out->density = ResTable_config::DENSITY_HIGH;
321    return true;
322  }
323
324  if (strcmp(name, "xhdpi") == 0) {
325    if (out) out->density = ResTable_config::DENSITY_XHIGH;
326    return true;
327  }
328
329  if (strcmp(name, "xxhdpi") == 0) {
330    if (out) out->density = ResTable_config::DENSITY_XXHIGH;
331    return true;
332  }
333
334  if (strcmp(name, "xxxhdpi") == 0) {
335    if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
336    return true;
337  }
338
339  char* c = (char*)name;
340  while (*c >= '0' && *c <= '9') {
341    c++;
342  }
343
344  // check that we have 'dpi' after the last digit.
345  if (toupper(c[0]) != 'D' || toupper(c[1]) != 'P' || toupper(c[2]) != 'I' ||
346      c[3] != 0) {
347    return false;
348  }
349
350  // temporarily replace the first letter with \0 to
351  // use atoi.
352  char tmp = c[0];
353  c[0] = '\0';
354
355  int d = atoi(name);
356  c[0] = tmp;
357
358  if (d != 0) {
359    if (out) out->density = d;
360    return true;
361  }
362
363  return false;
364}
365
366static bool parseTouchscreen(const char* name, ResTable_config* out) {
367  if (strcmp(name, kWildcardName) == 0) {
368    if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
369    return true;
370  } else if (strcmp(name, "notouch") == 0) {
371    if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
372    return true;
373  } else if (strcmp(name, "stylus") == 0) {
374    if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
375    return true;
376  } else if (strcmp(name, "finger") == 0) {
377    if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
378    return true;
379  }
380
381  return false;
382}
383
384static bool parseKeysHidden(const char* name, ResTable_config* out) {
385  uint8_t mask = 0;
386  uint8_t value = 0;
387  if (strcmp(name, kWildcardName) == 0) {
388    mask = ResTable_config::MASK_KEYSHIDDEN;
389    value = ResTable_config::KEYSHIDDEN_ANY;
390  } else if (strcmp(name, "keysexposed") == 0) {
391    mask = ResTable_config::MASK_KEYSHIDDEN;
392    value = ResTable_config::KEYSHIDDEN_NO;
393  } else if (strcmp(name, "keyshidden") == 0) {
394    mask = ResTable_config::MASK_KEYSHIDDEN;
395    value = ResTable_config::KEYSHIDDEN_YES;
396  } else if (strcmp(name, "keyssoft") == 0) {
397    mask = ResTable_config::MASK_KEYSHIDDEN;
398    value = ResTable_config::KEYSHIDDEN_SOFT;
399  }
400
401  if (mask != 0) {
402    if (out) out->inputFlags = (out->inputFlags & ~mask) | value;
403    return true;
404  }
405
406  return false;
407}
408
409static bool parseKeyboard(const char* name, ResTable_config* out) {
410  if (strcmp(name, kWildcardName) == 0) {
411    if (out) out->keyboard = out->KEYBOARD_ANY;
412    return true;
413  } else if (strcmp(name, "nokeys") == 0) {
414    if (out) out->keyboard = out->KEYBOARD_NOKEYS;
415    return true;
416  } else if (strcmp(name, "qwerty") == 0) {
417    if (out) out->keyboard = out->KEYBOARD_QWERTY;
418    return true;
419  } else if (strcmp(name, "12key") == 0) {
420    if (out) out->keyboard = out->KEYBOARD_12KEY;
421    return true;
422  }
423
424  return false;
425}
426
427static bool parseNavHidden(const char* name, ResTable_config* out) {
428  uint8_t mask = 0;
429  uint8_t value = 0;
430  if (strcmp(name, kWildcardName) == 0) {
431    mask = ResTable_config::MASK_NAVHIDDEN;
432    value = ResTable_config::NAVHIDDEN_ANY;
433  } else if (strcmp(name, "navexposed") == 0) {
434    mask = ResTable_config::MASK_NAVHIDDEN;
435    value = ResTable_config::NAVHIDDEN_NO;
436  } else if (strcmp(name, "navhidden") == 0) {
437    mask = ResTable_config::MASK_NAVHIDDEN;
438    value = ResTable_config::NAVHIDDEN_YES;
439  }
440
441  if (mask != 0) {
442    if (out) out->inputFlags = (out->inputFlags & ~mask) | value;
443    return true;
444  }
445
446  return false;
447}
448
449static bool parseNavigation(const char* name, ResTable_config* out) {
450  if (strcmp(name, kWildcardName) == 0) {
451    if (out) out->navigation = out->NAVIGATION_ANY;
452    return true;
453  } else if (strcmp(name, "nonav") == 0) {
454    if (out) out->navigation = out->NAVIGATION_NONAV;
455    return true;
456  } else if (strcmp(name, "dpad") == 0) {
457    if (out) out->navigation = out->NAVIGATION_DPAD;
458    return true;
459  } else if (strcmp(name, "trackball") == 0) {
460    if (out) out->navigation = out->NAVIGATION_TRACKBALL;
461    return true;
462  } else if (strcmp(name, "wheel") == 0) {
463    if (out) out->navigation = out->NAVIGATION_WHEEL;
464    return true;
465  }
466
467  return false;
468}
469
470static bool parseScreenSize(const char* name, ResTable_config* out) {
471  if (strcmp(name, kWildcardName) == 0) {
472    if (out) {
473      out->screenWidth = out->SCREENWIDTH_ANY;
474      out->screenHeight = out->SCREENHEIGHT_ANY;
475    }
476    return true;
477  }
478
479  const char* x = name;
480  while (*x >= '0' && *x <= '9') x++;
481  if (x == name || *x != 'x') return false;
482  std::string xName(name, x - name);
483  x++;
484
485  const char* y = x;
486  while (*y >= '0' && *y <= '9') y++;
487  if (y == name || *y != 0) return false;
488  std::string yName(x, y - x);
489
490  uint16_t w = (uint16_t)atoi(xName.c_str());
491  uint16_t h = (uint16_t)atoi(yName.c_str());
492  if (w < h) {
493    return false;
494  }
495
496  if (out) {
497    out->screenWidth = w;
498    out->screenHeight = h;
499  }
500
501  return true;
502}
503
504static bool parseSmallestScreenWidthDp(const char* name, ResTable_config* out) {
505  if (strcmp(name, kWildcardName) == 0) {
506    if (out) {
507      out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
508    }
509    return true;
510  }
511
512  if (*name != 's') return false;
513  name++;
514  if (*name != 'w') 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->smallestScreenWidthDp = (uint16_t)atoi(xName.c_str());
523  }
524
525  return true;
526}
527
528static bool parseScreenWidthDp(const char* name, ResTable_config* out) {
529  if (strcmp(name, kWildcardName) == 0) {
530    if (out) {
531      out->screenWidthDp = out->SCREENWIDTH_ANY;
532    }
533    return true;
534  }
535
536  if (*name != 'w') return false;
537  name++;
538  const char* x = name;
539  while (*x >= '0' && *x <= '9') x++;
540  if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
541  std::string xName(name, x - name);
542
543  if (out) {
544    out->screenWidthDp = (uint16_t)atoi(xName.c_str());
545  }
546
547  return true;
548}
549
550static bool parseScreenHeightDp(const char* name, ResTable_config* out) {
551  if (strcmp(name, kWildcardName) == 0) {
552    if (out) {
553      out->screenHeightDp = out->SCREENWIDTH_ANY;
554    }
555    return true;
556  }
557
558  if (*name != 'h') return false;
559  name++;
560  const char* x = name;
561  while (*x >= '0' && *x <= '9') x++;
562  if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
563  std::string xName(name, x - name);
564
565  if (out) {
566    out->screenHeightDp = (uint16_t)atoi(xName.c_str());
567  }
568
569  return true;
570}
571
572static bool parseVersion(const char* name, ResTable_config* out) {
573  if (strcmp(name, kWildcardName) == 0) {
574    if (out) {
575      out->sdkVersion = out->SDKVERSION_ANY;
576      out->minorVersion = out->MINORVERSION_ANY;
577    }
578    return true;
579  }
580
581  if (*name != 'v') {
582    return false;
583  }
584
585  name++;
586  const char* s = name;
587  while (*s >= '0' && *s <= '9') s++;
588  if (s == name || *s != 0) return false;
589  std::string sdkName(name, s - name);
590
591  if (out) {
592    out->sdkVersion = (uint16_t)atoi(sdkName.c_str());
593    out->minorVersion = 0;
594  }
595
596  return true;
597}
598
599bool ConfigDescription::Parse(const StringPiece& str, ConfigDescription* out) {
600  std::vector<std::string> parts = util::SplitAndLowercase(str, '-');
601
602  ConfigDescription config;
603  ssize_t parts_consumed = 0;
604  LocaleValue locale;
605
606  const auto parts_end = parts.end();
607  auto part_iter = parts.begin();
608
609  if (str.size() == 0) {
610    goto success;
611  }
612
613  if (parseMcc(part_iter->c_str(), &config)) {
614    ++part_iter;
615    if (part_iter == parts_end) {
616      goto success;
617    }
618  }
619
620  if (parseMnc(part_iter->c_str(), &config)) {
621    ++part_iter;
622    if (part_iter == parts_end) {
623      goto success;
624    }
625  }
626
627  // Locale spans a few '-' separators, so we let it
628  // control the index.
629  parts_consumed = locale.InitFromParts(part_iter, parts_end);
630  if (parts_consumed < 0) {
631    return false;
632  } else {
633    locale.WriteTo(&config);
634    part_iter += parts_consumed;
635    if (part_iter == parts_end) {
636      goto success;
637    }
638  }
639
640  if (parseLayoutDirection(part_iter->c_str(), &config)) {
641    ++part_iter;
642    if (part_iter == parts_end) {
643      goto success;
644    }
645  }
646
647  if (parseSmallestScreenWidthDp(part_iter->c_str(), &config)) {
648    ++part_iter;
649    if (part_iter == parts_end) {
650      goto success;
651    }
652  }
653
654  if (parseScreenWidthDp(part_iter->c_str(), &config)) {
655    ++part_iter;
656    if (part_iter == parts_end) {
657      goto success;
658    }
659  }
660
661  if (parseScreenHeightDp(part_iter->c_str(), &config)) {
662    ++part_iter;
663    if (part_iter == parts_end) {
664      goto success;
665    }
666  }
667
668  if (parseScreenLayoutSize(part_iter->c_str(), &config)) {
669    ++part_iter;
670    if (part_iter == parts_end) {
671      goto success;
672    }
673  }
674
675  if (parseScreenLayoutLong(part_iter->c_str(), &config)) {
676    ++part_iter;
677    if (part_iter == parts_end) {
678      goto success;
679    }
680  }
681
682  if (parseScreenRound(part_iter->c_str(), &config)) {
683    ++part_iter;
684    if (part_iter == parts_end) {
685      goto success;
686    }
687  }
688
689  if (parseOrientation(part_iter->c_str(), &config)) {
690    ++part_iter;
691    if (part_iter == parts_end) {
692      goto success;
693    }
694  }
695
696  if (parseUiModeType(part_iter->c_str(), &config)) {
697    ++part_iter;
698    if (part_iter == parts_end) {
699      goto success;
700    }
701  }
702
703  if (parseUiModeNight(part_iter->c_str(), &config)) {
704    ++part_iter;
705    if (part_iter == parts_end) {
706      goto success;
707    }
708  }
709
710  if (parseDensity(part_iter->c_str(), &config)) {
711    ++part_iter;
712    if (part_iter == parts_end) {
713      goto success;
714    }
715  }
716
717  if (parseTouchscreen(part_iter->c_str(), &config)) {
718    ++part_iter;
719    if (part_iter == parts_end) {
720      goto success;
721    }
722  }
723
724  if (parseKeysHidden(part_iter->c_str(), &config)) {
725    ++part_iter;
726    if (part_iter == parts_end) {
727      goto success;
728    }
729  }
730
731  if (parseKeyboard(part_iter->c_str(), &config)) {
732    ++part_iter;
733    if (part_iter == parts_end) {
734      goto success;
735    }
736  }
737
738  if (parseNavHidden(part_iter->c_str(), &config)) {
739    ++part_iter;
740    if (part_iter == parts_end) {
741      goto success;
742    }
743  }
744
745  if (parseNavigation(part_iter->c_str(), &config)) {
746    ++part_iter;
747    if (part_iter == parts_end) {
748      goto success;
749    }
750  }
751
752  if (parseScreenSize(part_iter->c_str(), &config)) {
753    ++part_iter;
754    if (part_iter == parts_end) {
755      goto success;
756    }
757  }
758
759  if (parseVersion(part_iter->c_str(), &config)) {
760    ++part_iter;
761    if (part_iter == parts_end) {
762      goto success;
763    }
764  }
765
766  // Unrecognized.
767  return false;
768
769success:
770  if (out != NULL) {
771    ApplyVersionForCompatibility(&config);
772    *out = config;
773  }
774  return true;
775}
776
777void ConfigDescription::ApplyVersionForCompatibility(
778    ConfigDescription* config) {
779  uint16_t min_sdk = 0;
780  if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE)
781                == ResTable_config::UI_MODE_TYPE_VR_HEADSET) {
782        min_sdk = SDK_O;
783  } else if (config->screenLayout2 & ResTable_config::MASK_SCREENROUND) {
784    min_sdk = SDK_MARSHMALLOW;
785  } else if (config->density == ResTable_config::DENSITY_ANY) {
786    min_sdk = SDK_LOLLIPOP;
787  } else if (config->smallestScreenWidthDp !=
788                 ResTable_config::SCREENWIDTH_ANY ||
789             config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY ||
790             config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
791    min_sdk = SDK_HONEYCOMB_MR2;
792  } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE) !=
793                 ResTable_config::UI_MODE_TYPE_ANY ||
794             (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT) !=
795                 ResTable_config::UI_MODE_NIGHT_ANY) {
796    min_sdk = SDK_FROYO;
797  } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE) !=
798                 ResTable_config::SCREENSIZE_ANY ||
799             (config->screenLayout & ResTable_config::MASK_SCREENLONG) !=
800                 ResTable_config::SCREENLONG_ANY ||
801             config->density != ResTable_config::DENSITY_DEFAULT) {
802    min_sdk = SDK_DONUT;
803  }
804
805  if (min_sdk > config->sdkVersion) {
806    config->sdkVersion = min_sdk;
807  }
808}
809
810ConfigDescription ConfigDescription::CopyWithoutSdkVersion() const {
811  ConfigDescription copy = *this;
812  copy.sdkVersion = 0;
813  return copy;
814}
815
816bool ConfigDescription::Dominates(const ConfigDescription& o) const {
817  if (*this == DefaultConfig() || *this == o) {
818    return true;
819  }
820  return MatchWithDensity(o) && !o.MatchWithDensity(*this) &&
821         !isMoreSpecificThan(o) && !o.HasHigherPrecedenceThan(*this);
822}
823
824bool ConfigDescription::HasHigherPrecedenceThan(
825    const ConfigDescription& o) const {
826  // The order of the following tests defines the importance of one
827  // configuration parameter over another. Those tests first are more
828  // important, trumping any values in those following them.
829  // The ordering should be the same as ResTable_config#isBetterThan.
830  if (mcc || o.mcc) return (!o.mcc);
831  if (mnc || o.mnc) return (!o.mnc);
832  if (language[0] || o.language[0]) return (!o.language[0]);
833  if (country[0] || o.country[0]) return (!o.country[0]);
834  // Script and variant require either a language or country, both of which
835  // have higher precedence.
836  if ((screenLayout | o.screenLayout) & MASK_LAYOUTDIR) {
837    return !(o.screenLayout & MASK_LAYOUTDIR);
838  }
839  if (smallestScreenWidthDp || o.smallestScreenWidthDp)
840    return (!o.smallestScreenWidthDp);
841  if (screenWidthDp || o.screenWidthDp) return (!o.screenWidthDp);
842  if (screenHeightDp || o.screenHeightDp) return (!o.screenHeightDp);
843  if ((screenLayout | o.screenLayout) & MASK_SCREENSIZE) {
844    return !(o.screenLayout & MASK_SCREENSIZE);
845  }
846  if ((screenLayout | o.screenLayout) & MASK_SCREENLONG) {
847    return !(o.screenLayout & MASK_SCREENLONG);
848  }
849  if ((screenLayout2 | o.screenLayout2) & MASK_SCREENROUND) {
850    return !(o.screenLayout2 & MASK_SCREENROUND);
851  }
852  if (orientation || o.orientation) return (!o.orientation);
853  if ((uiMode | o.uiMode) & MASK_UI_MODE_TYPE) {
854    return !(o.uiMode & MASK_UI_MODE_TYPE);
855  }
856  if ((uiMode | o.uiMode) & MASK_UI_MODE_NIGHT) {
857    return !(o.uiMode & MASK_UI_MODE_NIGHT);
858  }
859  if (density || o.density) return (!o.density);
860  if (touchscreen || o.touchscreen) return (!o.touchscreen);
861  if ((inputFlags | o.inputFlags) & MASK_KEYSHIDDEN) {
862    return !(o.inputFlags & MASK_KEYSHIDDEN);
863  }
864  if ((inputFlags | o.inputFlags) & MASK_NAVHIDDEN) {
865    return !(o.inputFlags & MASK_NAVHIDDEN);
866  }
867  if (keyboard || o.keyboard) return (!o.keyboard);
868  if (navigation || o.navigation) return (!o.navigation);
869  if (screenWidth || o.screenWidth) return (!o.screenWidth);
870  if (screenHeight || o.screenHeight) return (!o.screenHeight);
871  if (sdkVersion || o.sdkVersion) return (!o.sdkVersion);
872  if (minorVersion || o.minorVersion) return (!o.minorVersion);
873  // Both configurations have nothing defined except some possible future
874  // value. Returning the comparison of the two configurations is a
875  // "best effort" at this point to protect against incorrect dominations.
876  return *this != o;
877}
878
879bool ConfigDescription::ConflictsWith(const ConfigDescription& o) const {
880  // This method should be updated as new configuration parameters are
881  // introduced (e.g. screenConfig2).
882  auto pred = [](const uint32_t a, const uint32_t b) -> bool {
883    return a == 0 || b == 0 || a == b;
884  };
885  // The values here can be found in ResTable_config#match. Density and range
886  // values can't lead to conflicts, and are ignored.
887  return !pred(mcc, o.mcc) || !pred(mnc, o.mnc) || !pred(locale, o.locale) ||
888         !pred(screenLayout & MASK_LAYOUTDIR,
889               o.screenLayout & MASK_LAYOUTDIR) ||
890         !pred(screenLayout & MASK_SCREENLONG,
891               o.screenLayout & MASK_SCREENLONG) ||
892         !pred(screenLayout & MASK_UI_MODE_TYPE,
893               o.screenLayout & MASK_UI_MODE_TYPE) ||
894         !pred(uiMode & MASK_UI_MODE_TYPE, o.uiMode & MASK_UI_MODE_TYPE) ||
895         !pred(uiMode & MASK_UI_MODE_NIGHT, o.uiMode & MASK_UI_MODE_NIGHT) ||
896         !pred(screenLayout2 & MASK_SCREENROUND,
897               o.screenLayout2 & MASK_SCREENROUND) ||
898         !pred(orientation, o.orientation) ||
899         !pred(touchscreen, o.touchscreen) ||
900         !pred(inputFlags & MASK_KEYSHIDDEN, o.inputFlags & MASK_KEYSHIDDEN) ||
901         !pred(inputFlags & MASK_NAVHIDDEN, o.inputFlags & MASK_NAVHIDDEN) ||
902         !pred(keyboard, o.keyboard) || !pred(navigation, o.navigation);
903}
904
905bool ConfigDescription::IsCompatibleWith(const ConfigDescription& o) const {
906  return !ConflictsWith(o) && !Dominates(o) && !o.Dominates(*this);
907}
908
909}  // namespace aapt
910