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