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