AaptAssets.cpp revision 6c191299a73388cd593809c0b66bafbd08fd2982
1// 2// Copyright 2006 The Android Open Source Project 3// 4 5#include "AaptAssets.h" 6#include "ResourceFilter.h" 7#include "Main.h" 8 9#include <utils/misc.h> 10#include <utils/SortedVector.h> 11 12#include <ctype.h> 13#include <dirent.h> 14#include <errno.h> 15 16static const char* kDefaultLocale = "default"; 17static const char* kWildcardName = "any"; 18static const char* kAssetDir = "assets"; 19static const char* kResourceDir = "res"; 20static const char* kValuesDir = "values"; 21static const char* kMipmapDir = "mipmap"; 22static const char* kInvalidChars = "/\\:"; 23static const size_t kMaxAssetFileName = 100; 24 25static const String8 kResString(kResourceDir); 26 27/* 28 * Names of asset files must meet the following criteria: 29 * 30 * - the filename length must be less than kMaxAssetFileName bytes long 31 * (and can't be empty) 32 * - all characters must be 7-bit printable ASCII 33 * - none of { '/' '\\' ':' } 34 * 35 * Pass in just the filename, not the full path. 36 */ 37static bool validateFileName(const char* fileName) 38{ 39 const char* cp = fileName; 40 size_t len = 0; 41 42 while (*cp != '\0') { 43 if ((*cp & 0x80) != 0) 44 return false; // reject high ASCII 45 if (*cp < 0x20 || *cp >= 0x7f) 46 return false; // reject control chars and 0x7f 47 if (strchr(kInvalidChars, *cp) != NULL) 48 return false; // reject path sep chars 49 cp++; 50 len++; 51 } 52 53 if (len < 1 || len > kMaxAssetFileName) 54 return false; // reject empty or too long 55 56 return true; 57} 58 59// The default to use if no other ignore pattern is defined. 60const char * const gDefaultIgnoreAssets = 61 "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~"; 62// The ignore pattern that can be passed via --ignore-assets in Main.cpp 63const char * gUserIgnoreAssets = NULL; 64 65static bool isHidden(const char *root, const char *path) 66{ 67 // Patterns syntax: 68 // - Delimiter is : 69 // - Entry can start with the flag ! to avoid printing a warning 70 // about the file being ignored. 71 // - Entry can have the flag "<dir>" to match only directories 72 // or <file> to match only files. Default is to match both. 73 // - Entry can be a simplified glob "<prefix>*" or "*<suffix>" 74 // where prefix/suffix must have at least 1 character (so that 75 // we don't match a '*' catch-all pattern.) 76 // - The special filenames "." and ".." are always ignored. 77 // - Otherwise the full string is matched. 78 // - match is not case-sensitive. 79 80 if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) { 81 return true; 82 } 83 84 const char *delim = ":"; 85 const char *p = gUserIgnoreAssets; 86 if (!p || !p[0]) { 87 p = getenv("ANDROID_AAPT_IGNORE"); 88 } 89 if (!p || !p[0]) { 90 p = gDefaultIgnoreAssets; 91 } 92 char *patterns = strdup(p); 93 94 bool ignore = false; 95 bool chatty = true; 96 char *matchedPattern = NULL; 97 98 String8 fullPath(root); 99 fullPath.appendPath(path); 100 FileType type = getFileType(fullPath); 101 102 int plen = strlen(path); 103 104 // Note: we don't have strtok_r under mingw. 105 for(char *token = strtok(patterns, delim); 106 !ignore && token != NULL; 107 token = strtok(NULL, delim)) { 108 chatty = token[0] != '!'; 109 if (!chatty) token++; // skip ! 110 if (strncasecmp(token, "<dir>" , 5) == 0) { 111 if (type != kFileTypeDirectory) continue; 112 token += 5; 113 } 114 if (strncasecmp(token, "<file>", 6) == 0) { 115 if (type != kFileTypeRegular) continue; 116 token += 6; 117 } 118 119 matchedPattern = token; 120 int n = strlen(token); 121 122 if (token[0] == '*') { 123 // Match *suffix 124 token++; 125 n--; 126 if (n <= plen) { 127 ignore = strncasecmp(token, path + plen - n, n) == 0; 128 } 129 } else if (n > 1 && token[n - 1] == '*') { 130 // Match prefix* 131 ignore = strncasecmp(token, path, n - 1) == 0; 132 } else { 133 ignore = strcasecmp(token, path) == 0; 134 } 135 } 136 137 if (ignore && chatty) { 138 fprintf(stderr, " (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n", 139 type == kFileTypeDirectory ? "dir" : "file", 140 path, 141 matchedPattern ? matchedPattern : ""); 142 } 143 144 free(patterns); 145 return ignore; 146} 147 148// ========================================================================= 149// ========================================================================= 150// ========================================================================= 151 152status_t 153AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value) 154{ 155 ResTable_config config; 156 157 // IMSI - MCC 158 if (getMccName(part.string(), &config)) { 159 *axis = AXIS_MCC; 160 *value = config.mcc; 161 return 0; 162 } 163 164 // IMSI - MNC 165 if (getMncName(part.string(), &config)) { 166 *axis = AXIS_MNC; 167 *value = config.mnc; 168 return 0; 169 } 170 171 // locale - language 172 if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) { 173 *axis = AXIS_LANGUAGE; 174 *value = part[1] << 8 | part[0]; 175 return 0; 176 } 177 178 // locale - language_REGION 179 if (part.length() == 5 && isalpha(part[0]) && isalpha(part[1]) 180 && part[2] == '_' && isalpha(part[3]) && isalpha(part[4])) { 181 *axis = AXIS_LANGUAGE; 182 *value = (part[4] << 24) | (part[3] << 16) | (part[1] << 8) | (part[0]); 183 return 0; 184 } 185 186 // layout direction 187 if (getLayoutDirectionName(part.string(), &config)) { 188 *axis = AXIS_LAYOUTDIR; 189 *value = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR); 190 return 0; 191 } 192 193 // smallest screen dp width 194 if (getSmallestScreenWidthDpName(part.string(), &config)) { 195 *axis = AXIS_SMALLESTSCREENWIDTHDP; 196 *value = config.smallestScreenWidthDp; 197 return 0; 198 } 199 200 // screen dp width 201 if (getScreenWidthDpName(part.string(), &config)) { 202 *axis = AXIS_SCREENWIDTHDP; 203 *value = config.screenWidthDp; 204 return 0; 205 } 206 207 // screen dp height 208 if (getScreenHeightDpName(part.string(), &config)) { 209 *axis = AXIS_SCREENHEIGHTDP; 210 *value = config.screenHeightDp; 211 return 0; 212 } 213 214 // screen layout size 215 if (getScreenLayoutSizeName(part.string(), &config)) { 216 *axis = AXIS_SCREENLAYOUTSIZE; 217 *value = (config.screenLayout&ResTable_config::MASK_SCREENSIZE); 218 return 0; 219 } 220 221 // screen layout long 222 if (getScreenLayoutLongName(part.string(), &config)) { 223 *axis = AXIS_SCREENLAYOUTLONG; 224 *value = (config.screenLayout&ResTable_config::MASK_SCREENLONG); 225 return 0; 226 } 227 228 // orientation 229 if (getOrientationName(part.string(), &config)) { 230 *axis = AXIS_ORIENTATION; 231 *value = config.orientation; 232 return 0; 233 } 234 235 // ui mode type 236 if (getUiModeTypeName(part.string(), &config)) { 237 *axis = AXIS_UIMODETYPE; 238 *value = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE); 239 return 0; 240 } 241 242 // ui mode night 243 if (getUiModeNightName(part.string(), &config)) { 244 *axis = AXIS_UIMODENIGHT; 245 *value = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT); 246 return 0; 247 } 248 249 // density 250 if (getDensityName(part.string(), &config)) { 251 *axis = AXIS_DENSITY; 252 *value = config.density; 253 return 0; 254 } 255 256 // touchscreen 257 if (getTouchscreenName(part.string(), &config)) { 258 *axis = AXIS_TOUCHSCREEN; 259 *value = config.touchscreen; 260 return 0; 261 } 262 263 // keyboard hidden 264 if (getKeysHiddenName(part.string(), &config)) { 265 *axis = AXIS_KEYSHIDDEN; 266 *value = config.inputFlags; 267 return 0; 268 } 269 270 // keyboard 271 if (getKeyboardName(part.string(), &config)) { 272 *axis = AXIS_KEYBOARD; 273 *value = config.keyboard; 274 return 0; 275 } 276 277 // navigation hidden 278 if (getNavHiddenName(part.string(), &config)) { 279 *axis = AXIS_NAVHIDDEN; 280 *value = config.inputFlags; 281 return 0; 282 } 283 284 // navigation 285 if (getNavigationName(part.string(), &config)) { 286 *axis = AXIS_NAVIGATION; 287 *value = config.navigation; 288 return 0; 289 } 290 291 // screen size 292 if (getScreenSizeName(part.string(), &config)) { 293 *axis = AXIS_SCREENSIZE; 294 *value = config.screenSize; 295 return 0; 296 } 297 298 // version 299 if (getVersionName(part.string(), &config)) { 300 *axis = AXIS_VERSION; 301 *value = config.version; 302 return 0; 303 } 304 305 return 1; 306} 307 308uint32_t 309AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis) 310{ 311 switch (axis) { 312 case AXIS_MCC: 313 return config.mcc; 314 case AXIS_MNC: 315 return config.mnc; 316 case AXIS_LANGUAGE: 317 return (((uint32_t)config.country[1]) << 24) | (((uint32_t)config.country[0]) << 16) 318 | (((uint32_t)config.language[1]) << 8) | (config.language[0]); 319 case AXIS_LAYOUTDIR: 320 return config.screenLayout&ResTable_config::MASK_LAYOUTDIR; 321 case AXIS_SCREENLAYOUTSIZE: 322 return config.screenLayout&ResTable_config::MASK_SCREENSIZE; 323 case AXIS_ORIENTATION: 324 return config.orientation; 325 case AXIS_UIMODETYPE: 326 return (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE); 327 case AXIS_UIMODENIGHT: 328 return (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT); 329 case AXIS_DENSITY: 330 return config.density; 331 case AXIS_TOUCHSCREEN: 332 return config.touchscreen; 333 case AXIS_KEYSHIDDEN: 334 return config.inputFlags; 335 case AXIS_KEYBOARD: 336 return config.keyboard; 337 case AXIS_NAVIGATION: 338 return config.navigation; 339 case AXIS_SCREENSIZE: 340 return config.screenSize; 341 case AXIS_SMALLESTSCREENWIDTHDP: 342 return config.smallestScreenWidthDp; 343 case AXIS_SCREENWIDTHDP: 344 return config.screenWidthDp; 345 case AXIS_SCREENHEIGHTDP: 346 return config.screenHeightDp; 347 case AXIS_VERSION: 348 return config.version; 349 } 350 return 0; 351} 352 353bool 354AaptGroupEntry::configSameExcept(const ResTable_config& config, 355 const ResTable_config& otherConfig, int axis) 356{ 357 for (int i=AXIS_START; i<=AXIS_END; i++) { 358 if (i == axis) { 359 continue; 360 } 361 if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) { 362 return false; 363 } 364 } 365 return true; 366} 367 368bool 369AaptGroupEntry::initFromDirName(const char* dir, String8* resType) 370{ 371 mParamsChanged = true; 372 373 Vector<String8> parts; 374 375 String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den; 376 String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers; 377 String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp; 378 379 const char *p = dir; 380 const char *q; 381 while (NULL != (q = strchr(p, '-'))) { 382 String8 val(p, q-p); 383 val.toLower(); 384 parts.add(val); 385 //printf("part: %s\n", parts[parts.size()-1].string()); 386 p = q+1; 387 } 388 String8 val(p); 389 val.toLower(); 390 parts.add(val); 391 //printf("part: %s\n", parts[parts.size()-1].string()); 392 393 const int N = parts.size(); 394 int index = 0; 395 String8 part = parts[index]; 396 397 // resource type 398 if (!isValidResourceType(part)) { 399 return false; 400 } 401 *resType = part; 402 403 index++; 404 if (index == N) { 405 goto success; 406 } 407 part = parts[index]; 408 409 // imsi - mcc 410 if (getMccName(part.string())) { 411 mcc = part; 412 413 index++; 414 if (index == N) { 415 goto success; 416 } 417 part = parts[index]; 418 } else { 419 //printf("not mcc: %s\n", part.string()); 420 } 421 422 // imsi - mnc 423 if (getMncName(part.string())) { 424 mnc = part; 425 426 index++; 427 if (index == N) { 428 goto success; 429 } 430 part = parts[index]; 431 } else { 432 //printf("not mcc: %s\n", part.string()); 433 } 434 435 // locale - language 436 if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) { 437 loc = part; 438 439 index++; 440 if (index == N) { 441 goto success; 442 } 443 part = parts[index]; 444 } else { 445 //printf("not language: %s\n", part.string()); 446 } 447 448 // locale - region 449 if (loc.length() > 0 450 && part.length() == 3 && part[0] == 'r' && part[0] && part[1]) { 451 loc += "-"; 452 part.toUpper(); 453 loc += part.string() + 1; 454 455 index++; 456 if (index == N) { 457 goto success; 458 } 459 part = parts[index]; 460 } else { 461 //printf("not region: %s\n", part.string()); 462 } 463 464 if (getLayoutDirectionName(part.string())) { 465 layoutDir = part; 466 467 index++; 468 if (index == N) { 469 goto success; 470 } 471 part = parts[index]; 472 } else { 473 //printf("not layout direction: %s\n", part.string()); 474 } 475 476 if (getSmallestScreenWidthDpName(part.string())) { 477 smallestwidthdp = part; 478 479 index++; 480 if (index == N) { 481 goto success; 482 } 483 part = parts[index]; 484 } else { 485 //printf("not smallest screen width dp: %s\n", part.string()); 486 } 487 488 if (getScreenWidthDpName(part.string())) { 489 widthdp = part; 490 491 index++; 492 if (index == N) { 493 goto success; 494 } 495 part = parts[index]; 496 } else { 497 //printf("not screen width dp: %s\n", part.string()); 498 } 499 500 if (getScreenHeightDpName(part.string())) { 501 heightdp = part; 502 503 index++; 504 if (index == N) { 505 goto success; 506 } 507 part = parts[index]; 508 } else { 509 //printf("not screen height dp: %s\n", part.string()); 510 } 511 512 if (getScreenLayoutSizeName(part.string())) { 513 layoutsize = part; 514 515 index++; 516 if (index == N) { 517 goto success; 518 } 519 part = parts[index]; 520 } else { 521 //printf("not screen layout size: %s\n", part.string()); 522 } 523 524 if (getScreenLayoutLongName(part.string())) { 525 layoutlong = part; 526 527 index++; 528 if (index == N) { 529 goto success; 530 } 531 part = parts[index]; 532 } else { 533 //printf("not screen layout long: %s\n", part.string()); 534 } 535 536 // orientation 537 if (getOrientationName(part.string())) { 538 orient = part; 539 540 index++; 541 if (index == N) { 542 goto success; 543 } 544 part = parts[index]; 545 } else { 546 //printf("not orientation: %s\n", part.string()); 547 } 548 549 // ui mode type 550 if (getUiModeTypeName(part.string())) { 551 uiModeType = part; 552 553 index++; 554 if (index == N) { 555 goto success; 556 } 557 part = parts[index]; 558 } else { 559 //printf("not ui mode type: %s\n", part.string()); 560 } 561 562 // ui mode night 563 if (getUiModeNightName(part.string())) { 564 uiModeNight = part; 565 566 index++; 567 if (index == N) { 568 goto success; 569 } 570 part = parts[index]; 571 } else { 572 //printf("not ui mode night: %s\n", part.string()); 573 } 574 575 // density 576 if (getDensityName(part.string())) { 577 den = part; 578 579 index++; 580 if (index == N) { 581 goto success; 582 } 583 part = parts[index]; 584 } else { 585 //printf("not density: %s\n", part.string()); 586 } 587 588 // touchscreen 589 if (getTouchscreenName(part.string())) { 590 touch = part; 591 592 index++; 593 if (index == N) { 594 goto success; 595 } 596 part = parts[index]; 597 } else { 598 //printf("not touchscreen: %s\n", part.string()); 599 } 600 601 // keyboard hidden 602 if (getKeysHiddenName(part.string())) { 603 keysHidden = part; 604 605 index++; 606 if (index == N) { 607 goto success; 608 } 609 part = parts[index]; 610 } else { 611 //printf("not keysHidden: %s\n", part.string()); 612 } 613 614 // keyboard 615 if (getKeyboardName(part.string())) { 616 key = part; 617 618 index++; 619 if (index == N) { 620 goto success; 621 } 622 part = parts[index]; 623 } else { 624 //printf("not keyboard: %s\n", part.string()); 625 } 626 627 // navigation hidden 628 if (getNavHiddenName(part.string())) { 629 navHidden = part; 630 631 index++; 632 if (index == N) { 633 goto success; 634 } 635 part = parts[index]; 636 } else { 637 //printf("not navHidden: %s\n", part.string()); 638 } 639 640 if (getNavigationName(part.string())) { 641 nav = part; 642 643 index++; 644 if (index == N) { 645 goto success; 646 } 647 part = parts[index]; 648 } else { 649 //printf("not navigation: %s\n", part.string()); 650 } 651 652 if (getScreenSizeName(part.string())) { 653 size = part; 654 655 index++; 656 if (index == N) { 657 goto success; 658 } 659 part = parts[index]; 660 } else { 661 //printf("not screen size: %s\n", part.string()); 662 } 663 664 if (getVersionName(part.string())) { 665 vers = part; 666 667 index++; 668 if (index == N) { 669 goto success; 670 } 671 part = parts[index]; 672 } else { 673 //printf("not version: %s\n", part.string()); 674 } 675 676 // if there are extra parts, it doesn't match 677 return false; 678 679success: 680 this->mcc = mcc; 681 this->mnc = mnc; 682 this->locale = loc; 683 this->screenLayoutSize = layoutsize; 684 this->screenLayoutLong = layoutlong; 685 this->smallestScreenWidthDp = smallestwidthdp; 686 this->screenWidthDp = widthdp; 687 this->screenHeightDp = heightdp; 688 this->orientation = orient; 689 this->uiModeType = uiModeType; 690 this->uiModeNight = uiModeNight; 691 this->density = den; 692 this->touchscreen = touch; 693 this->keysHidden = keysHidden; 694 this->keyboard = key; 695 this->navHidden = navHidden; 696 this->navigation = nav; 697 this->screenSize = size; 698 this->layoutDirection = layoutDir; 699 this->version = vers; 700 701 // what is this anyway? 702 this->vendor = ""; 703 704 return true; 705} 706 707String8 708AaptGroupEntry::toString() const 709{ 710 String8 s = this->mcc; 711 s += ","; 712 s += this->mnc; 713 s += ","; 714 s += this->locale; 715 s += ","; 716 s += layoutDirection; 717 s += ","; 718 s += smallestScreenWidthDp; 719 s += ","; 720 s += screenWidthDp; 721 s += ","; 722 s += screenHeightDp; 723 s += ","; 724 s += screenLayoutSize; 725 s += ","; 726 s += screenLayoutLong; 727 s += ","; 728 s += this->orientation; 729 s += ","; 730 s += uiModeType; 731 s += ","; 732 s += uiModeNight; 733 s += ","; 734 s += density; 735 s += ","; 736 s += touchscreen; 737 s += ","; 738 s += keysHidden; 739 s += ","; 740 s += keyboard; 741 s += ","; 742 s += navHidden; 743 s += ","; 744 s += navigation; 745 s += ","; 746 s += screenSize; 747 s += ","; 748 s += version; 749 return s; 750} 751 752String8 753AaptGroupEntry::toDirName(const String8& resType) const 754{ 755 String8 s = resType; 756 if (this->mcc != "") { 757 if (s.length() > 0) { 758 s += "-"; 759 } 760 s += mcc; 761 } 762 if (this->mnc != "") { 763 if (s.length() > 0) { 764 s += "-"; 765 } 766 s += mnc; 767 } 768 if (this->locale != "") { 769 if (s.length() > 0) { 770 s += "-"; 771 } 772 s += locale; 773 } 774 if (this->layoutDirection != "") { 775 if (s.length() > 0) { 776 s += "-"; 777 } 778 s += layoutDirection; 779 } 780 if (this->smallestScreenWidthDp != "") { 781 if (s.length() > 0) { 782 s += "-"; 783 } 784 s += smallestScreenWidthDp; 785 } 786 if (this->screenWidthDp != "") { 787 if (s.length() > 0) { 788 s += "-"; 789 } 790 s += screenWidthDp; 791 } 792 if (this->screenHeightDp != "") { 793 if (s.length() > 0) { 794 s += "-"; 795 } 796 s += screenHeightDp; 797 } 798 if (this->screenLayoutSize != "") { 799 if (s.length() > 0) { 800 s += "-"; 801 } 802 s += screenLayoutSize; 803 } 804 if (this->screenLayoutLong != "") { 805 if (s.length() > 0) { 806 s += "-"; 807 } 808 s += screenLayoutLong; 809 } 810 if (this->orientation != "") { 811 if (s.length() > 0) { 812 s += "-"; 813 } 814 s += orientation; 815 } 816 if (this->uiModeType != "") { 817 if (s.length() > 0) { 818 s += "-"; 819 } 820 s += uiModeType; 821 } 822 if (this->uiModeNight != "") { 823 if (s.length() > 0) { 824 s += "-"; 825 } 826 s += uiModeNight; 827 } 828 if (this->density != "") { 829 if (s.length() > 0) { 830 s += "-"; 831 } 832 s += density; 833 } 834 if (this->touchscreen != "") { 835 if (s.length() > 0) { 836 s += "-"; 837 } 838 s += touchscreen; 839 } 840 if (this->keysHidden != "") { 841 if (s.length() > 0) { 842 s += "-"; 843 } 844 s += keysHidden; 845 } 846 if (this->keyboard != "") { 847 if (s.length() > 0) { 848 s += "-"; 849 } 850 s += keyboard; 851 } 852 if (this->navHidden != "") { 853 if (s.length() > 0) { 854 s += "-"; 855 } 856 s += navHidden; 857 } 858 if (this->navigation != "") { 859 if (s.length() > 0) { 860 s += "-"; 861 } 862 s += navigation; 863 } 864 if (this->screenSize != "") { 865 if (s.length() > 0) { 866 s += "-"; 867 } 868 s += screenSize; 869 } 870 if (this->version != "") { 871 if (s.length() > 0) { 872 s += "-"; 873 } 874 s += version; 875 } 876 877 return s; 878} 879 880bool AaptGroupEntry::getMccName(const char* name, 881 ResTable_config* out) 882{ 883 if (strcmp(name, kWildcardName) == 0) { 884 if (out) out->mcc = 0; 885 return true; 886 } 887 const char* c = name; 888 if (tolower(*c) != 'm') return false; 889 c++; 890 if (tolower(*c) != 'c') return false; 891 c++; 892 if (tolower(*c) != 'c') return false; 893 c++; 894 895 const char* val = c; 896 897 while (*c >= '0' && *c <= '9') { 898 c++; 899 } 900 if (*c != 0) return false; 901 if (c-val != 3) return false; 902 903 int d = atoi(val); 904 if (d != 0) { 905 if (out) out->mcc = d; 906 return true; 907 } 908 909 return false; 910} 911 912bool AaptGroupEntry::getMncName(const char* name, 913 ResTable_config* out) 914{ 915 if (strcmp(name, kWildcardName) == 0) { 916 if (out) out->mcc = 0; 917 return true; 918 } 919 const char* c = name; 920 if (tolower(*c) != 'm') return false; 921 c++; 922 if (tolower(*c) != 'n') return false; 923 c++; 924 if (tolower(*c) != 'c') return false; 925 c++; 926 927 const char* val = c; 928 929 while (*c >= '0' && *c <= '9') { 930 c++; 931 } 932 if (*c != 0) return false; 933 if (c-val == 0 || c-val > 3) return false; 934 935 if (out) { 936 out->mnc = atoi(val); 937 if (out->mnc == 0) { 938 out->mnc = ACONFIGURATION_MNC_ZERO; 939 } 940 } 941 942 return true; 943} 944 945/* 946 * Does this directory name fit the pattern of a locale dir ("en-rUS" or 947 * "default")? 948 * 949 * TODO: Should insist that the first two letters are lower case, and the 950 * second two are upper. 951 */ 952bool AaptGroupEntry::getLocaleName(const char* fileName, 953 ResTable_config* out) 954{ 955 if (strcmp(fileName, kWildcardName) == 0 956 || strcmp(fileName, kDefaultLocale) == 0) { 957 if (out) { 958 out->language[0] = 0; 959 out->language[1] = 0; 960 out->country[0] = 0; 961 out->country[1] = 0; 962 } 963 return true; 964 } 965 966 if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) { 967 if (out) { 968 out->language[0] = fileName[0]; 969 out->language[1] = fileName[1]; 970 out->country[0] = 0; 971 out->country[1] = 0; 972 } 973 return true; 974 } 975 976 if (strlen(fileName) == 5 && 977 isalpha(fileName[0]) && 978 isalpha(fileName[1]) && 979 fileName[2] == '-' && 980 isalpha(fileName[3]) && 981 isalpha(fileName[4])) { 982 if (out) { 983 out->language[0] = fileName[0]; 984 out->language[1] = fileName[1]; 985 out->country[0] = fileName[3]; 986 out->country[1] = fileName[4]; 987 } 988 return true; 989 } 990 991 return false; 992} 993 994bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out) 995{ 996 if (strcmp(name, kWildcardName) == 0) { 997 if (out) out->screenLayout = 998 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) 999 | ResTable_config::LAYOUTDIR_ANY; 1000 return true; 1001 } else if (strcmp(name, "ldltr") == 0) { 1002 if (out) out->screenLayout = 1003 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) 1004 | ResTable_config::LAYOUTDIR_LTR; 1005 return true; 1006 } else if (strcmp(name, "ldrtl") == 0) { 1007 if (out) out->screenLayout = 1008 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) 1009 | ResTable_config::LAYOUTDIR_RTL; 1010 return true; 1011 } 1012 1013 return false; 1014} 1015 1016bool AaptGroupEntry::getScreenLayoutSizeName(const char* name, 1017 ResTable_config* out) 1018{ 1019 if (strcmp(name, kWildcardName) == 0) { 1020 if (out) out->screenLayout = 1021 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 1022 | ResTable_config::SCREENSIZE_ANY; 1023 return true; 1024 } else if (strcmp(name, "small") == 0) { 1025 if (out) out->screenLayout = 1026 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 1027 | ResTable_config::SCREENSIZE_SMALL; 1028 return true; 1029 } else if (strcmp(name, "normal") == 0) { 1030 if (out) out->screenLayout = 1031 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 1032 | ResTable_config::SCREENSIZE_NORMAL; 1033 return true; 1034 } else if (strcmp(name, "large") == 0) { 1035 if (out) out->screenLayout = 1036 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 1037 | ResTable_config::SCREENSIZE_LARGE; 1038 return true; 1039 } else if (strcmp(name, "xlarge") == 0) { 1040 if (out) out->screenLayout = 1041 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 1042 | ResTable_config::SCREENSIZE_XLARGE; 1043 return true; 1044 } 1045 1046 return false; 1047} 1048 1049bool AaptGroupEntry::getScreenLayoutLongName(const char* name, 1050 ResTable_config* out) 1051{ 1052 if (strcmp(name, kWildcardName) == 0) { 1053 if (out) out->screenLayout = 1054 (out->screenLayout&~ResTable_config::MASK_SCREENLONG) 1055 | ResTable_config::SCREENLONG_ANY; 1056 return true; 1057 } else if (strcmp(name, "long") == 0) { 1058 if (out) out->screenLayout = 1059 (out->screenLayout&~ResTable_config::MASK_SCREENLONG) 1060 | ResTable_config::SCREENLONG_YES; 1061 return true; 1062 } else if (strcmp(name, "notlong") == 0) { 1063 if (out) out->screenLayout = 1064 (out->screenLayout&~ResTable_config::MASK_SCREENLONG) 1065 | ResTable_config::SCREENLONG_NO; 1066 return true; 1067 } 1068 1069 return false; 1070} 1071 1072bool AaptGroupEntry::getOrientationName(const char* name, 1073 ResTable_config* out) 1074{ 1075 if (strcmp(name, kWildcardName) == 0) { 1076 if (out) out->orientation = out->ORIENTATION_ANY; 1077 return true; 1078 } else if (strcmp(name, "port") == 0) { 1079 if (out) out->orientation = out->ORIENTATION_PORT; 1080 return true; 1081 } else if (strcmp(name, "land") == 0) { 1082 if (out) out->orientation = out->ORIENTATION_LAND; 1083 return true; 1084 } else if (strcmp(name, "square") == 0) { 1085 if (out) out->orientation = out->ORIENTATION_SQUARE; 1086 return true; 1087 } 1088 1089 return false; 1090} 1091 1092bool AaptGroupEntry::getUiModeTypeName(const char* name, 1093 ResTable_config* out) 1094{ 1095 if (strcmp(name, kWildcardName) == 0) { 1096 if (out) out->uiMode = 1097 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 1098 | ResTable_config::UI_MODE_TYPE_ANY; 1099 return true; 1100 } else if (strcmp(name, "desk") == 0) { 1101 if (out) out->uiMode = 1102 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 1103 | ResTable_config::UI_MODE_TYPE_DESK; 1104 return true; 1105 } else if (strcmp(name, "car") == 0) { 1106 if (out) out->uiMode = 1107 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 1108 | ResTable_config::UI_MODE_TYPE_CAR; 1109 return true; 1110 } else if (strcmp(name, "television") == 0) { 1111 if (out) out->uiMode = 1112 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 1113 | ResTable_config::UI_MODE_TYPE_TELEVISION; 1114 return true; 1115 } else if (strcmp(name, "appliance") == 0) { 1116 if (out) out->uiMode = 1117 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 1118 | ResTable_config::UI_MODE_TYPE_APPLIANCE; 1119 return true; 1120 } else if (strcmp(name, "watch") == 0) { 1121 if (out) out->uiMode = 1122 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 1123 | ResTable_config::UI_MODE_TYPE_WATCH; 1124 return true; 1125 } 1126 1127 return false; 1128} 1129 1130bool AaptGroupEntry::getUiModeNightName(const char* name, 1131 ResTable_config* out) 1132{ 1133 if (strcmp(name, kWildcardName) == 0) { 1134 if (out) out->uiMode = 1135 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) 1136 | ResTable_config::UI_MODE_NIGHT_ANY; 1137 return true; 1138 } else if (strcmp(name, "night") == 0) { 1139 if (out) out->uiMode = 1140 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) 1141 | ResTable_config::UI_MODE_NIGHT_YES; 1142 return true; 1143 } else if (strcmp(name, "notnight") == 0) { 1144 if (out) out->uiMode = 1145 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) 1146 | ResTable_config::UI_MODE_NIGHT_NO; 1147 return true; 1148 } 1149 1150 return false; 1151} 1152 1153bool AaptGroupEntry::getDensityName(const char* name, 1154 ResTable_config* out) 1155{ 1156 if (strcmp(name, kWildcardName) == 0) { 1157 if (out) out->density = ResTable_config::DENSITY_DEFAULT; 1158 return true; 1159 } 1160 1161 if (strcmp(name, "nodpi") == 0) { 1162 if (out) out->density = ResTable_config::DENSITY_NONE; 1163 return true; 1164 } 1165 1166 if (strcmp(name, "ldpi") == 0) { 1167 if (out) out->density = ResTable_config::DENSITY_LOW; 1168 return true; 1169 } 1170 1171 if (strcmp(name, "mdpi") == 0) { 1172 if (out) out->density = ResTable_config::DENSITY_MEDIUM; 1173 return true; 1174 } 1175 1176 if (strcmp(name, "tvdpi") == 0) { 1177 if (out) out->density = ResTable_config::DENSITY_TV; 1178 return true; 1179 } 1180 1181 if (strcmp(name, "hdpi") == 0) { 1182 if (out) out->density = ResTable_config::DENSITY_HIGH; 1183 return true; 1184 } 1185 1186 if (strcmp(name, "xhdpi") == 0) { 1187 if (out) out->density = ResTable_config::DENSITY_XHIGH; 1188 return true; 1189 } 1190 1191 if (strcmp(name, "xxhdpi") == 0) { 1192 if (out) out->density = ResTable_config::DENSITY_XXHIGH; 1193 return true; 1194 } 1195 1196 if (strcmp(name, "xxxhdpi") == 0) { 1197 if (out) out->density = ResTable_config::DENSITY_XXXHIGH; 1198 return true; 1199 } 1200 1201 char* c = (char*)name; 1202 while (*c >= '0' && *c <= '9') { 1203 c++; 1204 } 1205 1206 // check that we have 'dpi' after the last digit. 1207 if (toupper(c[0]) != 'D' || 1208 toupper(c[1]) != 'P' || 1209 toupper(c[2]) != 'I' || 1210 c[3] != 0) { 1211 return false; 1212 } 1213 1214 // temporarily replace the first letter with \0 to 1215 // use atoi. 1216 char tmp = c[0]; 1217 c[0] = '\0'; 1218 1219 int d = atoi(name); 1220 c[0] = tmp; 1221 1222 if (d != 0) { 1223 if (out) out->density = d; 1224 return true; 1225 } 1226 1227 return false; 1228} 1229 1230bool AaptGroupEntry::getTouchscreenName(const char* name, 1231 ResTable_config* out) 1232{ 1233 if (strcmp(name, kWildcardName) == 0) { 1234 if (out) out->touchscreen = out->TOUCHSCREEN_ANY; 1235 return true; 1236 } else if (strcmp(name, "notouch") == 0) { 1237 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH; 1238 return true; 1239 } else if (strcmp(name, "stylus") == 0) { 1240 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS; 1241 return true; 1242 } else if (strcmp(name, "finger") == 0) { 1243 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER; 1244 return true; 1245 } 1246 1247 return false; 1248} 1249 1250bool AaptGroupEntry::getKeysHiddenName(const char* name, 1251 ResTable_config* out) 1252{ 1253 uint8_t mask = 0; 1254 uint8_t value = 0; 1255 if (strcmp(name, kWildcardName) == 0) { 1256 mask = ResTable_config::MASK_KEYSHIDDEN; 1257 value = ResTable_config::KEYSHIDDEN_ANY; 1258 } else if (strcmp(name, "keysexposed") == 0) { 1259 mask = ResTable_config::MASK_KEYSHIDDEN; 1260 value = ResTable_config::KEYSHIDDEN_NO; 1261 } else if (strcmp(name, "keyshidden") == 0) { 1262 mask = ResTable_config::MASK_KEYSHIDDEN; 1263 value = ResTable_config::KEYSHIDDEN_YES; 1264 } else if (strcmp(name, "keyssoft") == 0) { 1265 mask = ResTable_config::MASK_KEYSHIDDEN; 1266 value = ResTable_config::KEYSHIDDEN_SOFT; 1267 } 1268 1269 if (mask != 0) { 1270 if (out) out->inputFlags = (out->inputFlags&~mask) | value; 1271 return true; 1272 } 1273 1274 return false; 1275} 1276 1277bool AaptGroupEntry::getKeyboardName(const char* name, 1278 ResTable_config* out) 1279{ 1280 if (strcmp(name, kWildcardName) == 0) { 1281 if (out) out->keyboard = out->KEYBOARD_ANY; 1282 return true; 1283 } else if (strcmp(name, "nokeys") == 0) { 1284 if (out) out->keyboard = out->KEYBOARD_NOKEYS; 1285 return true; 1286 } else if (strcmp(name, "qwerty") == 0) { 1287 if (out) out->keyboard = out->KEYBOARD_QWERTY; 1288 return true; 1289 } else if (strcmp(name, "12key") == 0) { 1290 if (out) out->keyboard = out->KEYBOARD_12KEY; 1291 return true; 1292 } 1293 1294 return false; 1295} 1296 1297bool AaptGroupEntry::getNavHiddenName(const char* name, 1298 ResTable_config* out) 1299{ 1300 uint8_t mask = 0; 1301 uint8_t value = 0; 1302 if (strcmp(name, kWildcardName) == 0) { 1303 mask = ResTable_config::MASK_NAVHIDDEN; 1304 value = ResTable_config::NAVHIDDEN_ANY; 1305 } else if (strcmp(name, "navexposed") == 0) { 1306 mask = ResTable_config::MASK_NAVHIDDEN; 1307 value = ResTable_config::NAVHIDDEN_NO; 1308 } else if (strcmp(name, "navhidden") == 0) { 1309 mask = ResTable_config::MASK_NAVHIDDEN; 1310 value = ResTable_config::NAVHIDDEN_YES; 1311 } 1312 1313 if (mask != 0) { 1314 if (out) out->inputFlags = (out->inputFlags&~mask) | value; 1315 return true; 1316 } 1317 1318 return false; 1319} 1320 1321bool AaptGroupEntry::getNavigationName(const char* name, 1322 ResTable_config* out) 1323{ 1324 if (strcmp(name, kWildcardName) == 0) { 1325 if (out) out->navigation = out->NAVIGATION_ANY; 1326 return true; 1327 } else if (strcmp(name, "nonav") == 0) { 1328 if (out) out->navigation = out->NAVIGATION_NONAV; 1329 return true; 1330 } else if (strcmp(name, "dpad") == 0) { 1331 if (out) out->navigation = out->NAVIGATION_DPAD; 1332 return true; 1333 } else if (strcmp(name, "trackball") == 0) { 1334 if (out) out->navigation = out->NAVIGATION_TRACKBALL; 1335 return true; 1336 } else if (strcmp(name, "wheel") == 0) { 1337 if (out) out->navigation = out->NAVIGATION_WHEEL; 1338 return true; 1339 } 1340 1341 return false; 1342} 1343 1344bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out) 1345{ 1346 if (strcmp(name, kWildcardName) == 0) { 1347 if (out) { 1348 out->screenWidth = out->SCREENWIDTH_ANY; 1349 out->screenHeight = out->SCREENHEIGHT_ANY; 1350 } 1351 return true; 1352 } 1353 1354 const char* x = name; 1355 while (*x >= '0' && *x <= '9') x++; 1356 if (x == name || *x != 'x') return false; 1357 String8 xName(name, x-name); 1358 x++; 1359 1360 const char* y = x; 1361 while (*y >= '0' && *y <= '9') y++; 1362 if (y == name || *y != 0) return false; 1363 String8 yName(x, y-x); 1364 1365 uint16_t w = (uint16_t)atoi(xName.string()); 1366 uint16_t h = (uint16_t)atoi(yName.string()); 1367 if (w < h) { 1368 return false; 1369 } 1370 1371 if (out) { 1372 out->screenWidth = w; 1373 out->screenHeight = h; 1374 } 1375 1376 return true; 1377} 1378 1379bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out) 1380{ 1381 if (strcmp(name, kWildcardName) == 0) { 1382 if (out) { 1383 out->smallestScreenWidthDp = out->SCREENWIDTH_ANY; 1384 } 1385 return true; 1386 } 1387 1388 if (*name != 's') return false; 1389 name++; 1390 if (*name != 'w') return false; 1391 name++; 1392 const char* x = name; 1393 while (*x >= '0' && *x <= '9') x++; 1394 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; 1395 String8 xName(name, x-name); 1396 1397 if (out) { 1398 out->smallestScreenWidthDp = (uint16_t)atoi(xName.string()); 1399 } 1400 1401 return true; 1402} 1403 1404bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out) 1405{ 1406 if (strcmp(name, kWildcardName) == 0) { 1407 if (out) { 1408 out->screenWidthDp = out->SCREENWIDTH_ANY; 1409 } 1410 return true; 1411 } 1412 1413 if (*name != 'w') return false; 1414 name++; 1415 const char* x = name; 1416 while (*x >= '0' && *x <= '9') x++; 1417 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; 1418 String8 xName(name, x-name); 1419 1420 if (out) { 1421 out->screenWidthDp = (uint16_t)atoi(xName.string()); 1422 } 1423 1424 return true; 1425} 1426 1427bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out) 1428{ 1429 if (strcmp(name, kWildcardName) == 0) { 1430 if (out) { 1431 out->screenHeightDp = out->SCREENWIDTH_ANY; 1432 } 1433 return true; 1434 } 1435 1436 if (*name != 'h') return false; 1437 name++; 1438 const char* x = name; 1439 while (*x >= '0' && *x <= '9') x++; 1440 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; 1441 String8 xName(name, x-name); 1442 1443 if (out) { 1444 out->screenHeightDp = (uint16_t)atoi(xName.string()); 1445 } 1446 1447 return true; 1448} 1449 1450bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out) 1451{ 1452 if (strcmp(name, kWildcardName) == 0) { 1453 if (out) { 1454 out->sdkVersion = out->SDKVERSION_ANY; 1455 out->minorVersion = out->MINORVERSION_ANY; 1456 } 1457 return true; 1458 } 1459 1460 if (*name != 'v') { 1461 return false; 1462 } 1463 1464 name++; 1465 const char* s = name; 1466 while (*s >= '0' && *s <= '9') s++; 1467 if (s == name || *s != 0) return false; 1468 String8 sdkName(name, s-name); 1469 1470 if (out) { 1471 out->sdkVersion = (uint16_t)atoi(sdkName.string()); 1472 out->minorVersion = 0; 1473 } 1474 1475 return true; 1476} 1477 1478int AaptGroupEntry::compare(const AaptGroupEntry& o) const 1479{ 1480 int v = mcc.compare(o.mcc); 1481 if (v == 0) v = mnc.compare(o.mnc); 1482 if (v == 0) v = locale.compare(o.locale); 1483 if (v == 0) v = layoutDirection.compare(o.layoutDirection); 1484 if (v == 0) v = vendor.compare(o.vendor); 1485 if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp); 1486 if (v == 0) v = screenWidthDp.compare(o.screenWidthDp); 1487 if (v == 0) v = screenHeightDp.compare(o.screenHeightDp); 1488 if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize); 1489 if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong); 1490 if (v == 0) v = orientation.compare(o.orientation); 1491 if (v == 0) v = uiModeType.compare(o.uiModeType); 1492 if (v == 0) v = uiModeNight.compare(o.uiModeNight); 1493 if (v == 0) v = density.compare(o.density); 1494 if (v == 0) v = touchscreen.compare(o.touchscreen); 1495 if (v == 0) v = keysHidden.compare(o.keysHidden); 1496 if (v == 0) v = keyboard.compare(o.keyboard); 1497 if (v == 0) v = navHidden.compare(o.navHidden); 1498 if (v == 0) v = navigation.compare(o.navigation); 1499 if (v == 0) v = screenSize.compare(o.screenSize); 1500 if (v == 0) v = version.compare(o.version); 1501 return v; 1502} 1503 1504const ResTable_config& AaptGroupEntry::toParams() const 1505{ 1506 if (!mParamsChanged) { 1507 return mParams; 1508 } 1509 1510 mParamsChanged = false; 1511 ResTable_config& params(mParams); 1512 memset(¶ms, 0, sizeof(params)); 1513 getMccName(mcc.string(), ¶ms); 1514 getMncName(mnc.string(), ¶ms); 1515 getLocaleName(locale.string(), ¶ms); 1516 getLayoutDirectionName(layoutDirection.string(), ¶ms); 1517 getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), ¶ms); 1518 getScreenWidthDpName(screenWidthDp.string(), ¶ms); 1519 getScreenHeightDpName(screenHeightDp.string(), ¶ms); 1520 getScreenLayoutSizeName(screenLayoutSize.string(), ¶ms); 1521 getScreenLayoutLongName(screenLayoutLong.string(), ¶ms); 1522 getOrientationName(orientation.string(), ¶ms); 1523 getUiModeTypeName(uiModeType.string(), ¶ms); 1524 getUiModeNightName(uiModeNight.string(), ¶ms); 1525 getDensityName(density.string(), ¶ms); 1526 getTouchscreenName(touchscreen.string(), ¶ms); 1527 getKeysHiddenName(keysHidden.string(), ¶ms); 1528 getKeyboardName(keyboard.string(), ¶ms); 1529 getNavHiddenName(navHidden.string(), ¶ms); 1530 getNavigationName(navigation.string(), ¶ms); 1531 getScreenSizeName(screenSize.string(), ¶ms); 1532 getVersionName(version.string(), ¶ms); 1533 1534 // Fix up version number based on specified parameters. 1535 int minSdk = 0; 1536 if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY 1537 || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY 1538 || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) { 1539 minSdk = SDK_HONEYCOMB_MR2; 1540 } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE) 1541 != ResTable_config::UI_MODE_TYPE_ANY 1542 || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT) 1543 != ResTable_config::UI_MODE_NIGHT_ANY) { 1544 minSdk = SDK_FROYO; 1545 } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE) 1546 != ResTable_config::SCREENSIZE_ANY 1547 || (params.screenLayout&ResTable_config::MASK_SCREENLONG) 1548 != ResTable_config::SCREENLONG_ANY 1549 || params.density != ResTable_config::DENSITY_DEFAULT) { 1550 minSdk = SDK_DONUT; 1551 } 1552 1553 if (minSdk > params.sdkVersion) { 1554 params.sdkVersion = minSdk; 1555 } 1556 1557 return params; 1558} 1559 1560// ========================================================================= 1561// ========================================================================= 1562// ========================================================================= 1563 1564void* AaptFile::editData(size_t size) 1565{ 1566 if (size <= mBufferSize) { 1567 mDataSize = size; 1568 return mData; 1569 } 1570 size_t allocSize = (size*3)/2; 1571 void* buf = realloc(mData, allocSize); 1572 if (buf == NULL) { 1573 return NULL; 1574 } 1575 mData = buf; 1576 mDataSize = size; 1577 mBufferSize = allocSize; 1578 return buf; 1579} 1580 1581void* AaptFile::editData(size_t* outSize) 1582{ 1583 if (outSize) { 1584 *outSize = mDataSize; 1585 } 1586 return mData; 1587} 1588 1589void* AaptFile::padData(size_t wordSize) 1590{ 1591 const size_t extra = mDataSize%wordSize; 1592 if (extra == 0) { 1593 return mData; 1594 } 1595 1596 size_t initial = mDataSize; 1597 void* data = editData(initial+(wordSize-extra)); 1598 if (data != NULL) { 1599 memset(((uint8_t*)data) + initial, 0, wordSize-extra); 1600 } 1601 return data; 1602} 1603 1604status_t AaptFile::writeData(const void* data, size_t size) 1605{ 1606 size_t end = mDataSize; 1607 size_t total = size + end; 1608 void* buf = editData(total); 1609 if (buf == NULL) { 1610 return UNKNOWN_ERROR; 1611 } 1612 memcpy(((char*)buf)+end, data, size); 1613 return NO_ERROR; 1614} 1615 1616void AaptFile::clearData() 1617{ 1618 if (mData != NULL) free(mData); 1619 mData = NULL; 1620 mDataSize = 0; 1621 mBufferSize = 0; 1622} 1623 1624String8 AaptFile::getPrintableSource() const 1625{ 1626 if (hasData()) { 1627 String8 name(mGroupEntry.toDirName(String8())); 1628 name.appendPath(mPath); 1629 name.append(" #generated"); 1630 return name; 1631 } 1632 return mSourceFile; 1633} 1634 1635// ========================================================================= 1636// ========================================================================= 1637// ========================================================================= 1638 1639status_t AaptGroup::addFile(const sp<AaptFile>& file, const bool overwriteDuplicate) 1640{ 1641 ssize_t index = mFiles.indexOfKey(file->getGroupEntry()); 1642 if (index >= 0 && overwriteDuplicate) { 1643 fprintf(stderr, "warning: overwriting '%s' with '%s'\n", 1644 mFiles[index]->getSourceFile().string(), 1645 file->getSourceFile().string()); 1646 removeFile(index); 1647 index = -1; 1648 } 1649 1650 if (index < 0) { 1651 file->mPath = mPath; 1652 mFiles.add(file->getGroupEntry(), file); 1653 return NO_ERROR; 1654 } 1655 1656#if 0 1657 printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n", 1658 file->getSourceFile().string(), 1659 file->getGroupEntry().toDirName(String8()).string(), 1660 mLeaf.string(), mPath.string()); 1661#endif 1662 1663 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.", 1664 getPrintableSource().string()); 1665 return UNKNOWN_ERROR; 1666} 1667 1668void AaptGroup::removeFile(size_t index) 1669{ 1670 mFiles.removeItemsAt(index); 1671} 1672 1673void AaptGroup::print(const String8& prefix) const 1674{ 1675 printf("%s%s\n", prefix.string(), getPath().string()); 1676 const size_t N=mFiles.size(); 1677 size_t i; 1678 for (i=0; i<N; i++) { 1679 sp<AaptFile> file = mFiles.valueAt(i); 1680 const AaptGroupEntry& e = file->getGroupEntry(); 1681 if (file->hasData()) { 1682 printf("%s Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(), 1683 (int)file->getSize()); 1684 } else { 1685 printf("%s Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(), 1686 file->getPrintableSource().string()); 1687 } 1688 //printf("%s File Group Entry: %s\n", prefix.string(), 1689 // file->getGroupEntry().toDirName(String8()).string()); 1690 } 1691} 1692 1693String8 AaptGroup::getPrintableSource() const 1694{ 1695 if (mFiles.size() > 0) { 1696 // Arbitrarily pull the first source file out of the list. 1697 return mFiles.valueAt(0)->getPrintableSource(); 1698 } 1699 1700 // Should never hit this case, but to be safe... 1701 return getPath(); 1702 1703} 1704 1705// ========================================================================= 1706// ========================================================================= 1707// ========================================================================= 1708 1709status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file) 1710{ 1711 if (mFiles.indexOfKey(name) >= 0) { 1712 return ALREADY_EXISTS; 1713 } 1714 mFiles.add(name, file); 1715 return NO_ERROR; 1716} 1717 1718status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir) 1719{ 1720 if (mDirs.indexOfKey(name) >= 0) { 1721 return ALREADY_EXISTS; 1722 } 1723 mDirs.add(name, dir); 1724 return NO_ERROR; 1725} 1726 1727sp<AaptDir> AaptDir::makeDir(const String8& path) 1728{ 1729 String8 name; 1730 String8 remain = path; 1731 1732 sp<AaptDir> subdir = this; 1733 while (name = remain.walkPath(&remain), remain != "") { 1734 subdir = subdir->makeDir(name); 1735 } 1736 1737 ssize_t i = subdir->mDirs.indexOfKey(name); 1738 if (i >= 0) { 1739 return subdir->mDirs.valueAt(i); 1740 } 1741 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name)); 1742 subdir->mDirs.add(name, dir); 1743 return dir; 1744} 1745 1746void AaptDir::removeFile(const String8& name) 1747{ 1748 mFiles.removeItem(name); 1749} 1750 1751void AaptDir::removeDir(const String8& name) 1752{ 1753 mDirs.removeItem(name); 1754} 1755 1756status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file, 1757 const bool overwrite) 1758{ 1759 sp<AaptGroup> group; 1760 if (mFiles.indexOfKey(leafName) >= 0) { 1761 group = mFiles.valueFor(leafName); 1762 } else { 1763 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName)); 1764 mFiles.add(leafName, group); 1765 } 1766 1767 return group->addFile(file, overwrite); 1768} 1769 1770ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir, 1771 const AaptGroupEntry& kind, const String8& resType, 1772 sp<FilePathStore>& fullResPaths, const bool overwrite) 1773{ 1774 Vector<String8> fileNames; 1775 { 1776 DIR* dir = NULL; 1777 1778 dir = opendir(srcDir.string()); 1779 if (dir == NULL) { 1780 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno)); 1781 return UNKNOWN_ERROR; 1782 } 1783 1784 /* 1785 * Slurp the filenames out of the directory. 1786 */ 1787 while (1) { 1788 struct dirent* entry; 1789 1790 entry = readdir(dir); 1791 if (entry == NULL) 1792 break; 1793 1794 if (isHidden(srcDir.string(), entry->d_name)) 1795 continue; 1796 1797 String8 name(entry->d_name); 1798 fileNames.add(name); 1799 // Add fully qualified path for dependency purposes 1800 // if we're collecting them 1801 if (fullResPaths != NULL) { 1802 fullResPaths->add(srcDir.appendPathCopy(name)); 1803 } 1804 } 1805 closedir(dir); 1806 } 1807 1808 ssize_t count = 0; 1809 1810 /* 1811 * Stash away the files and recursively descend into subdirectories. 1812 */ 1813 const size_t N = fileNames.size(); 1814 size_t i; 1815 for (i = 0; i < N; i++) { 1816 String8 pathName(srcDir); 1817 FileType type; 1818 1819 pathName.appendPath(fileNames[i].string()); 1820 type = getFileType(pathName.string()); 1821 if (type == kFileTypeDirectory) { 1822 sp<AaptDir> subdir; 1823 bool notAdded = false; 1824 if (mDirs.indexOfKey(fileNames[i]) >= 0) { 1825 subdir = mDirs.valueFor(fileNames[i]); 1826 } else { 1827 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i])); 1828 notAdded = true; 1829 } 1830 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind, 1831 resType, fullResPaths, overwrite); 1832 if (res < NO_ERROR) { 1833 return res; 1834 } 1835 if (res > 0 && notAdded) { 1836 mDirs.add(fileNames[i], subdir); 1837 } 1838 count += res; 1839 } else if (type == kFileTypeRegular) { 1840 sp<AaptFile> file = new AaptFile(pathName, kind, resType); 1841 status_t err = addLeafFile(fileNames[i], file, overwrite); 1842 if (err != NO_ERROR) { 1843 return err; 1844 } 1845 1846 count++; 1847 1848 } else { 1849 if (bundle->getVerbose()) 1850 printf(" (ignoring non-file/dir '%s')\n", pathName.string()); 1851 } 1852 } 1853 1854 return count; 1855} 1856 1857status_t AaptDir::validate() const 1858{ 1859 const size_t NF = mFiles.size(); 1860 const size_t ND = mDirs.size(); 1861 size_t i; 1862 for (i = 0; i < NF; i++) { 1863 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) { 1864 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( 1865 "Invalid filename. Unable to add."); 1866 return UNKNOWN_ERROR; 1867 } 1868 1869 size_t j; 1870 for (j = i+1; j < NF; j++) { 1871 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(), 1872 mFiles.valueAt(j)->getLeaf().string()) == 0) { 1873 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( 1874 "File is case-insensitive equivalent to: %s", 1875 mFiles.valueAt(j)->getPrintableSource().string()); 1876 return UNKNOWN_ERROR; 1877 } 1878 1879 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz" 1880 // (this is mostly caught by the "marked" stuff, below) 1881 } 1882 1883 for (j = 0; j < ND; j++) { 1884 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(), 1885 mDirs.valueAt(j)->getLeaf().string()) == 0) { 1886 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( 1887 "File conflicts with dir from: %s", 1888 mDirs.valueAt(j)->getPrintableSource().string()); 1889 return UNKNOWN_ERROR; 1890 } 1891 } 1892 } 1893 1894 for (i = 0; i < ND; i++) { 1895 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) { 1896 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error( 1897 "Invalid directory name, unable to add."); 1898 return UNKNOWN_ERROR; 1899 } 1900 1901 size_t j; 1902 for (j = i+1; j < ND; j++) { 1903 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(), 1904 mDirs.valueAt(j)->getLeaf().string()) == 0) { 1905 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error( 1906 "Directory is case-insensitive equivalent to: %s", 1907 mDirs.valueAt(j)->getPrintableSource().string()); 1908 return UNKNOWN_ERROR; 1909 } 1910 } 1911 1912 status_t err = mDirs.valueAt(i)->validate(); 1913 if (err != NO_ERROR) { 1914 return err; 1915 } 1916 } 1917 1918 return NO_ERROR; 1919} 1920 1921void AaptDir::print(const String8& prefix) const 1922{ 1923 const size_t ND=getDirs().size(); 1924 size_t i; 1925 for (i=0; i<ND; i++) { 1926 getDirs().valueAt(i)->print(prefix); 1927 } 1928 1929 const size_t NF=getFiles().size(); 1930 for (i=0; i<NF; i++) { 1931 getFiles().valueAt(i)->print(prefix); 1932 } 1933} 1934 1935String8 AaptDir::getPrintableSource() const 1936{ 1937 if (mFiles.size() > 0) { 1938 // Arbitrarily pull the first file out of the list as the source dir. 1939 return mFiles.valueAt(0)->getPrintableSource().getPathDir(); 1940 } 1941 if (mDirs.size() > 0) { 1942 // Or arbitrarily pull the first dir out of the list as the source dir. 1943 return mDirs.valueAt(0)->getPrintableSource().getPathDir(); 1944 } 1945 1946 // Should never hit this case, but to be safe... 1947 return mPath; 1948 1949} 1950 1951// ========================================================================= 1952// ========================================================================= 1953// ========================================================================= 1954 1955status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols) 1956{ 1957 status_t err = NO_ERROR; 1958 size_t N = javaSymbols->mSymbols.size(); 1959 for (size_t i=0; i<N; i++) { 1960 const String8& name = javaSymbols->mSymbols.keyAt(i); 1961 const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i); 1962 ssize_t pos = mSymbols.indexOfKey(name); 1963 if (pos < 0) { 1964 entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string()); 1965 err = UNKNOWN_ERROR; 1966 continue; 1967 } 1968 //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n", 1969 // i, N, name.string(), entry.isJavaSymbol ? 1 : 0); 1970 mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol; 1971 } 1972 1973 N = javaSymbols->mNestedSymbols.size(); 1974 for (size_t i=0; i<N; i++) { 1975 const String8& name = javaSymbols->mNestedSymbols.keyAt(i); 1976 const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i); 1977 ssize_t pos = mNestedSymbols.indexOfKey(name); 1978 if (pos < 0) { 1979 SourcePos pos; 1980 pos.error("Java symbol dir %s not defined\n", name.string()); 1981 err = UNKNOWN_ERROR; 1982 continue; 1983 } 1984 //printf("**** applying java symbols in dir %s\n", name.string()); 1985 status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols); 1986 if (myerr != NO_ERROR) { 1987 err = myerr; 1988 } 1989 } 1990 1991 return err; 1992} 1993 1994// ========================================================================= 1995// ========================================================================= 1996// ========================================================================= 1997 1998AaptAssets::AaptAssets() 1999 : AaptDir(String8(), String8()), 2000 mChanged(false), mHaveIncludedAssets(false), mRes(NULL) 2001{ 2002} 2003 2004const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const { 2005 if (mChanged) { 2006 } 2007 return mGroupEntries; 2008} 2009 2010status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file) 2011{ 2012 mChanged = true; 2013 return AaptDir::addFile(name, file); 2014} 2015 2016sp<AaptFile> AaptAssets::addFile( 2017 const String8& filePath, const AaptGroupEntry& entry, 2018 const String8& srcDir, sp<AaptGroup>* outGroup, 2019 const String8& resType) 2020{ 2021 sp<AaptDir> dir = this; 2022 sp<AaptGroup> group; 2023 sp<AaptFile> file; 2024 String8 root, remain(filePath), partialPath; 2025 while (remain.length() > 0) { 2026 root = remain.walkPath(&remain); 2027 partialPath.appendPath(root); 2028 2029 const String8 rootStr(root); 2030 2031 if (remain.length() == 0) { 2032 ssize_t i = dir->getFiles().indexOfKey(rootStr); 2033 if (i >= 0) { 2034 group = dir->getFiles().valueAt(i); 2035 } else { 2036 group = new AaptGroup(rootStr, filePath); 2037 status_t res = dir->addFile(rootStr, group); 2038 if (res != NO_ERROR) { 2039 return NULL; 2040 } 2041 } 2042 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType); 2043 status_t res = group->addFile(file); 2044 if (res != NO_ERROR) { 2045 return NULL; 2046 } 2047 break; 2048 2049 } else { 2050 ssize_t i = dir->getDirs().indexOfKey(rootStr); 2051 if (i >= 0) { 2052 dir = dir->getDirs().valueAt(i); 2053 } else { 2054 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath); 2055 status_t res = dir->addDir(rootStr, subdir); 2056 if (res != NO_ERROR) { 2057 return NULL; 2058 } 2059 dir = subdir; 2060 } 2061 } 2062 } 2063 2064 mGroupEntries.add(entry); 2065 if (outGroup) *outGroup = group; 2066 return file; 2067} 2068 2069void AaptAssets::addResource(const String8& leafName, const String8& path, 2070 const sp<AaptFile>& file, const String8& resType) 2071{ 2072 sp<AaptDir> res = AaptDir::makeDir(kResString); 2073 String8 dirname = file->getGroupEntry().toDirName(resType); 2074 sp<AaptDir> subdir = res->makeDir(dirname); 2075 sp<AaptGroup> grr = new AaptGroup(leafName, path); 2076 grr->addFile(file); 2077 2078 subdir->addFile(leafName, grr); 2079} 2080 2081 2082ssize_t AaptAssets::slurpFromArgs(Bundle* bundle) 2083{ 2084 int count; 2085 int totalCount = 0; 2086 FileType type; 2087 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs(); 2088 const size_t dirCount =resDirs.size(); 2089 sp<AaptAssets> current = this; 2090 2091 const int N = bundle->getFileSpecCount(); 2092 2093 /* 2094 * If a package manifest was specified, include that first. 2095 */ 2096 if (bundle->getAndroidManifestFile() != NULL) { 2097 // place at root of zip. 2098 String8 srcFile(bundle->getAndroidManifestFile()); 2099 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(), 2100 NULL, String8()); 2101 totalCount++; 2102 } 2103 2104 /* 2105 * If a directory of custom assets was supplied, slurp 'em up. 2106 */ 2107 const Vector<const char*>& assetDirs = bundle->getAssetSourceDirs(); 2108 const int AN = assetDirs.size(); 2109 for (int i = 0; i < AN; i++) { 2110 FileType type = getFileType(assetDirs[i]); 2111 if (type == kFileTypeNonexistent) { 2112 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDirs[i]); 2113 return UNKNOWN_ERROR; 2114 } 2115 if (type != kFileTypeDirectory) { 2116 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDirs[i]); 2117 return UNKNOWN_ERROR; 2118 } 2119 2120 String8 assetRoot(assetDirs[i]); 2121 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir)); 2122 AaptGroupEntry group; 2123 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group, 2124 String8(), mFullAssetPaths, true); 2125 if (count < 0) { 2126 totalCount = count; 2127 goto bail; 2128 } 2129 if (count > 0) { 2130 mGroupEntries.add(group); 2131 } 2132 totalCount += count; 2133 2134 if (bundle->getVerbose()) { 2135 printf("Found %d custom asset file%s in %s\n", 2136 count, (count==1) ? "" : "s", assetDirs[i]); 2137 } 2138 } 2139 2140 /* 2141 * If a directory of resource-specific assets was supplied, slurp 'em up. 2142 */ 2143 for (size_t i=0; i<dirCount; i++) { 2144 const char *res = resDirs[i]; 2145 if (res) { 2146 type = getFileType(res); 2147 if (type == kFileTypeNonexistent) { 2148 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res); 2149 return UNKNOWN_ERROR; 2150 } 2151 if (type == kFileTypeDirectory) { 2152 if (i>0) { 2153 sp<AaptAssets> nextOverlay = new AaptAssets(); 2154 current->setOverlay(nextOverlay); 2155 current = nextOverlay; 2156 current->setFullResPaths(mFullResPaths); 2157 } 2158 count = current->slurpResourceTree(bundle, String8(res)); 2159 2160 if (count < 0) { 2161 totalCount = count; 2162 goto bail; 2163 } 2164 totalCount += count; 2165 } 2166 else { 2167 fprintf(stderr, "ERROR: '%s' is not a directory\n", res); 2168 return UNKNOWN_ERROR; 2169 } 2170 } 2171 2172 } 2173 /* 2174 * Now do any additional raw files. 2175 */ 2176 for (int arg=0; arg<N; arg++) { 2177 const char* assetDir = bundle->getFileSpecEntry(arg); 2178 2179 FileType type = getFileType(assetDir); 2180 if (type == kFileTypeNonexistent) { 2181 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir); 2182 return UNKNOWN_ERROR; 2183 } 2184 if (type != kFileTypeDirectory) { 2185 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir); 2186 return UNKNOWN_ERROR; 2187 } 2188 2189 String8 assetRoot(assetDir); 2190 2191 if (bundle->getVerbose()) 2192 printf("Processing raw dir '%s'\n", (const char*) assetDir); 2193 2194 /* 2195 * Do a recursive traversal of subdir tree. We don't make any 2196 * guarantees about ordering, so we're okay with an inorder search 2197 * using whatever order the OS happens to hand back to us. 2198 */ 2199 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths); 2200 if (count < 0) { 2201 /* failure; report error and remove archive */ 2202 totalCount = count; 2203 goto bail; 2204 } 2205 totalCount += count; 2206 2207 if (bundle->getVerbose()) 2208 printf("Found %d asset file%s in %s\n", 2209 count, (count==1) ? "" : "s", assetDir); 2210 } 2211 2212 count = validate(); 2213 if (count != NO_ERROR) { 2214 totalCount = count; 2215 goto bail; 2216 } 2217 2218 count = filter(bundle); 2219 if (count != NO_ERROR) { 2220 totalCount = count; 2221 goto bail; 2222 } 2223 2224bail: 2225 return totalCount; 2226} 2227 2228ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir, 2229 const AaptGroupEntry& kind, 2230 const String8& resType, 2231 sp<FilePathStore>& fullResPaths) 2232{ 2233 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths); 2234 if (res > 0) { 2235 mGroupEntries.add(kind); 2236 } 2237 2238 return res; 2239} 2240 2241ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir) 2242{ 2243 ssize_t err = 0; 2244 2245 DIR* dir = opendir(srcDir.string()); 2246 if (dir == NULL) { 2247 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno)); 2248 return UNKNOWN_ERROR; 2249 } 2250 2251 status_t count = 0; 2252 2253 /* 2254 * Run through the directory, looking for dirs that match the 2255 * expected pattern. 2256 */ 2257 while (1) { 2258 struct dirent* entry = readdir(dir); 2259 if (entry == NULL) { 2260 break; 2261 } 2262 2263 if (isHidden(srcDir.string(), entry->d_name)) { 2264 continue; 2265 } 2266 2267 String8 subdirName(srcDir); 2268 subdirName.appendPath(entry->d_name); 2269 2270 AaptGroupEntry group; 2271 String8 resType; 2272 bool b = group.initFromDirName(entry->d_name, &resType); 2273 if (!b) { 2274 fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(), 2275 entry->d_name); 2276 err = -1; 2277 continue; 2278 } 2279 2280 if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) { 2281 int maxResInt = atoi(bundle->getMaxResVersion()); 2282 const char *verString = group.getVersionString().string(); 2283 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name 2284 if (dirVersionInt > maxResInt) { 2285 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name); 2286 continue; 2287 } 2288 } 2289 2290 FileType type = getFileType(subdirName.string()); 2291 2292 if (type == kFileTypeDirectory) { 2293 sp<AaptDir> dir = makeDir(resType); 2294 ssize_t res = dir->slurpFullTree(bundle, subdirName, group, 2295 resType, mFullResPaths); 2296 if (res < 0) { 2297 count = res; 2298 goto bail; 2299 } 2300 if (res > 0) { 2301 mGroupEntries.add(group); 2302 count += res; 2303 } 2304 2305 // Only add this directory if we don't already have a resource dir 2306 // for the current type. This ensures that we only add the dir once 2307 // for all configs. 2308 sp<AaptDir> rdir = resDir(resType); 2309 if (rdir == NULL) { 2310 mResDirs.add(dir); 2311 } 2312 } else { 2313 if (bundle->getVerbose()) { 2314 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string()); 2315 } 2316 } 2317 } 2318 2319bail: 2320 closedir(dir); 2321 dir = NULL; 2322 2323 if (err != 0) { 2324 return err; 2325 } 2326 return count; 2327} 2328 2329ssize_t 2330AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename) 2331{ 2332 int count = 0; 2333 SortedVector<AaptGroupEntry> entries; 2334 2335 ZipFile* zip = new ZipFile; 2336 status_t err = zip->open(filename, ZipFile::kOpenReadOnly); 2337 if (err != NO_ERROR) { 2338 fprintf(stderr, "error opening zip file %s\n", filename); 2339 count = err; 2340 delete zip; 2341 return -1; 2342 } 2343 2344 const int N = zip->getNumEntries(); 2345 for (int i=0; i<N; i++) { 2346 ZipEntry* entry = zip->getEntryByIndex(i); 2347 if (entry->getDeleted()) { 2348 continue; 2349 } 2350 2351 String8 entryName(entry->getFileName()); 2352 2353 String8 dirName = entryName.getPathDir(); 2354 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName); 2355 2356 String8 resType; 2357 AaptGroupEntry kind; 2358 2359 String8 remain; 2360 if (entryName.walkPath(&remain) == kResourceDir) { 2361 // these are the resources, pull their type out of the directory name 2362 kind.initFromDirName(remain.walkPath().string(), &resType); 2363 } else { 2364 // these are untyped and don't have an AaptGroupEntry 2365 } 2366 if (entries.indexOf(kind) < 0) { 2367 entries.add(kind); 2368 mGroupEntries.add(kind); 2369 } 2370 2371 // use the one from the zip file if they both exist. 2372 dir->removeFile(entryName.getPathLeaf()); 2373 2374 sp<AaptFile> file = new AaptFile(entryName, kind, resType); 2375 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file); 2376 if (err != NO_ERROR) { 2377 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string()); 2378 count = err; 2379 goto bail; 2380 } 2381 file->setCompressionMethod(entry->getCompressionMethod()); 2382 2383#if 0 2384 if (entryName == "AndroidManifest.xml") { 2385 printf("AndroidManifest.xml\n"); 2386 } 2387 printf("\n\nfile: %s\n", entryName.string()); 2388#endif 2389 2390 size_t len = entry->getUncompressedLen(); 2391 void* data = zip->uncompress(entry); 2392 void* buf = file->editData(len); 2393 memcpy(buf, data, len); 2394 2395#if 0 2396 const int OFF = 0; 2397 const unsigned char* p = (unsigned char*)data; 2398 const unsigned char* end = p+len; 2399 p += OFF; 2400 for (int i=0; i<32 && p < end; i++) { 2401 printf("0x%03x ", i*0x10 + OFF); 2402 for (int j=0; j<0x10 && p < end; j++) { 2403 printf(" %02x", *p); 2404 p++; 2405 } 2406 printf("\n"); 2407 } 2408#endif 2409 2410 free(data); 2411 2412 count++; 2413 } 2414 2415bail: 2416 delete zip; 2417 return count; 2418} 2419 2420status_t AaptAssets::filter(Bundle* bundle) 2421{ 2422 ResourceFilter reqFilter; 2423 status_t err = reqFilter.parse(bundle->getConfigurations()); 2424 if (err != NO_ERROR) { 2425 return err; 2426 } 2427 2428 ResourceFilter prefFilter; 2429 err = prefFilter.parse(bundle->getPreferredConfigurations()); 2430 if (err != NO_ERROR) { 2431 return err; 2432 } 2433 2434 if (reqFilter.isEmpty() && prefFilter.isEmpty()) { 2435 return NO_ERROR; 2436 } 2437 2438 if (bundle->getVerbose()) { 2439 if (!reqFilter.isEmpty()) { 2440 printf("Applying required filter: %s\n", 2441 bundle->getConfigurations()); 2442 } 2443 if (!prefFilter.isEmpty()) { 2444 printf("Applying preferred filter: %s\n", 2445 bundle->getPreferredConfigurations()); 2446 } 2447 } 2448 2449 const Vector<sp<AaptDir> >& resdirs = mResDirs; 2450 const size_t ND = resdirs.size(); 2451 for (size_t i=0; i<ND; i++) { 2452 const sp<AaptDir>& dir = resdirs.itemAt(i); 2453 if (dir->getLeaf() == kValuesDir) { 2454 // The "value" dir is special since a single file defines 2455 // multiple resources, so we can not do filtering on the 2456 // files themselves. 2457 continue; 2458 } 2459 if (dir->getLeaf() == kMipmapDir) { 2460 // We also skip the "mipmap" directory, since the point of this 2461 // is to include all densities without stripping. If you put 2462 // other configurations in here as well they won't be stripped 2463 // either... So don't do that. Seriously. What is wrong with you? 2464 continue; 2465 } 2466 2467 const size_t NG = dir->getFiles().size(); 2468 for (size_t j=0; j<NG; j++) { 2469 sp<AaptGroup> grp = dir->getFiles().valueAt(j); 2470 2471 // First remove any configurations we know we don't need. 2472 for (size_t k=0; k<grp->getFiles().size(); k++) { 2473 sp<AaptFile> file = grp->getFiles().valueAt(k); 2474 if (k == 0 && grp->getFiles().size() == 1) { 2475 // If this is the only file left, we need to keep it. 2476 // Otherwise the resource IDs we are using will be inconsistent 2477 // with what we get when not stripping. Sucky, but at least 2478 // for now we can rely on the back-end doing another filtering 2479 // pass to take this out and leave us with this resource name 2480 // containing no entries. 2481 continue; 2482 } 2483 if (file->getPath().getPathExtension() == ".xml") { 2484 // We can't remove .xml files at this point, because when 2485 // we parse them they may add identifier resources, so 2486 // removing them can cause our resource identifiers to 2487 // become inconsistent. 2488 continue; 2489 } 2490 const ResTable_config& config(file->getGroupEntry().toParams()); 2491 if (!reqFilter.match(config)) { 2492 if (bundle->getVerbose()) { 2493 printf("Pruning unneeded resource: %s\n", 2494 file->getPrintableSource().string()); 2495 } 2496 grp->removeFile(k); 2497 k--; 2498 } 2499 } 2500 2501 // Quick check: no preferred filters, nothing more to do. 2502 if (prefFilter.isEmpty()) { 2503 continue; 2504 } 2505 2506 // Get the preferred density if there is one. We do not match exactly for density. 2507 // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we 2508 // pick xhdpi. 2509 uint32_t preferredDensity = 0; 2510 const SortedVector<uint32_t>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY); 2511 if (preferredConfigs != NULL && preferredConfigs->size() > 0) { 2512 preferredDensity = (*preferredConfigs)[0]; 2513 } 2514 2515 // Now deal with preferred configurations. 2516 for (int axis=AXIS_START; axis<=AXIS_END; axis++) { 2517 for (size_t k=0; k<grp->getFiles().size(); k++) { 2518 sp<AaptFile> file = grp->getFiles().valueAt(k); 2519 if (k == 0 && grp->getFiles().size() == 1) { 2520 // If this is the only file left, we need to keep it. 2521 // Otherwise the resource IDs we are using will be inconsistent 2522 // with what we get when not stripping. Sucky, but at least 2523 // for now we can rely on the back-end doing another filtering 2524 // pass to take this out and leave us with this resource name 2525 // containing no entries. 2526 continue; 2527 } 2528 if (file->getPath().getPathExtension() == ".xml") { 2529 // We can't remove .xml files at this point, because when 2530 // we parse them they may add identifier resources, so 2531 // removing them can cause our resource identifiers to 2532 // become inconsistent. 2533 continue; 2534 } 2535 const ResTable_config& config(file->getGroupEntry().toParams()); 2536 if (!prefFilter.match(axis, config)) { 2537 // This is a resource we would prefer not to have. Check 2538 // to see if have a similar variation that we would like 2539 // to have and, if so, we can drop it. 2540 2541 uint32_t bestDensity = config.density; 2542 2543 for (size_t m=0; m<grp->getFiles().size(); m++) { 2544 if (m == k) continue; 2545 sp<AaptFile> mfile = grp->getFiles().valueAt(m); 2546 const ResTable_config& mconfig(mfile->getGroupEntry().toParams()); 2547 if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) { 2548 if (axis == AXIS_DENSITY && preferredDensity > 0) { 2549 // See if there is a better density resource 2550 if (mconfig.density < bestDensity && 2551 mconfig.density > preferredDensity && 2552 bestDensity > preferredDensity) { 2553 // This density is between our best density and 2554 // the preferred density, therefore it is better. 2555 bestDensity = mconfig.density; 2556 } else if (mconfig.density > bestDensity && 2557 bestDensity < preferredDensity) { 2558 // This density is better than our best density and 2559 // our best density was smaller than our preferred 2560 // density, so it is better. 2561 bestDensity = mconfig.density; 2562 } 2563 } else if (prefFilter.match(axis, mconfig)) { 2564 if (bundle->getVerbose()) { 2565 printf("Pruning unneeded resource: %s\n", 2566 file->getPrintableSource().string()); 2567 } 2568 grp->removeFile(k); 2569 k--; 2570 break; 2571 } 2572 } 2573 } 2574 2575 if (axis == AXIS_DENSITY && preferredDensity > 0 && 2576 bestDensity != config.density) { 2577 if (bundle->getVerbose()) { 2578 printf("Pruning unneeded resource: %s\n", 2579 file->getPrintableSource().string()); 2580 } 2581 grp->removeFile(k); 2582 k--; 2583 } 2584 } 2585 } 2586 } 2587 } 2588 } 2589 2590 return NO_ERROR; 2591} 2592 2593sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name) 2594{ 2595 sp<AaptSymbols> sym = mSymbols.valueFor(name); 2596 if (sym == NULL) { 2597 sym = new AaptSymbols(); 2598 mSymbols.add(name, sym); 2599 } 2600 return sym; 2601} 2602 2603sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name) 2604{ 2605 sp<AaptSymbols> sym = mJavaSymbols.valueFor(name); 2606 if (sym == NULL) { 2607 sym = new AaptSymbols(); 2608 mJavaSymbols.add(name, sym); 2609 } 2610 return sym; 2611} 2612 2613status_t AaptAssets::applyJavaSymbols() 2614{ 2615 size_t N = mJavaSymbols.size(); 2616 for (size_t i=0; i<N; i++) { 2617 const String8& name = mJavaSymbols.keyAt(i); 2618 const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i); 2619 ssize_t pos = mSymbols.indexOfKey(name); 2620 if (pos < 0) { 2621 SourcePos pos; 2622 pos.error("Java symbol dir %s not defined\n", name.string()); 2623 return UNKNOWN_ERROR; 2624 } 2625 //printf("**** applying java symbols in dir %s\n", name.string()); 2626 status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols); 2627 if (err != NO_ERROR) { 2628 return err; 2629 } 2630 } 2631 2632 return NO_ERROR; 2633} 2634 2635bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const { 2636 //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n", 2637 // sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0, 2638 // sym.isJavaSymbol ? 1 : 0); 2639 if (!mHavePrivateSymbols) return true; 2640 if (sym.isPublic) return true; 2641 if (includePrivate && sym.isJavaSymbol) return true; 2642 return false; 2643} 2644 2645status_t AaptAssets::buildIncludedResources(Bundle* bundle) 2646{ 2647 if (!mHaveIncludedAssets) { 2648 // Add in all includes. 2649 const Vector<const char*>& incl = bundle->getPackageIncludes(); 2650 const size_t N=incl.size(); 2651 for (size_t i=0; i<N; i++) { 2652 if (bundle->getVerbose()) 2653 printf("Including resources from package: %s\n", incl[i]); 2654 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) { 2655 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n", 2656 incl[i]); 2657 return UNKNOWN_ERROR; 2658 } 2659 } 2660 mHaveIncludedAssets = true; 2661 } 2662 2663 return NO_ERROR; 2664} 2665 2666status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file) 2667{ 2668 const ResTable& res = getIncludedResources(); 2669 // XXX dirty! 2670 return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL); 2671} 2672 2673const ResTable& AaptAssets::getIncludedResources() const 2674{ 2675 return mIncludedAssets.getResources(false); 2676} 2677 2678void AaptAssets::print(const String8& prefix) const 2679{ 2680 String8 innerPrefix(prefix); 2681 innerPrefix.append(" "); 2682 String8 innerInnerPrefix(innerPrefix); 2683 innerInnerPrefix.append(" "); 2684 printf("%sConfigurations:\n", prefix.string()); 2685 const size_t N=mGroupEntries.size(); 2686 for (size_t i=0; i<N; i++) { 2687 String8 cname = mGroupEntries.itemAt(i).toDirName(String8()); 2688 printf("%s %s\n", prefix.string(), 2689 cname != "" ? cname.string() : "(default)"); 2690 } 2691 2692 printf("\n%sFiles:\n", prefix.string()); 2693 AaptDir::print(innerPrefix); 2694 2695 printf("\n%sResource Dirs:\n", prefix.string()); 2696 const Vector<sp<AaptDir> >& resdirs = mResDirs; 2697 const size_t NR = resdirs.size(); 2698 for (size_t i=0; i<NR; i++) { 2699 const sp<AaptDir>& d = resdirs.itemAt(i); 2700 printf("%s Type %s\n", prefix.string(), d->getLeaf().string()); 2701 d->print(innerInnerPrefix); 2702 } 2703} 2704 2705sp<AaptDir> AaptAssets::resDir(const String8& name) const 2706{ 2707 const Vector<sp<AaptDir> >& resdirs = mResDirs; 2708 const size_t N = resdirs.size(); 2709 for (size_t i=0; i<N; i++) { 2710 const sp<AaptDir>& d = resdirs.itemAt(i); 2711 if (d->getLeaf() == name) { 2712 return d; 2713 } 2714 } 2715 return NULL; 2716} 2717 2718bool 2719valid_symbol_name(const String8& symbol) 2720{ 2721 static char const * const KEYWORDS[] = { 2722 "abstract", "assert", "boolean", "break", 2723 "byte", "case", "catch", "char", "class", "const", "continue", 2724 "default", "do", "double", "else", "enum", "extends", "final", 2725 "finally", "float", "for", "goto", "if", "implements", "import", 2726 "instanceof", "int", "interface", "long", "native", "new", "package", 2727 "private", "protected", "public", "return", "short", "static", 2728 "strictfp", "super", "switch", "synchronized", "this", "throw", 2729 "throws", "transient", "try", "void", "volatile", "while", 2730 "true", "false", "null", 2731 NULL 2732 }; 2733 const char*const* k = KEYWORDS; 2734 const char*const s = symbol.string(); 2735 while (*k) { 2736 if (0 == strcmp(s, *k)) { 2737 return false; 2738 } 2739 k++; 2740 } 2741 return true; 2742} 2743