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