AaptAssets.cpp revision b96cbbd11c4590bec846212c33361e02293f18b5
1// 2// Copyright 2006 The Android Open Source Project 3// 4 5#include "AaptAssets.h" 6#include "Main.h" 7 8#include <utils/misc.h> 9#include <utils/SortedVector.h> 10 11#include <ctype.h> 12#include <dirent.h> 13#include <errno.h> 14 15static const char* kDefaultLocale = "default"; 16static const char* kWildcardName = "any"; 17static const char* kAssetDir = "assets"; 18static const char* kResourceDir = "res"; 19static const char* kInvalidChars = "/\\:"; 20static const size_t kMaxAssetFileName = 100; 21 22static const String8 kResString(kResourceDir); 23 24/* 25 * Names of asset files must meet the following criteria: 26 * 27 * - the filename length must be less than kMaxAssetFileName bytes long 28 * (and can't be empty) 29 * - all characters must be 7-bit printable ASCII 30 * - none of { '/' '\\' ':' } 31 * 32 * Pass in just the filename, not the full path. 33 */ 34static bool validateFileName(const char* fileName) 35{ 36 const char* cp = fileName; 37 size_t len = 0; 38 39 while (*cp != '\0') { 40 if ((*cp & 0x80) != 0) 41 return false; // reject high ASCII 42 if (*cp < 0x20 || *cp >= 0x7f) 43 return false; // reject control chars and 0x7f 44 if (strchr(kInvalidChars, *cp) != NULL) 45 return false; // reject path sep chars 46 cp++; 47 len++; 48 } 49 50 if (len < 1 || len > kMaxAssetFileName) 51 return false; // reject empty or too long 52 53 return true; 54} 55 56static bool isHidden(const char *root, const char *path) 57{ 58 const char *ext = NULL; 59 const char *type = NULL; 60 61 // Skip all hidden files. 62 if (path[0] == '.') { 63 // Skip ., .. and .svn but don't chatter about it. 64 if (strcmp(path, ".") == 0 65 || strcmp(path, "..") == 0 66 || strcmp(path, ".svn") == 0) { 67 return true; 68 } 69 type = "hidden"; 70 } else if (path[0] == '_') { 71 // skip directories starting with _ (don't chatter about it) 72 String8 subdirName(root); 73 subdirName.appendPath(path); 74 if (getFileType(subdirName.string()) == kFileTypeDirectory) { 75 return true; 76 } 77 } else if (strcmp(path, "CVS") == 0) { 78 // Skip CVS but don't chatter about it. 79 return true; 80 } else if (strcasecmp(path, "thumbs.db") == 0 81 || strcasecmp(path, "picasa.ini") == 0) { 82 // Skip suspected image indexes files. 83 type = "index"; 84 } else if (path[strlen(path)-1] == '~') { 85 // Skip suspected emacs backup files. 86 type = "backup"; 87 } else if ((ext = strrchr(path, '.')) != NULL && strcmp(ext, ".scc") == 0) { 88 // Skip VisualSourceSafe files and don't chatter about it 89 return true; 90 } else { 91 // Let everything else through. 92 return false; 93 } 94 95 /* If we get this far, "type" should be set and the file 96 * should be skipped. 97 */ 98 String8 subdirName(root); 99 subdirName.appendPath(path); 100 fprintf(stderr, " (skipping %s %s '%s')\n", type, 101 getFileType(subdirName.string())==kFileTypeDirectory ? "dir":"file", 102 subdirName.string()); 103 104 return true; 105} 106 107// ========================================================================= 108// ========================================================================= 109// ========================================================================= 110 111status_t 112AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value) 113{ 114 ResTable_config config; 115 116 // IMSI - MCC 117 if (getMccName(part.string(), &config)) { 118 *axis = AXIS_MCC; 119 *value = config.mcc; 120 return 0; 121 } 122 123 // IMSI - MNC 124 if (getMncName(part.string(), &config)) { 125 *axis = AXIS_MNC; 126 *value = config.mnc; 127 return 0; 128 } 129 130 // locale - language 131 if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) { 132 *axis = AXIS_LANGUAGE; 133 *value = part[1] << 8 | part[0]; 134 return 0; 135 } 136 137 // locale - language_REGION 138 if (part.length() == 5 && isalpha(part[0]) && isalpha(part[1]) 139 && part[2] == '_' && isalpha(part[3]) && isalpha(part[4])) { 140 *axis = AXIS_LANGUAGE; 141 *value = (part[4] << 24) | (part[3] << 16) | (part[1] << 8) | (part[0]); 142 return 0; 143 } 144 145 // smallest screen dp width 146 if (getSmallestScreenWidthDpName(part.string(), &config)) { 147 *axis = AXIS_SMALLESTSCREENWIDTHDP; 148 *value = config.smallestScreenWidthDp; 149 return 0; 150 } 151 152 // screen dp width 153 if (getScreenWidthDpName(part.string(), &config)) { 154 *axis = AXIS_SCREENWIDTHDP; 155 *value = config.screenWidthDp; 156 return 0; 157 } 158 159 // screen dp height 160 if (getScreenHeightDpName(part.string(), &config)) { 161 *axis = AXIS_SCREENHEIGHTDP; 162 *value = config.screenHeightDp; 163 return 0; 164 } 165 166 // screen layout size 167 if (getScreenLayoutSizeName(part.string(), &config)) { 168 *axis = AXIS_SCREENLAYOUTSIZE; 169 *value = (config.screenLayout&ResTable_config::MASK_SCREENSIZE); 170 return 0; 171 } 172 173 // screen layout long 174 if (getScreenLayoutLongName(part.string(), &config)) { 175 *axis = AXIS_SCREENLAYOUTLONG; 176 *value = (config.screenLayout&ResTable_config::MASK_SCREENLONG); 177 return 0; 178 } 179 180 // orientation 181 if (getOrientationName(part.string(), &config)) { 182 *axis = AXIS_ORIENTATION; 183 *value = config.orientation; 184 return 0; 185 } 186 187 // ui mode type 188 if (getUiModeTypeName(part.string(), &config)) { 189 *axis = AXIS_UIMODETYPE; 190 *value = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE); 191 return 0; 192 } 193 194 // ui mode night 195 if (getUiModeNightName(part.string(), &config)) { 196 *axis = AXIS_UIMODENIGHT; 197 *value = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT); 198 return 0; 199 } 200 201 // density 202 if (getDensityName(part.string(), &config)) { 203 *axis = AXIS_DENSITY; 204 *value = config.density; 205 return 0; 206 } 207 208 // touchscreen 209 if (getTouchscreenName(part.string(), &config)) { 210 *axis = AXIS_TOUCHSCREEN; 211 *value = config.touchscreen; 212 return 0; 213 } 214 215 // keyboard hidden 216 if (getKeysHiddenName(part.string(), &config)) { 217 *axis = AXIS_KEYSHIDDEN; 218 *value = config.inputFlags; 219 return 0; 220 } 221 222 // keyboard 223 if (getKeyboardName(part.string(), &config)) { 224 *axis = AXIS_KEYBOARD; 225 *value = config.keyboard; 226 return 0; 227 } 228 229 // navigation hidden 230 if (getNavHiddenName(part.string(), &config)) { 231 *axis = AXIS_NAVHIDDEN; 232 *value = config.inputFlags; 233 return 0; 234 } 235 236 // navigation 237 if (getNavigationName(part.string(), &config)) { 238 *axis = AXIS_NAVIGATION; 239 *value = config.navigation; 240 return 0; 241 } 242 243 // screen size 244 if (getScreenSizeName(part.string(), &config)) { 245 *axis = AXIS_SCREENSIZE; 246 *value = config.screenSize; 247 return 0; 248 } 249 250 // version 251 if (getVersionName(part.string(), &config)) { 252 *axis = AXIS_VERSION; 253 *value = config.version; 254 return 0; 255 } 256 257 return 1; 258} 259 260bool 261AaptGroupEntry::initFromDirName(const char* dir, String8* resType) 262{ 263 Vector<String8> parts; 264 265 String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den; 266 String8 touch, key, keysHidden, nav, navHidden, size, vers; 267 String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp; 268 269 const char *p = dir; 270 const char *q; 271 while (NULL != (q = strchr(p, '-'))) { 272 String8 val(p, q-p); 273 val.toLower(); 274 parts.add(val); 275 //printf("part: %s\n", parts[parts.size()-1].string()); 276 p = q+1; 277 } 278 String8 val(p); 279 val.toLower(); 280 parts.add(val); 281 //printf("part: %s\n", parts[parts.size()-1].string()); 282 283 const int N = parts.size(); 284 int index = 0; 285 String8 part = parts[index]; 286 287 // resource type 288 if (!isValidResourceType(part)) { 289 return false; 290 } 291 *resType = part; 292 293 index++; 294 if (index == N) { 295 goto success; 296 } 297 part = parts[index]; 298 299 // imsi - mcc 300 if (getMccName(part.string())) { 301 mcc = part; 302 303 index++; 304 if (index == N) { 305 goto success; 306 } 307 part = parts[index]; 308 } else { 309 //printf("not mcc: %s\n", part.string()); 310 } 311 312 // imsi - mnc 313 if (getMncName(part.string())) { 314 mnc = part; 315 316 index++; 317 if (index == N) { 318 goto success; 319 } 320 part = parts[index]; 321 } else { 322 //printf("not mcc: %s\n", part.string()); 323 } 324 325 // locale - language 326 if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) { 327 loc = part; 328 329 index++; 330 if (index == N) { 331 goto success; 332 } 333 part = parts[index]; 334 } else { 335 //printf("not language: %s\n", part.string()); 336 } 337 338 // locale - region 339 if (loc.length() > 0 340 && part.length() == 3 && part[0] == 'r' && part[0] && part[1]) { 341 loc += "-"; 342 part.toUpper(); 343 loc += part.string() + 1; 344 345 index++; 346 if (index == N) { 347 goto success; 348 } 349 part = parts[index]; 350 } else { 351 //printf("not region: %s\n", part.string()); 352 } 353 354 if (getSmallestScreenWidthDpName(part.string())) { 355 smallestwidthdp = part; 356 357 index++; 358 if (index == N) { 359 goto success; 360 } 361 part = parts[index]; 362 } else { 363 //printf("not smallest screen width dp: %s\n", part.string()); 364 } 365 366 if (getScreenWidthDpName(part.string())) { 367 widthdp = part; 368 369 index++; 370 if (index == N) { 371 goto success; 372 } 373 part = parts[index]; 374 } else { 375 //printf("not screen width dp: %s\n", part.string()); 376 } 377 378 if (getScreenHeightDpName(part.string())) { 379 heightdp = part; 380 381 index++; 382 if (index == N) { 383 goto success; 384 } 385 part = parts[index]; 386 } else { 387 //printf("not screen height dp: %s\n", part.string()); 388 } 389 390 if (getScreenLayoutSizeName(part.string())) { 391 layoutsize = part; 392 393 index++; 394 if (index == N) { 395 goto success; 396 } 397 part = parts[index]; 398 } else { 399 //printf("not screen layout size: %s\n", part.string()); 400 } 401 402 if (getScreenLayoutLongName(part.string())) { 403 layoutlong = part; 404 405 index++; 406 if (index == N) { 407 goto success; 408 } 409 part = parts[index]; 410 } else { 411 //printf("not screen layout long: %s\n", part.string()); 412 } 413 414 // orientation 415 if (getOrientationName(part.string())) { 416 orient = part; 417 418 index++; 419 if (index == N) { 420 goto success; 421 } 422 part = parts[index]; 423 } else { 424 //printf("not orientation: %s\n", part.string()); 425 } 426 427 // ui mode type 428 if (getUiModeTypeName(part.string())) { 429 uiModeType = part; 430 431 index++; 432 if (index == N) { 433 goto success; 434 } 435 part = parts[index]; 436 } else { 437 //printf("not ui mode type: %s\n", part.string()); 438 } 439 440 // ui mode night 441 if (getUiModeNightName(part.string())) { 442 uiModeNight = part; 443 444 index++; 445 if (index == N) { 446 goto success; 447 } 448 part = parts[index]; 449 } else { 450 //printf("not ui mode night: %s\n", part.string()); 451 } 452 453 // density 454 if (getDensityName(part.string())) { 455 den = part; 456 457 index++; 458 if (index == N) { 459 goto success; 460 } 461 part = parts[index]; 462 } else { 463 //printf("not density: %s\n", part.string()); 464 } 465 466 // touchscreen 467 if (getTouchscreenName(part.string())) { 468 touch = part; 469 470 index++; 471 if (index == N) { 472 goto success; 473 } 474 part = parts[index]; 475 } else { 476 //printf("not touchscreen: %s\n", part.string()); 477 } 478 479 // keyboard hidden 480 if (getKeysHiddenName(part.string())) { 481 keysHidden = part; 482 483 index++; 484 if (index == N) { 485 goto success; 486 } 487 part = parts[index]; 488 } else { 489 //printf("not keysHidden: %s\n", part.string()); 490 } 491 492 // keyboard 493 if (getKeyboardName(part.string())) { 494 key = part; 495 496 index++; 497 if (index == N) { 498 goto success; 499 } 500 part = parts[index]; 501 } else { 502 //printf("not keyboard: %s\n", part.string()); 503 } 504 505 // navigation hidden 506 if (getNavHiddenName(part.string())) { 507 navHidden = part; 508 509 index++; 510 if (index == N) { 511 goto success; 512 } 513 part = parts[index]; 514 } else { 515 //printf("not navHidden: %s\n", part.string()); 516 } 517 518 if (getNavigationName(part.string())) { 519 nav = part; 520 521 index++; 522 if (index == N) { 523 goto success; 524 } 525 part = parts[index]; 526 } else { 527 //printf("not navigation: %s\n", part.string()); 528 } 529 530 if (getScreenSizeName(part.string())) { 531 size = part; 532 533 index++; 534 if (index == N) { 535 goto success; 536 } 537 part = parts[index]; 538 } else { 539 //printf("not screen size: %s\n", part.string()); 540 } 541 542 if (getVersionName(part.string())) { 543 vers = part; 544 545 index++; 546 if (index == N) { 547 goto success; 548 } 549 part = parts[index]; 550 } else { 551 //printf("not version: %s\n", part.string()); 552 } 553 554 // if there are extra parts, it doesn't match 555 return false; 556 557success: 558 this->mcc = mcc; 559 this->mnc = mnc; 560 this->locale = loc; 561 this->screenLayoutSize = layoutsize; 562 this->screenLayoutLong = layoutlong; 563 this->smallestScreenWidthDp = smallestwidthdp; 564 this->screenWidthDp = widthdp; 565 this->screenHeightDp = heightdp; 566 this->orientation = orient; 567 this->uiModeType = uiModeType; 568 this->uiModeNight = uiModeNight; 569 this->density = den; 570 this->touchscreen = touch; 571 this->keysHidden = keysHidden; 572 this->keyboard = key; 573 this->navHidden = navHidden; 574 this->navigation = nav; 575 this->screenSize = size; 576 this->version = vers; 577 578 // what is this anyway? 579 this->vendor = ""; 580 581 return true; 582} 583 584String8 585AaptGroupEntry::toString() const 586{ 587 String8 s = this->mcc; 588 s += ","; 589 s += this->mnc; 590 s += ","; 591 s += this->locale; 592 s += ","; 593 s += smallestScreenWidthDp; 594 s += ","; 595 s += screenWidthDp; 596 s += ","; 597 s += screenHeightDp; 598 s += ","; 599 s += screenLayoutSize; 600 s += ","; 601 s += screenLayoutLong; 602 s += ","; 603 s += this->orientation; 604 s += ","; 605 s += uiModeType; 606 s += ","; 607 s += uiModeNight; 608 s += ","; 609 s += density; 610 s += ","; 611 s += touchscreen; 612 s += ","; 613 s += keysHidden; 614 s += ","; 615 s += keyboard; 616 s += ","; 617 s += navHidden; 618 s += ","; 619 s += navigation; 620 s += ","; 621 s += screenSize; 622 s += ","; 623 s += version; 624 return s; 625} 626 627String8 628AaptGroupEntry::toDirName(const String8& resType) const 629{ 630 String8 s = resType; 631 if (this->mcc != "") { 632 s += "-"; 633 s += mcc; 634 } 635 if (this->mnc != "") { 636 s += "-"; 637 s += mnc; 638 } 639 if (this->locale != "") { 640 s += "-"; 641 s += locale; 642 } 643 if (this->smallestScreenWidthDp != "") { 644 s += "-"; 645 s += smallestScreenWidthDp; 646 } 647 if (this->screenWidthDp != "") { 648 s += "-"; 649 s += screenWidthDp; 650 } 651 if (this->screenHeightDp != "") { 652 s += "-"; 653 s += screenHeightDp; 654 } 655 if (this->screenLayoutSize != "") { 656 s += "-"; 657 s += screenLayoutSize; 658 } 659 if (this->screenLayoutLong != "") { 660 s += "-"; 661 s += screenLayoutLong; 662 } 663 if (this->orientation != "") { 664 s += "-"; 665 s += orientation; 666 } 667 if (this->uiModeType != "") { 668 s += "-"; 669 s += uiModeType; 670 } 671 if (this->uiModeNight != "") { 672 s += "-"; 673 s += uiModeNight; 674 } 675 if (this->density != "") { 676 s += "-"; 677 s += density; 678 } 679 if (this->touchscreen != "") { 680 s += "-"; 681 s += touchscreen; 682 } 683 if (this->keysHidden != "") { 684 s += "-"; 685 s += keysHidden; 686 } 687 if (this->keyboard != "") { 688 s += "-"; 689 s += keyboard; 690 } 691 if (this->navHidden != "") { 692 s += "-"; 693 s += navHidden; 694 } 695 if (this->navigation != "") { 696 s += "-"; 697 s += navigation; 698 } 699 if (this->screenSize != "") { 700 s += "-"; 701 s += screenSize; 702 } 703 if (this->version != "") { 704 s += "-"; 705 s += version; 706 } 707 708 return s; 709} 710 711bool AaptGroupEntry::getMccName(const char* name, 712 ResTable_config* out) 713{ 714 if (strcmp(name, kWildcardName) == 0) { 715 if (out) out->mcc = 0; 716 return true; 717 } 718 const char* c = name; 719 if (tolower(*c) != 'm') return false; 720 c++; 721 if (tolower(*c) != 'c') return false; 722 c++; 723 if (tolower(*c) != 'c') return false; 724 c++; 725 726 const char* val = c; 727 728 while (*c >= '0' && *c <= '9') { 729 c++; 730 } 731 if (*c != 0) return false; 732 if (c-val != 3) return false; 733 734 int d = atoi(val); 735 if (d != 0) { 736 if (out) out->mcc = d; 737 return true; 738 } 739 740 return false; 741} 742 743bool AaptGroupEntry::getMncName(const char* name, 744 ResTable_config* out) 745{ 746 if (strcmp(name, kWildcardName) == 0) { 747 if (out) out->mcc = 0; 748 return true; 749 } 750 const char* c = name; 751 if (tolower(*c) != 'm') return false; 752 c++; 753 if (tolower(*c) != 'n') return false; 754 c++; 755 if (tolower(*c) != 'c') return false; 756 c++; 757 758 const char* val = c; 759 760 while (*c >= '0' && *c <= '9') { 761 c++; 762 } 763 if (*c != 0) return false; 764 if (c-val == 0 || c-val > 3) return false; 765 766 if (out) { 767 out->mnc = atoi(val); 768 } 769 770 return true; 771} 772 773/* 774 * Does this directory name fit the pattern of a locale dir ("en-rUS" or 775 * "default")? 776 * 777 * TODO: Should insist that the first two letters are lower case, and the 778 * second two are upper. 779 */ 780bool AaptGroupEntry::getLocaleName(const char* fileName, 781 ResTable_config* out) 782{ 783 if (strcmp(fileName, kWildcardName) == 0 784 || strcmp(fileName, kDefaultLocale) == 0) { 785 if (out) { 786 out->language[0] = 0; 787 out->language[1] = 0; 788 out->country[0] = 0; 789 out->country[1] = 0; 790 } 791 return true; 792 } 793 794 if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) { 795 if (out) { 796 out->language[0] = fileName[0]; 797 out->language[1] = fileName[1]; 798 out->country[0] = 0; 799 out->country[1] = 0; 800 } 801 return true; 802 } 803 804 if (strlen(fileName) == 5 && 805 isalpha(fileName[0]) && 806 isalpha(fileName[1]) && 807 fileName[2] == '-' && 808 isalpha(fileName[3]) && 809 isalpha(fileName[4])) { 810 if (out) { 811 out->language[0] = fileName[0]; 812 out->language[1] = fileName[1]; 813 out->country[0] = fileName[3]; 814 out->country[1] = fileName[4]; 815 } 816 return true; 817 } 818 819 return false; 820} 821 822bool AaptGroupEntry::getScreenLayoutSizeName(const char* name, 823 ResTable_config* out) 824{ 825 if (strcmp(name, kWildcardName) == 0) { 826 if (out) out->screenLayout = 827 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 828 | ResTable_config::SCREENSIZE_ANY; 829 return true; 830 } else if (strcmp(name, "small") == 0) { 831 if (out) out->screenLayout = 832 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 833 | ResTable_config::SCREENSIZE_SMALL; 834 return true; 835 } else if (strcmp(name, "normal") == 0) { 836 if (out) out->screenLayout = 837 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 838 | ResTable_config::SCREENSIZE_NORMAL; 839 return true; 840 } else if (strcmp(name, "large") == 0) { 841 if (out) out->screenLayout = 842 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 843 | ResTable_config::SCREENSIZE_LARGE; 844 return true; 845 } else if (strcmp(name, "xlarge") == 0) { 846 if (out) out->screenLayout = 847 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) 848 | ResTable_config::SCREENSIZE_XLARGE; 849 return true; 850 } 851 852 return false; 853} 854 855bool AaptGroupEntry::getScreenLayoutLongName(const char* name, 856 ResTable_config* out) 857{ 858 if (strcmp(name, kWildcardName) == 0) { 859 if (out) out->screenLayout = 860 (out->screenLayout&~ResTable_config::MASK_SCREENLONG) 861 | ResTable_config::SCREENLONG_ANY; 862 return true; 863 } else if (strcmp(name, "long") == 0) { 864 if (out) out->screenLayout = 865 (out->screenLayout&~ResTable_config::MASK_SCREENLONG) 866 | ResTable_config::SCREENLONG_YES; 867 return true; 868 } else if (strcmp(name, "notlong") == 0) { 869 if (out) out->screenLayout = 870 (out->screenLayout&~ResTable_config::MASK_SCREENLONG) 871 | ResTable_config::SCREENLONG_NO; 872 return true; 873 } 874 875 return false; 876} 877 878bool AaptGroupEntry::getOrientationName(const char* name, 879 ResTable_config* out) 880{ 881 if (strcmp(name, kWildcardName) == 0) { 882 if (out) out->orientation = out->ORIENTATION_ANY; 883 return true; 884 } else if (strcmp(name, "port") == 0) { 885 if (out) out->orientation = out->ORIENTATION_PORT; 886 return true; 887 } else if (strcmp(name, "land") == 0) { 888 if (out) out->orientation = out->ORIENTATION_LAND; 889 return true; 890 } else if (strcmp(name, "square") == 0) { 891 if (out) out->orientation = out->ORIENTATION_SQUARE; 892 return true; 893 } 894 895 return false; 896} 897 898bool AaptGroupEntry::getUiModeTypeName(const char* name, 899 ResTable_config* out) 900{ 901 if (strcmp(name, kWildcardName) == 0) { 902 if (out) out->uiMode = 903 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 904 | ResTable_config::UI_MODE_TYPE_ANY; 905 return true; 906 } else if (strcmp(name, "desk") == 0) { 907 if (out) out->uiMode = 908 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 909 | ResTable_config::UI_MODE_TYPE_DESK; 910 return true; 911 } else if (strcmp(name, "car") == 0) { 912 if (out) out->uiMode = 913 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 914 | ResTable_config::UI_MODE_TYPE_CAR; 915 return true; 916 } else if (strcmp(name, "television") == 0) { 917 if (out) out->uiMode = 918 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) 919 | ResTable_config::UI_MODE_TYPE_TELEVISION; 920 return true; 921 } 922 923 return false; 924} 925 926bool AaptGroupEntry::getUiModeNightName(const char* name, 927 ResTable_config* out) 928{ 929 if (strcmp(name, kWildcardName) == 0) { 930 if (out) out->uiMode = 931 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) 932 | ResTable_config::UI_MODE_NIGHT_ANY; 933 return true; 934 } else if (strcmp(name, "night") == 0) { 935 if (out) out->uiMode = 936 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) 937 | ResTable_config::UI_MODE_NIGHT_YES; 938 return true; 939 } else if (strcmp(name, "notnight") == 0) { 940 if (out) out->uiMode = 941 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) 942 | ResTable_config::UI_MODE_NIGHT_NO; 943 return true; 944 } 945 946 return false; 947} 948 949bool AaptGroupEntry::getDensityName(const char* name, 950 ResTable_config* out) 951{ 952 if (strcmp(name, kWildcardName) == 0) { 953 if (out) out->density = ResTable_config::DENSITY_DEFAULT; 954 return true; 955 } 956 957 if (strcmp(name, "nodpi") == 0) { 958 if (out) out->density = ResTable_config::DENSITY_NONE; 959 return true; 960 } 961 962 if (strcmp(name, "ldpi") == 0) { 963 if (out) out->density = ResTable_config::DENSITY_LOW; 964 return true; 965 } 966 967 if (strcmp(name, "mdpi") == 0) { 968 if (out) out->density = ResTable_config::DENSITY_MEDIUM; 969 return true; 970 } 971 972 if (strcmp(name, "tvdpi") == 0) { 973 if (out) out->density = ResTable_config::DENSITY_TV; 974 return true; 975 } 976 977 if (strcmp(name, "hdpi") == 0) { 978 if (out) out->density = ResTable_config::DENSITY_HIGH; 979 return true; 980 } 981 982 if (strcmp(name, "xhdpi") == 0) { 983 if (out) out->density = ResTable_config::DENSITY_MEDIUM*2; 984 return true; 985 } 986 987 char* c = (char*)name; 988 while (*c >= '0' && *c <= '9') { 989 c++; 990 } 991 992 // check that we have 'dpi' after the last digit. 993 if (toupper(c[0]) != 'D' || 994 toupper(c[1]) != 'P' || 995 toupper(c[2]) != 'I' || 996 c[3] != 0) { 997 return false; 998 } 999 1000 // temporarily replace the first letter with \0 to 1001 // use atoi. 1002 char tmp = c[0]; 1003 c[0] = '\0'; 1004 1005 int d = atoi(name); 1006 c[0] = tmp; 1007 1008 if (d != 0) { 1009 if (out) out->density = d; 1010 return true; 1011 } 1012 1013 return false; 1014} 1015 1016bool AaptGroupEntry::getTouchscreenName(const char* name, 1017 ResTable_config* out) 1018{ 1019 if (strcmp(name, kWildcardName) == 0) { 1020 if (out) out->touchscreen = out->TOUCHSCREEN_ANY; 1021 return true; 1022 } else if (strcmp(name, "notouch") == 0) { 1023 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH; 1024 return true; 1025 } else if (strcmp(name, "stylus") == 0) { 1026 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS; 1027 return true; 1028 } else if (strcmp(name, "finger") == 0) { 1029 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER; 1030 return true; 1031 } 1032 1033 return false; 1034} 1035 1036bool AaptGroupEntry::getKeysHiddenName(const char* name, 1037 ResTable_config* out) 1038{ 1039 uint8_t mask = 0; 1040 uint8_t value = 0; 1041 if (strcmp(name, kWildcardName) == 0) { 1042 mask = ResTable_config::MASK_KEYSHIDDEN; 1043 value = ResTable_config::KEYSHIDDEN_ANY; 1044 } else if (strcmp(name, "keysexposed") == 0) { 1045 mask = ResTable_config::MASK_KEYSHIDDEN; 1046 value = ResTable_config::KEYSHIDDEN_NO; 1047 } else if (strcmp(name, "keyshidden") == 0) { 1048 mask = ResTable_config::MASK_KEYSHIDDEN; 1049 value = ResTable_config::KEYSHIDDEN_YES; 1050 } else if (strcmp(name, "keyssoft") == 0) { 1051 mask = ResTable_config::MASK_KEYSHIDDEN; 1052 value = ResTable_config::KEYSHIDDEN_SOFT; 1053 } 1054 1055 if (mask != 0) { 1056 if (out) out->inputFlags = (out->inputFlags&~mask) | value; 1057 return true; 1058 } 1059 1060 return false; 1061} 1062 1063bool AaptGroupEntry::getKeyboardName(const char* name, 1064 ResTable_config* out) 1065{ 1066 if (strcmp(name, kWildcardName) == 0) { 1067 if (out) out->keyboard = out->KEYBOARD_ANY; 1068 return true; 1069 } else if (strcmp(name, "nokeys") == 0) { 1070 if (out) out->keyboard = out->KEYBOARD_NOKEYS; 1071 return true; 1072 } else if (strcmp(name, "qwerty") == 0) { 1073 if (out) out->keyboard = out->KEYBOARD_QWERTY; 1074 return true; 1075 } else if (strcmp(name, "12key") == 0) { 1076 if (out) out->keyboard = out->KEYBOARD_12KEY; 1077 return true; 1078 } 1079 1080 return false; 1081} 1082 1083bool AaptGroupEntry::getNavHiddenName(const char* name, 1084 ResTable_config* out) 1085{ 1086 uint8_t mask = 0; 1087 uint8_t value = 0; 1088 if (strcmp(name, kWildcardName) == 0) { 1089 mask = ResTable_config::MASK_NAVHIDDEN; 1090 value = ResTable_config::NAVHIDDEN_ANY; 1091 } else if (strcmp(name, "navexposed") == 0) { 1092 mask = ResTable_config::MASK_NAVHIDDEN; 1093 value = ResTable_config::NAVHIDDEN_NO; 1094 } else if (strcmp(name, "navhidden") == 0) { 1095 mask = ResTable_config::MASK_NAVHIDDEN; 1096 value = ResTable_config::NAVHIDDEN_YES; 1097 } 1098 1099 if (mask != 0) { 1100 if (out) out->inputFlags = (out->inputFlags&~mask) | value; 1101 return true; 1102 } 1103 1104 return false; 1105} 1106 1107bool AaptGroupEntry::getNavigationName(const char* name, 1108 ResTable_config* out) 1109{ 1110 if (strcmp(name, kWildcardName) == 0) { 1111 if (out) out->navigation = out->NAVIGATION_ANY; 1112 return true; 1113 } else if (strcmp(name, "nonav") == 0) { 1114 if (out) out->navigation = out->NAVIGATION_NONAV; 1115 return true; 1116 } else if (strcmp(name, "dpad") == 0) { 1117 if (out) out->navigation = out->NAVIGATION_DPAD; 1118 return true; 1119 } else if (strcmp(name, "trackball") == 0) { 1120 if (out) out->navigation = out->NAVIGATION_TRACKBALL; 1121 return true; 1122 } else if (strcmp(name, "wheel") == 0) { 1123 if (out) out->navigation = out->NAVIGATION_WHEEL; 1124 return true; 1125 } 1126 1127 return false; 1128} 1129 1130bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out) 1131{ 1132 if (strcmp(name, kWildcardName) == 0) { 1133 if (out) { 1134 out->screenWidth = out->SCREENWIDTH_ANY; 1135 out->screenHeight = out->SCREENHEIGHT_ANY; 1136 } 1137 return true; 1138 } 1139 1140 const char* x = name; 1141 while (*x >= '0' && *x <= '9') x++; 1142 if (x == name || *x != 'x') return false; 1143 String8 xName(name, x-name); 1144 x++; 1145 1146 const char* y = x; 1147 while (*y >= '0' && *y <= '9') y++; 1148 if (y == name || *y != 0) return false; 1149 String8 yName(x, y-x); 1150 1151 uint16_t w = (uint16_t)atoi(xName.string()); 1152 uint16_t h = (uint16_t)atoi(yName.string()); 1153 if (w < h) { 1154 return false; 1155 } 1156 1157 if (out) { 1158 out->screenWidth = w; 1159 out->screenHeight = h; 1160 } 1161 1162 return true; 1163} 1164 1165bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out) 1166{ 1167 if (strcmp(name, kWildcardName) == 0) { 1168 if (out) { 1169 out->smallestScreenWidthDp = out->SCREENWIDTH_ANY; 1170 } 1171 return true; 1172 } 1173 1174 if (*name != 's') return false; 1175 name++; 1176 if (*name != 'w') return false; 1177 name++; 1178 const char* x = name; 1179 while (*x >= '0' && *x <= '9') x++; 1180 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; 1181 String8 xName(name, x-name); 1182 1183 if (out) { 1184 out->smallestScreenWidthDp = (uint16_t)atoi(xName.string()); 1185 } 1186 1187 return true; 1188} 1189 1190bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out) 1191{ 1192 if (strcmp(name, kWildcardName) == 0) { 1193 if (out) { 1194 out->screenWidthDp = out->SCREENWIDTH_ANY; 1195 } 1196 return true; 1197 } 1198 1199 if (*name != 'w') return false; 1200 name++; 1201 const char* x = name; 1202 while (*x >= '0' && *x <= '9') x++; 1203 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; 1204 String8 xName(name, x-name); 1205 1206 if (out) { 1207 out->screenWidthDp = (uint16_t)atoi(xName.string()); 1208 } 1209 1210 return true; 1211} 1212 1213bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out) 1214{ 1215 if (strcmp(name, kWildcardName) == 0) { 1216 if (out) { 1217 out->screenHeightDp = out->SCREENWIDTH_ANY; 1218 } 1219 return true; 1220 } 1221 1222 if (*name != 'h') return false; 1223 name++; 1224 const char* x = name; 1225 while (*x >= '0' && *x <= '9') x++; 1226 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; 1227 String8 xName(name, x-name); 1228 1229 if (out) { 1230 out->screenHeightDp = (uint16_t)atoi(xName.string()); 1231 } 1232 1233 return true; 1234} 1235 1236bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out) 1237{ 1238 if (strcmp(name, kWildcardName) == 0) { 1239 if (out) { 1240 out->sdkVersion = out->SDKVERSION_ANY; 1241 out->minorVersion = out->MINORVERSION_ANY; 1242 } 1243 return true; 1244 } 1245 1246 if (*name != 'v') { 1247 return false; 1248 } 1249 1250 name++; 1251 const char* s = name; 1252 while (*s >= '0' && *s <= '9') s++; 1253 if (s == name || *s != 0) return false; 1254 String8 sdkName(name, s-name); 1255 1256 if (out) { 1257 out->sdkVersion = (uint16_t)atoi(sdkName.string()); 1258 out->minorVersion = 0; 1259 } 1260 1261 return true; 1262} 1263 1264int AaptGroupEntry::compare(const AaptGroupEntry& o) const 1265{ 1266 int v = mcc.compare(o.mcc); 1267 if (v == 0) v = mnc.compare(o.mnc); 1268 if (v == 0) v = locale.compare(o.locale); 1269 if (v == 0) v = vendor.compare(o.vendor); 1270 if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp); 1271 if (v == 0) v = screenWidthDp.compare(o.screenWidthDp); 1272 if (v == 0) v = screenHeightDp.compare(o.screenHeightDp); 1273 if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize); 1274 if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong); 1275 if (v == 0) v = orientation.compare(o.orientation); 1276 if (v == 0) v = uiModeType.compare(o.uiModeType); 1277 if (v == 0) v = uiModeNight.compare(o.uiModeNight); 1278 if (v == 0) v = density.compare(o.density); 1279 if (v == 0) v = touchscreen.compare(o.touchscreen); 1280 if (v == 0) v = keysHidden.compare(o.keysHidden); 1281 if (v == 0) v = keyboard.compare(o.keyboard); 1282 if (v == 0) v = navHidden.compare(o.navHidden); 1283 if (v == 0) v = navigation.compare(o.navigation); 1284 if (v == 0) v = screenSize.compare(o.screenSize); 1285 if (v == 0) v = version.compare(o.version); 1286 return v; 1287} 1288 1289ResTable_config AaptGroupEntry::toParams() const 1290{ 1291 ResTable_config params; 1292 memset(¶ms, 0, sizeof(params)); 1293 getMccName(mcc.string(), ¶ms); 1294 getMncName(mnc.string(), ¶ms); 1295 getLocaleName(locale.string(), ¶ms); 1296 getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), ¶ms); 1297 getScreenWidthDpName(screenWidthDp.string(), ¶ms); 1298 getScreenHeightDpName(screenHeightDp.string(), ¶ms); 1299 getScreenLayoutSizeName(screenLayoutSize.string(), ¶ms); 1300 getScreenLayoutLongName(screenLayoutLong.string(), ¶ms); 1301 getOrientationName(orientation.string(), ¶ms); 1302 getUiModeTypeName(uiModeType.string(), ¶ms); 1303 getUiModeNightName(uiModeNight.string(), ¶ms); 1304 getDensityName(density.string(), ¶ms); 1305 getTouchscreenName(touchscreen.string(), ¶ms); 1306 getKeysHiddenName(keysHidden.string(), ¶ms); 1307 getKeyboardName(keyboard.string(), ¶ms); 1308 getNavHiddenName(navHidden.string(), ¶ms); 1309 getNavigationName(navigation.string(), ¶ms); 1310 getScreenSizeName(screenSize.string(), ¶ms); 1311 getVersionName(version.string(), ¶ms); 1312 1313 // Fix up version number based on specified parameters. 1314 int minSdk = 0; 1315 if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY 1316 || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY 1317 || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) { 1318 minSdk = SDK_HONEYCOMB_MR2; 1319 } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE) 1320 != ResTable_config::UI_MODE_TYPE_ANY 1321 || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT) 1322 != ResTable_config::UI_MODE_NIGHT_ANY) { 1323 minSdk = SDK_FROYO; 1324 } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE) 1325 != ResTable_config::SCREENSIZE_ANY 1326 || (params.screenLayout&ResTable_config::MASK_SCREENLONG) 1327 != ResTable_config::SCREENLONG_ANY 1328 || params.density != ResTable_config::DENSITY_DEFAULT) { 1329 minSdk = SDK_DONUT; 1330 } 1331 1332 if (minSdk > params.sdkVersion) { 1333 params.sdkVersion = minSdk; 1334 } 1335 1336 return params; 1337} 1338 1339// ========================================================================= 1340// ========================================================================= 1341// ========================================================================= 1342 1343void* AaptFile::editData(size_t size) 1344{ 1345 if (size <= mBufferSize) { 1346 mDataSize = size; 1347 return mData; 1348 } 1349 size_t allocSize = (size*3)/2; 1350 void* buf = realloc(mData, allocSize); 1351 if (buf == NULL) { 1352 return NULL; 1353 } 1354 mData = buf; 1355 mDataSize = size; 1356 mBufferSize = allocSize; 1357 return buf; 1358} 1359 1360void* AaptFile::editData(size_t* outSize) 1361{ 1362 if (outSize) { 1363 *outSize = mDataSize; 1364 } 1365 return mData; 1366} 1367 1368void* AaptFile::padData(size_t wordSize) 1369{ 1370 const size_t extra = mDataSize%wordSize; 1371 if (extra == 0) { 1372 return mData; 1373 } 1374 1375 size_t initial = mDataSize; 1376 void* data = editData(initial+(wordSize-extra)); 1377 if (data != NULL) { 1378 memset(((uint8_t*)data) + initial, 0, wordSize-extra); 1379 } 1380 return data; 1381} 1382 1383status_t AaptFile::writeData(const void* data, size_t size) 1384{ 1385 size_t end = mDataSize; 1386 size_t total = size + end; 1387 void* buf = editData(total); 1388 if (buf == NULL) { 1389 return UNKNOWN_ERROR; 1390 } 1391 memcpy(((char*)buf)+end, data, size); 1392 return NO_ERROR; 1393} 1394 1395void AaptFile::clearData() 1396{ 1397 if (mData != NULL) free(mData); 1398 mData = NULL; 1399 mDataSize = 0; 1400 mBufferSize = 0; 1401} 1402 1403String8 AaptFile::getPrintableSource() const 1404{ 1405 if (hasData()) { 1406 String8 name(mGroupEntry.locale.string()); 1407 name.appendPath(mGroupEntry.vendor.string()); 1408 name.appendPath(mPath); 1409 name.append(" #generated"); 1410 return name; 1411 } 1412 return mSourceFile; 1413} 1414 1415// ========================================================================= 1416// ========================================================================= 1417// ========================================================================= 1418 1419status_t AaptGroup::addFile(const sp<AaptFile>& file) 1420{ 1421 if (mFiles.indexOfKey(file->getGroupEntry()) < 0) { 1422 file->mPath = mPath; 1423 mFiles.add(file->getGroupEntry(), file); 1424 return NO_ERROR; 1425 } 1426 1427 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.", 1428 getPrintableSource().string()); 1429 return UNKNOWN_ERROR; 1430} 1431 1432void AaptGroup::removeFile(size_t index) 1433{ 1434 mFiles.removeItemsAt(index); 1435} 1436 1437void AaptGroup::print() const 1438{ 1439 printf(" %s\n", getPath().string()); 1440 const size_t N=mFiles.size(); 1441 size_t i; 1442 for (i=0; i<N; i++) { 1443 sp<AaptFile> file = mFiles.valueAt(i); 1444 const AaptGroupEntry& e = file->getGroupEntry(); 1445 if (file->hasData()) { 1446 printf(" Gen: (%s) %d bytes\n", e.toString().string(), 1447 (int)file->getSize()); 1448 } else { 1449 printf(" Src: %s\n", file->getPrintableSource().string()); 1450 } 1451 } 1452} 1453 1454String8 AaptGroup::getPrintableSource() const 1455{ 1456 if (mFiles.size() > 0) { 1457 // Arbitrarily pull the first source file out of the list. 1458 return mFiles.valueAt(0)->getPrintableSource(); 1459 } 1460 1461 // Should never hit this case, but to be safe... 1462 return getPath(); 1463 1464} 1465 1466// ========================================================================= 1467// ========================================================================= 1468// ========================================================================= 1469 1470status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file) 1471{ 1472 if (mFiles.indexOfKey(name) >= 0) { 1473 return ALREADY_EXISTS; 1474 } 1475 mFiles.add(name, file); 1476 return NO_ERROR; 1477} 1478 1479status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir) 1480{ 1481 if (mDirs.indexOfKey(name) >= 0) { 1482 return ALREADY_EXISTS; 1483 } 1484 mDirs.add(name, dir); 1485 return NO_ERROR; 1486} 1487 1488sp<AaptDir> AaptDir::makeDir(const String8& path) 1489{ 1490 String8 name; 1491 String8 remain = path; 1492 1493 sp<AaptDir> subdir = this; 1494 while (name = remain.walkPath(&remain), remain != "") { 1495 subdir = subdir->makeDir(name); 1496 } 1497 1498 ssize_t i = subdir->mDirs.indexOfKey(name); 1499 if (i >= 0) { 1500 return subdir->mDirs.valueAt(i); 1501 } 1502 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name)); 1503 subdir->mDirs.add(name, dir); 1504 return dir; 1505} 1506 1507void AaptDir::removeFile(const String8& name) 1508{ 1509 mFiles.removeItem(name); 1510} 1511 1512void AaptDir::removeDir(const String8& name) 1513{ 1514 mDirs.removeItem(name); 1515} 1516 1517status_t AaptDir::renameFile(const sp<AaptFile>& file, const String8& newName) 1518{ 1519 sp<AaptGroup> origGroup; 1520 1521 // Find and remove the given file with shear, brute force! 1522 const size_t NG = mFiles.size(); 1523 size_t i; 1524 for (i=0; origGroup == NULL && i<NG; i++) { 1525 sp<AaptGroup> g = mFiles.valueAt(i); 1526 const size_t NF = g->getFiles().size(); 1527 for (size_t j=0; j<NF; j++) { 1528 if (g->getFiles().valueAt(j) == file) { 1529 origGroup = g; 1530 g->removeFile(j); 1531 if (NF == 1) { 1532 mFiles.removeItemsAt(i); 1533 } 1534 break; 1535 } 1536 } 1537 } 1538 1539 //printf("Renaming %s to %s\n", file->getPath().getPathName(), newName.string()); 1540 1541 // Place the file under its new name. 1542 if (origGroup != NULL) { 1543 return addLeafFile(newName, file); 1544 } 1545 1546 return NO_ERROR; 1547} 1548 1549status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file) 1550{ 1551 sp<AaptGroup> group; 1552 if (mFiles.indexOfKey(leafName) >= 0) { 1553 group = mFiles.valueFor(leafName); 1554 } else { 1555 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName)); 1556 mFiles.add(leafName, group); 1557 } 1558 1559 return group->addFile(file); 1560} 1561 1562ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir, 1563 const AaptGroupEntry& kind, const String8& resType) 1564{ 1565 Vector<String8> fileNames; 1566 1567 { 1568 DIR* dir = NULL; 1569 1570 dir = opendir(srcDir.string()); 1571 if (dir == NULL) { 1572 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno)); 1573 return UNKNOWN_ERROR; 1574 } 1575 1576 /* 1577 * Slurp the filenames out of the directory. 1578 */ 1579 while (1) { 1580 struct dirent* entry; 1581 1582 entry = readdir(dir); 1583 if (entry == NULL) 1584 break; 1585 1586 if (isHidden(srcDir.string(), entry->d_name)) 1587 continue; 1588 1589 fileNames.add(String8(entry->d_name)); 1590 } 1591 1592 closedir(dir); 1593 } 1594 1595 ssize_t count = 0; 1596 1597 /* 1598 * Stash away the files and recursively descend into subdirectories. 1599 */ 1600 const size_t N = fileNames.size(); 1601 size_t i; 1602 for (i = 0; i < N; i++) { 1603 String8 pathName(srcDir); 1604 FileType type; 1605 1606 pathName.appendPath(fileNames[i].string()); 1607 type = getFileType(pathName.string()); 1608 if (type == kFileTypeDirectory) { 1609 sp<AaptDir> subdir; 1610 bool notAdded = false; 1611 if (mDirs.indexOfKey(fileNames[i]) >= 0) { 1612 subdir = mDirs.valueFor(fileNames[i]); 1613 } else { 1614 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i])); 1615 notAdded = true; 1616 } 1617 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind, 1618 resType); 1619 if (res < NO_ERROR) { 1620 return res; 1621 } 1622 if (res > 0 && notAdded) { 1623 mDirs.add(fileNames[i], subdir); 1624 } 1625 count += res; 1626 } else if (type == kFileTypeRegular) { 1627 sp<AaptFile> file = new AaptFile(pathName, kind, resType); 1628 status_t err = addLeafFile(fileNames[i], file); 1629 if (err != NO_ERROR) { 1630 return err; 1631 } 1632 1633 count++; 1634 1635 } else { 1636 if (bundle->getVerbose()) 1637 printf(" (ignoring non-file/dir '%s')\n", pathName.string()); 1638 } 1639 } 1640 1641 return count; 1642} 1643 1644status_t AaptDir::validate() const 1645{ 1646 const size_t NF = mFiles.size(); 1647 const size_t ND = mDirs.size(); 1648 size_t i; 1649 for (i = 0; i < NF; i++) { 1650 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) { 1651 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( 1652 "Invalid filename. Unable to add."); 1653 return UNKNOWN_ERROR; 1654 } 1655 1656 size_t j; 1657 for (j = i+1; j < NF; j++) { 1658 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(), 1659 mFiles.valueAt(j)->getLeaf().string()) == 0) { 1660 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( 1661 "File is case-insensitive equivalent to: %s", 1662 mFiles.valueAt(j)->getPrintableSource().string()); 1663 return UNKNOWN_ERROR; 1664 } 1665 1666 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz" 1667 // (this is mostly caught by the "marked" stuff, below) 1668 } 1669 1670 for (j = 0; j < ND; j++) { 1671 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(), 1672 mDirs.valueAt(j)->getLeaf().string()) == 0) { 1673 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( 1674 "File conflicts with dir from: %s", 1675 mDirs.valueAt(j)->getPrintableSource().string()); 1676 return UNKNOWN_ERROR; 1677 } 1678 } 1679 } 1680 1681 for (i = 0; i < ND; i++) { 1682 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) { 1683 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error( 1684 "Invalid directory name, unable to add."); 1685 return UNKNOWN_ERROR; 1686 } 1687 1688 size_t j; 1689 for (j = i+1; j < ND; j++) { 1690 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(), 1691 mDirs.valueAt(j)->getLeaf().string()) == 0) { 1692 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error( 1693 "Directory is case-insensitive equivalent to: %s", 1694 mDirs.valueAt(j)->getPrintableSource().string()); 1695 return UNKNOWN_ERROR; 1696 } 1697 } 1698 1699 status_t err = mDirs.valueAt(i)->validate(); 1700 if (err != NO_ERROR) { 1701 return err; 1702 } 1703 } 1704 1705 return NO_ERROR; 1706} 1707 1708void AaptDir::print() const 1709{ 1710 const size_t ND=getDirs().size(); 1711 size_t i; 1712 for (i=0; i<ND; i++) { 1713 getDirs().valueAt(i)->print(); 1714 } 1715 1716 const size_t NF=getFiles().size(); 1717 for (i=0; i<NF; i++) { 1718 getFiles().valueAt(i)->print(); 1719 } 1720} 1721 1722String8 AaptDir::getPrintableSource() const 1723{ 1724 if (mFiles.size() > 0) { 1725 // Arbitrarily pull the first file out of the list as the source dir. 1726 return mFiles.valueAt(0)->getPrintableSource().getPathDir(); 1727 } 1728 if (mDirs.size() > 0) { 1729 // Or arbitrarily pull the first dir out of the list as the source dir. 1730 return mDirs.valueAt(0)->getPrintableSource().getPathDir(); 1731 } 1732 1733 // Should never hit this case, but to be safe... 1734 return mPath; 1735 1736} 1737 1738// ========================================================================= 1739// ========================================================================= 1740// ========================================================================= 1741 1742sp<AaptFile> AaptAssets::addFile( 1743 const String8& filePath, const AaptGroupEntry& entry, 1744 const String8& srcDir, sp<AaptGroup>* outGroup, 1745 const String8& resType) 1746{ 1747 sp<AaptDir> dir = this; 1748 sp<AaptGroup> group; 1749 sp<AaptFile> file; 1750 String8 root, remain(filePath), partialPath; 1751 while (remain.length() > 0) { 1752 root = remain.walkPath(&remain); 1753 partialPath.appendPath(root); 1754 1755 const String8 rootStr(root); 1756 1757 if (remain.length() == 0) { 1758 ssize_t i = dir->getFiles().indexOfKey(rootStr); 1759 if (i >= 0) { 1760 group = dir->getFiles().valueAt(i); 1761 } else { 1762 group = new AaptGroup(rootStr, filePath); 1763 status_t res = dir->addFile(rootStr, group); 1764 if (res != NO_ERROR) { 1765 return NULL; 1766 } 1767 } 1768 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType); 1769 status_t res = group->addFile(file); 1770 if (res != NO_ERROR) { 1771 return NULL; 1772 } 1773 break; 1774 1775 } else { 1776 ssize_t i = dir->getDirs().indexOfKey(rootStr); 1777 if (i >= 0) { 1778 dir = dir->getDirs().valueAt(i); 1779 } else { 1780 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath); 1781 status_t res = dir->addDir(rootStr, subdir); 1782 if (res != NO_ERROR) { 1783 return NULL; 1784 } 1785 dir = subdir; 1786 } 1787 } 1788 } 1789 1790 mGroupEntries.add(entry); 1791 if (outGroup) *outGroup = group; 1792 return file; 1793} 1794 1795void AaptAssets::addResource(const String8& leafName, const String8& path, 1796 const sp<AaptFile>& file, const String8& resType) 1797{ 1798 sp<AaptDir> res = AaptDir::makeDir(kResString); 1799 String8 dirname = file->getGroupEntry().toDirName(resType); 1800 sp<AaptDir> subdir = res->makeDir(dirname); 1801 sp<AaptGroup> grr = new AaptGroup(leafName, path); 1802 grr->addFile(file); 1803 1804 subdir->addFile(leafName, grr); 1805} 1806 1807 1808ssize_t AaptAssets::slurpFromArgs(Bundle* bundle) 1809{ 1810 int count; 1811 int totalCount = 0; 1812 FileType type; 1813 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs(); 1814 const size_t dirCount =resDirs.size(); 1815 sp<AaptAssets> current = this; 1816 1817 const int N = bundle->getFileSpecCount(); 1818 1819 /* 1820 * If a package manifest was specified, include that first. 1821 */ 1822 if (bundle->getAndroidManifestFile() != NULL) { 1823 // place at root of zip. 1824 String8 srcFile(bundle->getAndroidManifestFile()); 1825 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(), 1826 NULL, String8()); 1827 totalCount++; 1828 } 1829 1830 /* 1831 * If a directory of custom assets was supplied, slurp 'em up. 1832 */ 1833 if (bundle->getAssetSourceDir()) { 1834 const char* assetDir = bundle->getAssetSourceDir(); 1835 1836 FileType type = getFileType(assetDir); 1837 if (type == kFileTypeNonexistent) { 1838 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir); 1839 return UNKNOWN_ERROR; 1840 } 1841 if (type != kFileTypeDirectory) { 1842 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir); 1843 return UNKNOWN_ERROR; 1844 } 1845 1846 String8 assetRoot(assetDir); 1847 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir)); 1848 AaptGroupEntry group; 1849 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group, 1850 String8()); 1851 if (count < 0) { 1852 totalCount = count; 1853 goto bail; 1854 } 1855 if (count > 0) { 1856 mGroupEntries.add(group); 1857 } 1858 totalCount += count; 1859 1860 if (bundle->getVerbose()) 1861 printf("Found %d custom asset file%s in %s\n", 1862 count, (count==1) ? "" : "s", assetDir); 1863 } 1864 1865 /* 1866 * If a directory of resource-specific assets was supplied, slurp 'em up. 1867 */ 1868 for (size_t i=0; i<dirCount; i++) { 1869 const char *res = resDirs[i]; 1870 if (res) { 1871 type = getFileType(res); 1872 if (type == kFileTypeNonexistent) { 1873 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res); 1874 return UNKNOWN_ERROR; 1875 } 1876 if (type == kFileTypeDirectory) { 1877 if (i>0) { 1878 sp<AaptAssets> nextOverlay = new AaptAssets(); 1879 current->setOverlay(nextOverlay); 1880 current = nextOverlay; 1881 } 1882 count = current->slurpResourceTree(bundle, String8(res)); 1883 1884 if (count < 0) { 1885 totalCount = count; 1886 goto bail; 1887 } 1888 totalCount += count; 1889 } 1890 else { 1891 fprintf(stderr, "ERROR: '%s' is not a directory\n", res); 1892 return UNKNOWN_ERROR; 1893 } 1894 } 1895 1896 } 1897 /* 1898 * Now do any additional raw files. 1899 */ 1900 for (int arg=0; arg<N; arg++) { 1901 const char* assetDir = bundle->getFileSpecEntry(arg); 1902 1903 FileType type = getFileType(assetDir); 1904 if (type == kFileTypeNonexistent) { 1905 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir); 1906 return UNKNOWN_ERROR; 1907 } 1908 if (type != kFileTypeDirectory) { 1909 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir); 1910 return UNKNOWN_ERROR; 1911 } 1912 1913 String8 assetRoot(assetDir); 1914 1915 if (bundle->getVerbose()) 1916 printf("Processing raw dir '%s'\n", (const char*) assetDir); 1917 1918 /* 1919 * Do a recursive traversal of subdir tree. We don't make any 1920 * guarantees about ordering, so we're okay with an inorder search 1921 * using whatever order the OS happens to hand back to us. 1922 */ 1923 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8()); 1924 if (count < 0) { 1925 /* failure; report error and remove archive */ 1926 totalCount = count; 1927 goto bail; 1928 } 1929 totalCount += count; 1930 1931 if (bundle->getVerbose()) 1932 printf("Found %d asset file%s in %s\n", 1933 count, (count==1) ? "" : "s", assetDir); 1934 } 1935 1936 count = validate(); 1937 if (count != NO_ERROR) { 1938 totalCount = count; 1939 goto bail; 1940 } 1941 1942 1943bail: 1944 return totalCount; 1945} 1946 1947ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir, 1948 const AaptGroupEntry& kind, 1949 const String8& resType) 1950{ 1951 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType); 1952 if (res > 0) { 1953 mGroupEntries.add(kind); 1954 } 1955 1956 return res; 1957} 1958 1959ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir) 1960{ 1961 ssize_t err = 0; 1962 1963 DIR* dir = opendir(srcDir.string()); 1964 if (dir == NULL) { 1965 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno)); 1966 return UNKNOWN_ERROR; 1967 } 1968 1969 status_t count = 0; 1970 1971 /* 1972 * Run through the directory, looking for dirs that match the 1973 * expected pattern. 1974 */ 1975 while (1) { 1976 struct dirent* entry = readdir(dir); 1977 if (entry == NULL) { 1978 break; 1979 } 1980 1981 if (isHidden(srcDir.string(), entry->d_name)) { 1982 continue; 1983 } 1984 1985 String8 subdirName(srcDir); 1986 subdirName.appendPath(entry->d_name); 1987 1988 AaptGroupEntry group; 1989 String8 resType; 1990 bool b = group.initFromDirName(entry->d_name, &resType); 1991 if (!b) { 1992 fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(), 1993 entry->d_name); 1994 err = -1; 1995 continue; 1996 } 1997 1998 if (bundle->getMaxResVersion() != NULL && group.version.length() != 0) { 1999 int maxResInt = atoi(bundle->getMaxResVersion()); 2000 const char *verString = group.version.string(); 2001 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name 2002 if (dirVersionInt > maxResInt) { 2003 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name); 2004 continue; 2005 } 2006 } 2007 2008 FileType type = getFileType(subdirName.string()); 2009 2010 if (type == kFileTypeDirectory) { 2011 sp<AaptDir> dir = makeDir(String8(entry->d_name)); 2012 ssize_t res = dir->slurpFullTree(bundle, subdirName, group, 2013 resType); 2014 if (res < 0) { 2015 count = res; 2016 goto bail; 2017 } 2018 if (res > 0) { 2019 mGroupEntries.add(group); 2020 count += res; 2021 } 2022 2023 mDirs.add(dir); 2024 } else { 2025 if (bundle->getVerbose()) { 2026 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string()); 2027 } 2028 } 2029 } 2030 2031bail: 2032 closedir(dir); 2033 dir = NULL; 2034 2035 if (err != 0) { 2036 return err; 2037 } 2038 return count; 2039} 2040 2041ssize_t 2042AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename) 2043{ 2044 int count = 0; 2045 SortedVector<AaptGroupEntry> entries; 2046 2047 ZipFile* zip = new ZipFile; 2048 status_t err = zip->open(filename, ZipFile::kOpenReadOnly); 2049 if (err != NO_ERROR) { 2050 fprintf(stderr, "error opening zip file %s\n", filename); 2051 count = err; 2052 delete zip; 2053 return -1; 2054 } 2055 2056 const int N = zip->getNumEntries(); 2057 for (int i=0; i<N; i++) { 2058 ZipEntry* entry = zip->getEntryByIndex(i); 2059 if (entry->getDeleted()) { 2060 continue; 2061 } 2062 2063 String8 entryName(entry->getFileName()); 2064 2065 String8 dirName = entryName.getPathDir(); 2066 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName); 2067 2068 String8 resType; 2069 AaptGroupEntry kind; 2070 2071 String8 remain; 2072 if (entryName.walkPath(&remain) == kResourceDir) { 2073 // these are the resources, pull their type out of the directory name 2074 kind.initFromDirName(remain.walkPath().string(), &resType); 2075 } else { 2076 // these are untyped and don't have an AaptGroupEntry 2077 } 2078 if (entries.indexOf(kind) < 0) { 2079 entries.add(kind); 2080 mGroupEntries.add(kind); 2081 } 2082 2083 // use the one from the zip file if they both exist. 2084 dir->removeFile(entryName.getPathLeaf()); 2085 2086 sp<AaptFile> file = new AaptFile(entryName, kind, resType); 2087 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file); 2088 if (err != NO_ERROR) { 2089 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string()); 2090 count = err; 2091 goto bail; 2092 } 2093 file->setCompressionMethod(entry->getCompressionMethod()); 2094 2095#if 0 2096 if (entryName == "AndroidManifest.xml") { 2097 printf("AndroidManifest.xml\n"); 2098 } 2099 printf("\n\nfile: %s\n", entryName.string()); 2100#endif 2101 2102 size_t len = entry->getUncompressedLen(); 2103 void* data = zip->uncompress(entry); 2104 void* buf = file->editData(len); 2105 memcpy(buf, data, len); 2106 2107#if 0 2108 const int OFF = 0; 2109 const unsigned char* p = (unsigned char*)data; 2110 const unsigned char* end = p+len; 2111 p += OFF; 2112 for (int i=0; i<32 && p < end; i++) { 2113 printf("0x%03x ", i*0x10 + OFF); 2114 for (int j=0; j<0x10 && p < end; j++) { 2115 printf(" %02x", *p); 2116 p++; 2117 } 2118 printf("\n"); 2119 } 2120#endif 2121 2122 free(data); 2123 2124 count++; 2125 } 2126 2127bail: 2128 delete zip; 2129 return count; 2130} 2131 2132sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name) 2133{ 2134 sp<AaptSymbols> sym = mSymbols.valueFor(name); 2135 if (sym == NULL) { 2136 sym = new AaptSymbols(); 2137 mSymbols.add(name, sym); 2138 } 2139 return sym; 2140} 2141 2142status_t AaptAssets::buildIncludedResources(Bundle* bundle) 2143{ 2144 if (!mHaveIncludedAssets) { 2145 // Add in all includes. 2146 const Vector<const char*>& incl = bundle->getPackageIncludes(); 2147 const size_t N=incl.size(); 2148 for (size_t i=0; i<N; i++) { 2149 if (bundle->getVerbose()) 2150 printf("Including resources from package: %s\n", incl[i]); 2151 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) { 2152 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n", 2153 incl[i]); 2154 return UNKNOWN_ERROR; 2155 } 2156 } 2157 mHaveIncludedAssets = true; 2158 } 2159 2160 return NO_ERROR; 2161} 2162 2163status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file) 2164{ 2165 const ResTable& res = getIncludedResources(); 2166 // XXX dirty! 2167 return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL); 2168} 2169 2170const ResTable& AaptAssets::getIncludedResources() const 2171{ 2172 return mIncludedAssets.getResources(false); 2173} 2174 2175void AaptAssets::print() const 2176{ 2177 printf("Locale/Vendor pairs:\n"); 2178 const size_t N=mGroupEntries.size(); 2179 for (size_t i=0; i<N; i++) { 2180 printf(" %s/%s\n", 2181 mGroupEntries.itemAt(i).locale.string(), 2182 mGroupEntries.itemAt(i).vendor.string()); 2183 } 2184 2185 printf("\nFiles:\n"); 2186 AaptDir::print(); 2187} 2188 2189sp<AaptDir> AaptAssets::resDir(const String8& name) 2190{ 2191 const Vector<sp<AaptDir> >& dirs = mDirs; 2192 const size_t N = dirs.size(); 2193 for (size_t i=0; i<N; i++) { 2194 const sp<AaptDir>& d = dirs.itemAt(i); 2195 if (d->getLeaf() == name) { 2196 return d; 2197 } 2198 } 2199 return NULL; 2200} 2201 2202bool 2203valid_symbol_name(const String8& symbol) 2204{ 2205 static char const * const KEYWORDS[] = { 2206 "abstract", "assert", "boolean", "break", 2207 "byte", "case", "catch", "char", "class", "const", "continue", 2208 "default", "do", "double", "else", "enum", "extends", "final", 2209 "finally", "float", "for", "goto", "if", "implements", "import", 2210 "instanceof", "int", "interface", "long", "native", "new", "package", 2211 "private", "protected", "public", "return", "short", "static", 2212 "strictfp", "super", "switch", "synchronized", "this", "throw", 2213 "throws", "transient", "try", "void", "volatile", "while", 2214 "true", "false", "null", 2215 NULL 2216 }; 2217 const char*const* k = KEYWORDS; 2218 const char*const s = symbol.string(); 2219 while (*k) { 2220 if (0 == strcmp(s, *k)) { 2221 return false; 2222 } 2223 k++; 2224 } 2225 return true; 2226} 2227