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