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