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