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#include "Locale.h" 19#include "SdkConstants.h" 20#include "StringPiece.h" 21#include "Util.h" 22 23#include <androidfw/ResourceTypes.h> 24#include <string> 25#include <vector> 26 27namespace aapt { 28 29using android::ResTable_config; 30 31static const char* kWildcardName = "any"; 32 33static bool parseMcc(const char* name, ResTable_config* out) { 34 if (strcmp(name, kWildcardName) == 0) { 35 if (out) out->mcc = 0; 36 return true; 37 } 38 const char* c = name; 39 if (tolower(*c) != 'm') return false; 40 c++; 41 if (tolower(*c) != 'c') return false; 42 c++; 43 if (tolower(*c) != 'c') return false; 44 c++; 45 46 const char* val = c; 47 48 while (*c >= '0' && *c <= '9') { 49 c++; 50 } 51 if (*c != 0) return false; 52 if (c-val != 3) return false; 53 54 int d = atoi(val); 55 if (d != 0) { 56 if (out) out->mcc = d; 57 return true; 58 } 59 60 return false; 61} 62 63static bool parseMnc(const char* name, ResTable_config* out) { 64 if (strcmp(name, kWildcardName) == 0) { 65 if (out) out->mcc = 0; 66 return true; 67 } 68 const char* c = name; 69 if (tolower(*c) != 'm') return false; 70 c++; 71 if (tolower(*c) != 'n') return false; 72 c++; 73 if (tolower(*c) != 'c') return false; 74 c++; 75 76 const char* val = c; 77 78 while (*c >= '0' && *c <= '9') { 79 c++; 80 } 81 if (*c != 0) return false; 82 if (c-val == 0 || c-val > 3) return false; 83 84 if (out) { 85 out->mnc = atoi(val); 86 if (out->mnc == 0) { 87 out->mnc = ACONFIGURATION_MNC_ZERO; 88 } 89 } 90 91 return true; 92} 93 94static bool parseLayoutDirection(const char* name, ResTable_config* out) { 95 if (strcmp(name, kWildcardName) == 0) { 96 if (out) out->screenLayout = 97 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) 98 | ResTable_config::LAYOUTDIR_ANY; 99 return true; 100 } else if (strcmp(name, "ldltr") == 0) { 101 if (out) out->screenLayout = 102 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) 103 | ResTable_config::LAYOUTDIR_LTR; 104 return true; 105 } else if (strcmp(name, "ldrtl") == 0) { 106 if (out) out->screenLayout = 107 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) 108 | ResTable_config::LAYOUTDIR_RTL; 109 return true; 110 } 111 112 return false; 113} 114 115static bool parseScreenLayoutSize(const char* name, ResTable_config* out) { 116 if (strcmp(name, kWildcardName) == 0) { 117 if (out) out->screenLayout = 118 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 119 | ResTable_config::SCREENSIZE_ANY; 120 return true; 121 } else if (strcmp(name, "small") == 0) { 122 if (out) out->screenLayout = 123 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 124 | ResTable_config::SCREENSIZE_SMALL; 125 return true; 126 } else if (strcmp(name, "normal") == 0) { 127 if (out) out->screenLayout = 128 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 129 | ResTable_config::SCREENSIZE_NORMAL; 130 return true; 131 } else if (strcmp(name, "large") == 0) { 132 if (out) out->screenLayout = 133 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 134 | ResTable_config::SCREENSIZE_LARGE; 135 return true; 136 } else if (strcmp(name, "xlarge") == 0) { 137 if (out) out->screenLayout = 138 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 139 | ResTable_config::SCREENSIZE_XLARGE; 140 return true; 141 } 142 143 return false; 144} 145 146static bool parseScreenLayoutLong(const char* name, ResTable_config* out) { 147 if (strcmp(name, kWildcardName) == 0) { 148 if (out) out->screenLayout = 149 (out->screenLayout&~ResTable_config::MASK_SCREENLONG) 150 | ResTable_config::SCREENLONG_ANY; 151 return true; 152 } else if (strcmp(name, "long") == 0) { 153 if (out) out->screenLayout = 154 (out->screenLayout&~ResTable_config::MASK_SCREENLONG) 155 | ResTable_config::SCREENLONG_YES; 156 return true; 157 } else if (strcmp(name, "notlong") == 0) { 158 if (out) out->screenLayout = 159 (out->screenLayout&~ResTable_config::MASK_SCREENLONG) 160 | ResTable_config::SCREENLONG_NO; 161 return true; 162 } 163 164 return false; 165} 166 167static bool parseOrientation(const char* name, ResTable_config* out) { 168 if (strcmp(name, kWildcardName) == 0) { 169 if (out) out->orientation = out->ORIENTATION_ANY; 170 return true; 171 } else if (strcmp(name, "port") == 0) { 172 if (out) out->orientation = out->ORIENTATION_PORT; 173 return true; 174 } else if (strcmp(name, "land") == 0) { 175 if (out) out->orientation = out->ORIENTATION_LAND; 176 return true; 177 } else if (strcmp(name, "square") == 0) { 178 if (out) out->orientation = out->ORIENTATION_SQUARE; 179 return true; 180 } 181 182 return false; 183} 184 185static bool parseUiModeType(const char* name, ResTable_config* out) { 186 if (strcmp(name, kWildcardName) == 0) { 187 if (out) out->uiMode = 188 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 189 | ResTable_config::UI_MODE_TYPE_ANY; 190 return true; 191 } else if (strcmp(name, "desk") == 0) { 192 if (out) out->uiMode = 193 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 194 | ResTable_config::UI_MODE_TYPE_DESK; 195 return true; 196 } else if (strcmp(name, "car") == 0) { 197 if (out) out->uiMode = 198 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 199 | ResTable_config::UI_MODE_TYPE_CAR; 200 return true; 201 } else if (strcmp(name, "television") == 0) { 202 if (out) out->uiMode = 203 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 204 | ResTable_config::UI_MODE_TYPE_TELEVISION; 205 return true; 206 } else if (strcmp(name, "appliance") == 0) { 207 if (out) out->uiMode = 208 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 209 | ResTable_config::UI_MODE_TYPE_APPLIANCE; 210 return true; 211 } else if (strcmp(name, "watch") == 0) { 212 if (out) out->uiMode = 213 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 214 | ResTable_config::UI_MODE_TYPE_WATCH; 215 return true; 216 } 217 218 return false; 219} 220 221static bool parseUiModeNight(const char* name, ResTable_config* out) { 222 if (strcmp(name, kWildcardName) == 0) { 223 if (out) out->uiMode = 224 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) 225 | ResTable_config::UI_MODE_NIGHT_ANY; 226 return true; 227 } else if (strcmp(name, "night") == 0) { 228 if (out) out->uiMode = 229 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) 230 | ResTable_config::UI_MODE_NIGHT_YES; 231 return true; 232 } else if (strcmp(name, "notnight") == 0) { 233 if (out) out->uiMode = 234 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) 235 | ResTable_config::UI_MODE_NIGHT_NO; 236 return true; 237 } 238 239 return false; 240} 241 242static bool parseDensity(const char* name, ResTable_config* out) { 243 if (strcmp(name, kWildcardName) == 0) { 244 if (out) out->density = ResTable_config::DENSITY_DEFAULT; 245 return true; 246 } 247 248 if (strcmp(name, "anydpi") == 0) { 249 if (out) out->density = ResTable_config::DENSITY_ANY; 250 return true; 251 } 252 253 if (strcmp(name, "nodpi") == 0) { 254 if (out) out->density = ResTable_config::DENSITY_NONE; 255 return true; 256 } 257 258 if (strcmp(name, "ldpi") == 0) { 259 if (out) out->density = ResTable_config::DENSITY_LOW; 260 return true; 261 } 262 263 if (strcmp(name, "mdpi") == 0) { 264 if (out) out->density = ResTable_config::DENSITY_MEDIUM; 265 return true; 266 } 267 268 if (strcmp(name, "tvdpi") == 0) { 269 if (out) out->density = ResTable_config::DENSITY_TV; 270 return true; 271 } 272 273 if (strcmp(name, "hdpi") == 0) { 274 if (out) out->density = ResTable_config::DENSITY_HIGH; 275 return true; 276 } 277 278 if (strcmp(name, "xhdpi") == 0) { 279 if (out) out->density = ResTable_config::DENSITY_XHIGH; 280 return true; 281 } 282 283 if (strcmp(name, "xxhdpi") == 0) { 284 if (out) out->density = ResTable_config::DENSITY_XXHIGH; 285 return true; 286 } 287 288 if (strcmp(name, "xxxhdpi") == 0) { 289 if (out) out->density = ResTable_config::DENSITY_XXXHIGH; 290 return true; 291 } 292 293 char* c = (char*)name; 294 while (*c >= '0' && *c <= '9') { 295 c++; 296 } 297 298 // check that we have 'dpi' after the last digit. 299 if (toupper(c[0]) != 'D' || 300 toupper(c[1]) != 'P' || 301 toupper(c[2]) != 'I' || 302 c[3] != 0) { 303 return false; 304 } 305 306 // temporarily replace the first letter with \0 to 307 // use atoi. 308 char tmp = c[0]; 309 c[0] = '\0'; 310 311 int d = atoi(name); 312 c[0] = tmp; 313 314 if (d != 0) { 315 if (out) out->density = d; 316 return true; 317 } 318 319 return false; 320} 321 322static bool parseTouchscreen(const char* name, ResTable_config* out) { 323 if (strcmp(name, kWildcardName) == 0) { 324 if (out) out->touchscreen = out->TOUCHSCREEN_ANY; 325 return true; 326 } else if (strcmp(name, "notouch") == 0) { 327 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH; 328 return true; 329 } else if (strcmp(name, "stylus") == 0) { 330 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS; 331 return true; 332 } else if (strcmp(name, "finger") == 0) { 333 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER; 334 return true; 335 } 336 337 return false; 338} 339 340static bool parseKeysHidden(const char* name, ResTable_config* out) { 341 uint8_t mask = 0; 342 uint8_t value = 0; 343 if (strcmp(name, kWildcardName) == 0) { 344 mask = ResTable_config::MASK_KEYSHIDDEN; 345 value = ResTable_config::KEYSHIDDEN_ANY; 346 } else if (strcmp(name, "keysexposed") == 0) { 347 mask = ResTable_config::MASK_KEYSHIDDEN; 348 value = ResTable_config::KEYSHIDDEN_NO; 349 } else if (strcmp(name, "keyshidden") == 0) { 350 mask = ResTable_config::MASK_KEYSHIDDEN; 351 value = ResTable_config::KEYSHIDDEN_YES; 352 } else if (strcmp(name, "keyssoft") == 0) { 353 mask = ResTable_config::MASK_KEYSHIDDEN; 354 value = ResTable_config::KEYSHIDDEN_SOFT; 355 } 356 357 if (mask != 0) { 358 if (out) out->inputFlags = (out->inputFlags&~mask) | value; 359 return true; 360 } 361 362 return false; 363} 364 365static bool parseKeyboard(const char* name, ResTable_config* out) { 366 if (strcmp(name, kWildcardName) == 0) { 367 if (out) out->keyboard = out->KEYBOARD_ANY; 368 return true; 369 } else if (strcmp(name, "nokeys") == 0) { 370 if (out) out->keyboard = out->KEYBOARD_NOKEYS; 371 return true; 372 } else if (strcmp(name, "qwerty") == 0) { 373 if (out) out->keyboard = out->KEYBOARD_QWERTY; 374 return true; 375 } else if (strcmp(name, "12key") == 0) { 376 if (out) out->keyboard = out->KEYBOARD_12KEY; 377 return true; 378 } 379 380 return false; 381} 382 383static bool parseNavHidden(const char* name, ResTable_config* out) { 384 uint8_t mask = 0; 385 uint8_t value = 0; 386 if (strcmp(name, kWildcardName) == 0) { 387 mask = ResTable_config::MASK_NAVHIDDEN; 388 value = ResTable_config::NAVHIDDEN_ANY; 389 } else if (strcmp(name, "navexposed") == 0) { 390 mask = ResTable_config::MASK_NAVHIDDEN; 391 value = ResTable_config::NAVHIDDEN_NO; 392 } else if (strcmp(name, "navhidden") == 0) { 393 mask = ResTable_config::MASK_NAVHIDDEN; 394 value = ResTable_config::NAVHIDDEN_YES; 395 } 396 397 if (mask != 0) { 398 if (out) out->inputFlags = (out->inputFlags&~mask) | value; 399 return true; 400 } 401 402 return false; 403} 404 405static bool parseNavigation(const char* name, ResTable_config* out) { 406 if (strcmp(name, kWildcardName) == 0) { 407 if (out) out->navigation = out->NAVIGATION_ANY; 408 return true; 409 } else if (strcmp(name, "nonav") == 0) { 410 if (out) out->navigation = out->NAVIGATION_NONAV; 411 return true; 412 } else if (strcmp(name, "dpad") == 0) { 413 if (out) out->navigation = out->NAVIGATION_DPAD; 414 return true; 415 } else if (strcmp(name, "trackball") == 0) { 416 if (out) out->navigation = out->NAVIGATION_TRACKBALL; 417 return true; 418 } else if (strcmp(name, "wheel") == 0) { 419 if (out) out->navigation = out->NAVIGATION_WHEEL; 420 return true; 421 } 422 423 return false; 424} 425 426static bool parseScreenSize(const char* name, ResTable_config* out) { 427 if (strcmp(name, kWildcardName) == 0) { 428 if (out) { 429 out->screenWidth = out->SCREENWIDTH_ANY; 430 out->screenHeight = out->SCREENHEIGHT_ANY; 431 } 432 return true; 433 } 434 435 const char* x = name; 436 while (*x >= '0' && *x <= '9') x++; 437 if (x == name || *x != 'x') return false; 438 std::string xName(name, x-name); 439 x++; 440 441 const char* y = x; 442 while (*y >= '0' && *y <= '9') y++; 443 if (y == name || *y != 0) return false; 444 std::string yName(x, y-x); 445 446 uint16_t w = (uint16_t)atoi(xName.c_str()); 447 uint16_t h = (uint16_t)atoi(yName.c_str()); 448 if (w < h) { 449 return false; 450 } 451 452 if (out) { 453 out->screenWidth = w; 454 out->screenHeight = h; 455 } 456 457 return true; 458} 459 460static bool parseSmallestScreenWidthDp(const char* name, ResTable_config* out) { 461 if (strcmp(name, kWildcardName) == 0) { 462 if (out) { 463 out->smallestScreenWidthDp = out->SCREENWIDTH_ANY; 464 } 465 return true; 466 } 467 468 if (*name != 's') return false; 469 name++; 470 if (*name != 'w') return false; 471 name++; 472 const char* x = name; 473 while (*x >= '0' && *x <= '9') x++; 474 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; 475 std::string xName(name, x-name); 476 477 if (out) { 478 out->smallestScreenWidthDp = (uint16_t)atoi(xName.c_str()); 479 } 480 481 return true; 482} 483 484static bool parseScreenWidthDp(const char* name, ResTable_config* out) { 485 if (strcmp(name, kWildcardName) == 0) { 486 if (out) { 487 out->screenWidthDp = out->SCREENWIDTH_ANY; 488 } 489 return true; 490 } 491 492 if (*name != 'w') return false; 493 name++; 494 const char* x = name; 495 while (*x >= '0' && *x <= '9') x++; 496 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; 497 std::string xName(name, x-name); 498 499 if (out) { 500 out->screenWidthDp = (uint16_t)atoi(xName.c_str()); 501 } 502 503 return true; 504} 505 506static bool parseScreenHeightDp(const char* name, ResTable_config* out) { 507 if (strcmp(name, kWildcardName) == 0) { 508 if (out) { 509 out->screenHeightDp = out->SCREENWIDTH_ANY; 510 } 511 return true; 512 } 513 514 if (*name != 'h') return false; 515 name++; 516 const char* x = name; 517 while (*x >= '0' && *x <= '9') x++; 518 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; 519 std::string xName(name, x-name); 520 521 if (out) { 522 out->screenHeightDp = (uint16_t)atoi(xName.c_str()); 523 } 524 525 return true; 526} 527 528static bool parseVersion(const char* name, ResTable_config* out) { 529 if (strcmp(name, kWildcardName) == 0) { 530 if (out) { 531 out->sdkVersion = out->SDKVERSION_ANY; 532 out->minorVersion = out->MINORVERSION_ANY; 533 } 534 return true; 535 } 536 537 if (*name != 'v') { 538 return false; 539 } 540 541 name++; 542 const char* s = name; 543 while (*s >= '0' && *s <= '9') s++; 544 if (s == name || *s != 0) return false; 545 std::string sdkName(name, s-name); 546 547 if (out) { 548 out->sdkVersion = (uint16_t)atoi(sdkName.c_str()); 549 out->minorVersion = 0; 550 } 551 552 return true; 553} 554 555bool ConfigDescription::parse(const StringPiece& str, ConfigDescription* out) { 556 std::vector<std::string> parts = util::splitAndLowercase(str, '-'); 557 558 ConfigDescription config; 559 ssize_t partsConsumed = 0; 560 LocaleValue locale; 561 562 const auto partsEnd = parts.end(); 563 auto partIter = parts.begin(); 564 565 if (str.size() == 0) { 566 goto success; 567 } 568 569 if (parseMcc(partIter->c_str(), &config)) { 570 ++partIter; 571 if (partIter == partsEnd) { 572 goto success; 573 } 574 } 575 576 if (parseMnc(partIter->c_str(), &config)) { 577 ++partIter; 578 if (partIter == partsEnd) { 579 goto success; 580 } 581 } 582 583 // Locale spans a few '-' separators, so we let it 584 // control the index. 585 partsConsumed = locale.initFromParts(partIter, partsEnd); 586 if (partsConsumed < 0) { 587 return false; 588 } else { 589 locale.writeTo(&config); 590 partIter += partsConsumed; 591 if (partIter == partsEnd) { 592 goto success; 593 } 594 } 595 596 if (parseLayoutDirection(partIter->c_str(), &config)) { 597 ++partIter; 598 if (partIter == partsEnd) { 599 goto success; 600 } 601 } 602 603 if (parseSmallestScreenWidthDp(partIter->c_str(), &config)) { 604 ++partIter; 605 if (partIter == partsEnd) { 606 goto success; 607 } 608 } 609 610 if (parseScreenWidthDp(partIter->c_str(), &config)) { 611 ++partIter; 612 if (partIter == partsEnd) { 613 goto success; 614 } 615 } 616 617 if (parseScreenHeightDp(partIter->c_str(), &config)) { 618 ++partIter; 619 if (partIter == partsEnd) { 620 goto success; 621 } 622 } 623 624 if (parseScreenLayoutSize(partIter->c_str(), &config)) { 625 ++partIter; 626 if (partIter == partsEnd) { 627 goto success; 628 } 629 } 630 631 if (parseScreenLayoutLong(partIter->c_str(), &config)) { 632 ++partIter; 633 if (partIter == partsEnd) { 634 goto success; 635 } 636 } 637 638 if (parseOrientation(partIter->c_str(), &config)) { 639 ++partIter; 640 if (partIter == partsEnd) { 641 goto success; 642 } 643 } 644 645 if (parseUiModeType(partIter->c_str(), &config)) { 646 ++partIter; 647 if (partIter == partsEnd) { 648 goto success; 649 } 650 } 651 652 if (parseUiModeNight(partIter->c_str(), &config)) { 653 ++partIter; 654 if (partIter == partsEnd) { 655 goto success; 656 } 657 } 658 659 if (parseDensity(partIter->c_str(), &config)) { 660 ++partIter; 661 if (partIter == partsEnd) { 662 goto success; 663 } 664 } 665 666 if (parseTouchscreen(partIter->c_str(), &config)) { 667 ++partIter; 668 if (partIter == partsEnd) { 669 goto success; 670 } 671 } 672 673 if (parseKeysHidden(partIter->c_str(), &config)) { 674 ++partIter; 675 if (partIter == partsEnd) { 676 goto success; 677 } 678 } 679 680 if (parseKeyboard(partIter->c_str(), &config)) { 681 ++partIter; 682 if (partIter == partsEnd) { 683 goto success; 684 } 685 } 686 687 if (parseNavHidden(partIter->c_str(), &config)) { 688 ++partIter; 689 if (partIter == partsEnd) { 690 goto success; 691 } 692 } 693 694 if (parseNavigation(partIter->c_str(), &config)) { 695 ++partIter; 696 if (partIter == partsEnd) { 697 goto success; 698 } 699 } 700 701 if (parseScreenSize(partIter->c_str(), &config)) { 702 ++partIter; 703 if (partIter == partsEnd) { 704 goto success; 705 } 706 } 707 708 if (parseVersion(partIter->c_str(), &config)) { 709 ++partIter; 710 if (partIter == partsEnd) { 711 goto success; 712 } 713 } 714 715 // Unrecognized. 716 return false; 717 718success: 719 if (out != NULL) { 720 applyVersionForCompatibility(&config); 721 *out = config; 722 } 723 return true; 724} 725 726void ConfigDescription::applyVersionForCompatibility(ConfigDescription* config) { 727 uint16_t minSdk = 0; 728 if (config->density == ResTable_config::DENSITY_ANY) { 729 minSdk = SDK_LOLLIPOP; 730 } else if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY 731 || config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY 732 || config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) { 733 minSdk = SDK_HONEYCOMB_MR2; 734 } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE) 735 != ResTable_config::UI_MODE_TYPE_ANY 736 || (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT) 737 != ResTable_config::UI_MODE_NIGHT_ANY) { 738 minSdk = SDK_FROYO; 739 } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE) 740 != ResTable_config::SCREENSIZE_ANY 741 || (config->screenLayout & ResTable_config::MASK_SCREENLONG) 742 != ResTable_config::SCREENLONG_ANY 743 || config->density != ResTable_config::DENSITY_DEFAULT) { 744 minSdk = SDK_DONUT; 745 } 746 747 if (minSdk > config->sdkVersion) { 748 config->sdkVersion = minSdk; 749 } 750} 751 752} // namespace aapt 753