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