AaptAssets.cpp revision e360bb6bd8ea84b8d37604fc11dfc744b18fb0de
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, "hdpi") == 0) { 973 if (out) out->density = ResTable_config::DENSITY_HIGH; 974 return true; 975 } 976 977 if (strcmp(name, "xhdpi") == 0) { 978 if (out) out->density = ResTable_config::DENSITY_MEDIUM*2; 979 return true; 980 } 981 982 char* c = (char*)name; 983 while (*c >= '0' && *c <= '9') { 984 c++; 985 } 986 987 // check that we have 'dpi' after the last digit. 988 if (toupper(c[0]) != 'D' || 989 toupper(c[1]) != 'P' || 990 toupper(c[2]) != 'I' || 991 c[3] != 0) { 992 return false; 993 } 994 995 // temporarily replace the first letter with \0 to 996 // use atoi. 997 char tmp = c[0]; 998 c[0] = '\0'; 999 1000 int d = atoi(name); 1001 c[0] = tmp; 1002 1003 if (d != 0) { 1004 if (out) out->density = d; 1005 return true; 1006 } 1007 1008 return false; 1009} 1010 1011bool AaptGroupEntry::getTouchscreenName(const char* name, 1012 ResTable_config* out) 1013{ 1014 if (strcmp(name, kWildcardName) == 0) { 1015 if (out) out->touchscreen = out->TOUCHSCREEN_ANY; 1016 return true; 1017 } else if (strcmp(name, "notouch") == 0) { 1018 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH; 1019 return true; 1020 } else if (strcmp(name, "stylus") == 0) { 1021 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS; 1022 return true; 1023 } else if (strcmp(name, "finger") == 0) { 1024 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER; 1025 return true; 1026 } 1027 1028 return false; 1029} 1030 1031bool AaptGroupEntry::getKeysHiddenName(const char* name, 1032 ResTable_config* out) 1033{ 1034 uint8_t mask = 0; 1035 uint8_t value = 0; 1036 if (strcmp(name, kWildcardName) == 0) { 1037 mask = ResTable_config::MASK_KEYSHIDDEN; 1038 value = ResTable_config::KEYSHIDDEN_ANY; 1039 } else if (strcmp(name, "keysexposed") == 0) { 1040 mask = ResTable_config::MASK_KEYSHIDDEN; 1041 value = ResTable_config::KEYSHIDDEN_NO; 1042 } else if (strcmp(name, "keyshidden") == 0) { 1043 mask = ResTable_config::MASK_KEYSHIDDEN; 1044 value = ResTable_config::KEYSHIDDEN_YES; 1045 } else if (strcmp(name, "keyssoft") == 0) { 1046 mask = ResTable_config::MASK_KEYSHIDDEN; 1047 value = ResTable_config::KEYSHIDDEN_SOFT; 1048 } 1049 1050 if (mask != 0) { 1051 if (out) out->inputFlags = (out->inputFlags&~mask) | value; 1052 return true; 1053 } 1054 1055 return false; 1056} 1057 1058bool AaptGroupEntry::getKeyboardName(const char* name, 1059 ResTable_config* out) 1060{ 1061 if (strcmp(name, kWildcardName) == 0) { 1062 if (out) out->keyboard = out->KEYBOARD_ANY; 1063 return true; 1064 } else if (strcmp(name, "nokeys") == 0) { 1065 if (out) out->keyboard = out->KEYBOARD_NOKEYS; 1066 return true; 1067 } else if (strcmp(name, "qwerty") == 0) { 1068 if (out) out->keyboard = out->KEYBOARD_QWERTY; 1069 return true; 1070 } else if (strcmp(name, "12key") == 0) { 1071 if (out) out->keyboard = out->KEYBOARD_12KEY; 1072 return true; 1073 } 1074 1075 return false; 1076} 1077 1078bool AaptGroupEntry::getNavHiddenName(const char* name, 1079 ResTable_config* out) 1080{ 1081 uint8_t mask = 0; 1082 uint8_t value = 0; 1083 if (strcmp(name, kWildcardName) == 0) { 1084 mask = ResTable_config::MASK_NAVHIDDEN; 1085 value = ResTable_config::NAVHIDDEN_ANY; 1086 } else if (strcmp(name, "navexposed") == 0) { 1087 mask = ResTable_config::MASK_NAVHIDDEN; 1088 value = ResTable_config::NAVHIDDEN_NO; 1089 } else if (strcmp(name, "navhidden") == 0) { 1090 mask = ResTable_config::MASK_NAVHIDDEN; 1091 value = ResTable_config::NAVHIDDEN_YES; 1092 } 1093 1094 if (mask != 0) { 1095 if (out) out->inputFlags = (out->inputFlags&~mask) | value; 1096 return true; 1097 } 1098 1099 return false; 1100} 1101 1102bool AaptGroupEntry::getNavigationName(const char* name, 1103 ResTable_config* out) 1104{ 1105 if (strcmp(name, kWildcardName) == 0) { 1106 if (out) out->navigation = out->NAVIGATION_ANY; 1107 return true; 1108 } else if (strcmp(name, "nonav") == 0) { 1109 if (out) out->navigation = out->NAVIGATION_NONAV; 1110 return true; 1111 } else if (strcmp(name, "dpad") == 0) { 1112 if (out) out->navigation = out->NAVIGATION_DPAD; 1113 return true; 1114 } else if (strcmp(name, "trackball") == 0) { 1115 if (out) out->navigation = out->NAVIGATION_TRACKBALL; 1116 return true; 1117 } else if (strcmp(name, "wheel") == 0) { 1118 if (out) out->navigation = out->NAVIGATION_WHEEL; 1119 return true; 1120 } 1121 1122 return false; 1123} 1124 1125bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out) 1126{ 1127 if (strcmp(name, kWildcardName) == 0) { 1128 if (out) { 1129 out->screenWidth = out->SCREENWIDTH_ANY; 1130 out->screenHeight = out->SCREENHEIGHT_ANY; 1131 } 1132 return true; 1133 } 1134 1135 const char* x = name; 1136 while (*x >= '0' && *x <= '9') x++; 1137 if (x == name || *x != 'x') return false; 1138 String8 xName(name, x-name); 1139 x++; 1140 1141 const char* y = x; 1142 while (*y >= '0' && *y <= '9') y++; 1143 if (y == name || *y != 0) return false; 1144 String8 yName(x, y-x); 1145 1146 uint16_t w = (uint16_t)atoi(xName.string()); 1147 uint16_t h = (uint16_t)atoi(yName.string()); 1148 if (w < h) { 1149 return false; 1150 } 1151 1152 if (out) { 1153 out->screenWidth = w; 1154 out->screenHeight = h; 1155 } 1156 1157 return true; 1158} 1159 1160bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out) 1161{ 1162 if (strcmp(name, kWildcardName) == 0) { 1163 if (out) { 1164 out->smallestScreenWidthDp = out->SCREENWIDTH_ANY; 1165 } 1166 return true; 1167 } 1168 1169 if (*name != 's') return false; 1170 name++; 1171 if (*name != 'w') return false; 1172 name++; 1173 const char* x = name; 1174 while (*x >= '0' && *x <= '9') x++; 1175 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; 1176 String8 xName(name, x-name); 1177 1178 if (out) { 1179 out->smallestScreenWidthDp = (uint16_t)atoi(xName.string()); 1180 } 1181 1182 return true; 1183} 1184 1185bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out) 1186{ 1187 if (strcmp(name, kWildcardName) == 0) { 1188 if (out) { 1189 out->screenWidthDp = out->SCREENWIDTH_ANY; 1190 } 1191 return true; 1192 } 1193 1194 if (*name != 'w') return false; 1195 name++; 1196 const char* x = name; 1197 while (*x >= '0' && *x <= '9') x++; 1198 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; 1199 String8 xName(name, x-name); 1200 1201 if (out) { 1202 out->screenWidthDp = (uint16_t)atoi(xName.string()); 1203 } 1204 1205 return true; 1206} 1207 1208bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out) 1209{ 1210 if (strcmp(name, kWildcardName) == 0) { 1211 if (out) { 1212 out->screenHeightDp = out->SCREENWIDTH_ANY; 1213 } 1214 return true; 1215 } 1216 1217 if (*name != 'h') return false; 1218 name++; 1219 const char* x = name; 1220 while (*x >= '0' && *x <= '9') x++; 1221 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; 1222 String8 xName(name, x-name); 1223 1224 if (out) { 1225 out->screenHeightDp = (uint16_t)atoi(xName.string()); 1226 } 1227 1228 return true; 1229} 1230 1231bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out) 1232{ 1233 if (strcmp(name, kWildcardName) == 0) { 1234 if (out) { 1235 out->sdkVersion = out->SDKVERSION_ANY; 1236 out->minorVersion = out->MINORVERSION_ANY; 1237 } 1238 return true; 1239 } 1240 1241 if (*name != 'v') { 1242 return false; 1243 } 1244 1245 name++; 1246 const char* s = name; 1247 while (*s >= '0' && *s <= '9') s++; 1248 if (s == name || *s != 0) return false; 1249 String8 sdkName(name, s-name); 1250 1251 if (out) { 1252 out->sdkVersion = (uint16_t)atoi(sdkName.string()); 1253 out->minorVersion = 0; 1254 } 1255 1256 return true; 1257} 1258 1259int AaptGroupEntry::compare(const AaptGroupEntry& o) const 1260{ 1261 int v = mcc.compare(o.mcc); 1262 if (v == 0) v = mnc.compare(o.mnc); 1263 if (v == 0) v = locale.compare(o.locale); 1264 if (v == 0) v = vendor.compare(o.vendor); 1265 if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp); 1266 if (v == 0) v = screenWidthDp.compare(o.screenWidthDp); 1267 if (v == 0) v = screenHeightDp.compare(o.screenHeightDp); 1268 if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize); 1269 if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong); 1270 if (v == 0) v = orientation.compare(o.orientation); 1271 if (v == 0) v = uiModeType.compare(o.uiModeType); 1272 if (v == 0) v = uiModeNight.compare(o.uiModeNight); 1273 if (v == 0) v = density.compare(o.density); 1274 if (v == 0) v = touchscreen.compare(o.touchscreen); 1275 if (v == 0) v = keysHidden.compare(o.keysHidden); 1276 if (v == 0) v = keyboard.compare(o.keyboard); 1277 if (v == 0) v = navHidden.compare(o.navHidden); 1278 if (v == 0) v = navigation.compare(o.navigation); 1279 if (v == 0) v = screenSize.compare(o.screenSize); 1280 if (v == 0) v = version.compare(o.version); 1281 return v; 1282} 1283 1284ResTable_config AaptGroupEntry::toParams() const 1285{ 1286 ResTable_config params; 1287 memset(¶ms, 0, sizeof(params)); 1288 getMccName(mcc.string(), ¶ms); 1289 getMncName(mnc.string(), ¶ms); 1290 getLocaleName(locale.string(), ¶ms); 1291 getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), ¶ms); 1292 getScreenWidthDpName(screenWidthDp.string(), ¶ms); 1293 getScreenHeightDpName(screenHeightDp.string(), ¶ms); 1294 getScreenLayoutSizeName(screenLayoutSize.string(), ¶ms); 1295 getScreenLayoutLongName(screenLayoutLong.string(), ¶ms); 1296 getOrientationName(orientation.string(), ¶ms); 1297 getUiModeTypeName(uiModeType.string(), ¶ms); 1298 getUiModeNightName(uiModeNight.string(), ¶ms); 1299 getDensityName(density.string(), ¶ms); 1300 getTouchscreenName(touchscreen.string(), ¶ms); 1301 getKeysHiddenName(keysHidden.string(), ¶ms); 1302 getKeyboardName(keyboard.string(), ¶ms); 1303 getNavHiddenName(navHidden.string(), ¶ms); 1304 getNavigationName(navigation.string(), ¶ms); 1305 getScreenSizeName(screenSize.string(), ¶ms); 1306 getVersionName(version.string(), ¶ms); 1307 1308 // Fix up version number based on specified parameters. 1309 int minSdk = 0; 1310 if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY 1311 || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY 1312 || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) { 1313 minSdk = SDK_HONEYCOMB_MR2; 1314 } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE) 1315 != ResTable_config::UI_MODE_TYPE_ANY 1316 || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT) 1317 != ResTable_config::UI_MODE_NIGHT_ANY) { 1318 minSdk = SDK_FROYO; 1319 } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE) 1320 != ResTable_config::SCREENSIZE_ANY 1321 || (params.screenLayout&ResTable_config::MASK_SCREENLONG) 1322 != ResTable_config::SCREENLONG_ANY 1323 || params.density != ResTable_config::DENSITY_DEFAULT) { 1324 minSdk = SDK_DONUT; 1325 } 1326 1327 if (minSdk > params.sdkVersion) { 1328 params.sdkVersion = minSdk; 1329 } 1330 1331 return params; 1332} 1333 1334// ========================================================================= 1335// ========================================================================= 1336// ========================================================================= 1337 1338void* AaptFile::editData(size_t size) 1339{ 1340 if (size <= mBufferSize) { 1341 mDataSize = size; 1342 return mData; 1343 } 1344 size_t allocSize = (size*3)/2; 1345 void* buf = realloc(mData, allocSize); 1346 if (buf == NULL) { 1347 return NULL; 1348 } 1349 mData = buf; 1350 mDataSize = size; 1351 mBufferSize = allocSize; 1352 return buf; 1353} 1354 1355void* AaptFile::editData(size_t* outSize) 1356{ 1357 if (outSize) { 1358 *outSize = mDataSize; 1359 } 1360 return mData; 1361} 1362 1363void* AaptFile::padData(size_t wordSize) 1364{ 1365 const size_t extra = mDataSize%wordSize; 1366 if (extra == 0) { 1367 return mData; 1368 } 1369 1370 size_t initial = mDataSize; 1371 void* data = editData(initial+(wordSize-extra)); 1372 if (data != NULL) { 1373 memset(((uint8_t*)data) + initial, 0, wordSize-extra); 1374 } 1375 return data; 1376} 1377 1378status_t AaptFile::writeData(const void* data, size_t size) 1379{ 1380 size_t end = mDataSize; 1381 size_t total = size + end; 1382 void* buf = editData(total); 1383 if (buf == NULL) { 1384 return UNKNOWN_ERROR; 1385 } 1386 memcpy(((char*)buf)+end, data, size); 1387 return NO_ERROR; 1388} 1389 1390void AaptFile::clearData() 1391{ 1392 if (mData != NULL) free(mData); 1393 mData = NULL; 1394 mDataSize = 0; 1395 mBufferSize = 0; 1396} 1397 1398String8 AaptFile::getPrintableSource() const 1399{ 1400 if (hasData()) { 1401 String8 name(mGroupEntry.locale.string()); 1402 name.appendPath(mGroupEntry.vendor.string()); 1403 name.appendPath(mPath); 1404 name.append(" #generated"); 1405 return name; 1406 } 1407 return mSourceFile; 1408} 1409 1410// ========================================================================= 1411// ========================================================================= 1412// ========================================================================= 1413 1414status_t AaptGroup::addFile(const sp<AaptFile>& file) 1415{ 1416 if (mFiles.indexOfKey(file->getGroupEntry()) < 0) { 1417 file->mPath = mPath; 1418 mFiles.add(file->getGroupEntry(), file); 1419 return NO_ERROR; 1420 } 1421 1422 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.", 1423 getPrintableSource().string()); 1424 return UNKNOWN_ERROR; 1425} 1426 1427void AaptGroup::removeFile(size_t index) 1428{ 1429 mFiles.removeItemsAt(index); 1430} 1431 1432void AaptGroup::print() const 1433{ 1434 printf(" %s\n", getPath().string()); 1435 const size_t N=mFiles.size(); 1436 size_t i; 1437 for (i=0; i<N; i++) { 1438 sp<AaptFile> file = mFiles.valueAt(i); 1439 const AaptGroupEntry& e = file->getGroupEntry(); 1440 if (file->hasData()) { 1441 printf(" Gen: (%s) %d bytes\n", e.toString().string(), 1442 (int)file->getSize()); 1443 } else { 1444 printf(" Src: %s\n", file->getPrintableSource().string()); 1445 } 1446 } 1447} 1448 1449String8 AaptGroup::getPrintableSource() const 1450{ 1451 if (mFiles.size() > 0) { 1452 // Arbitrarily pull the first source file out of the list. 1453 return mFiles.valueAt(0)->getPrintableSource(); 1454 } 1455 1456 // Should never hit this case, but to be safe... 1457 return getPath(); 1458 1459} 1460 1461// ========================================================================= 1462// ========================================================================= 1463// ========================================================================= 1464 1465status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file) 1466{ 1467 if (mFiles.indexOfKey(name) >= 0) { 1468 return ALREADY_EXISTS; 1469 } 1470 mFiles.add(name, file); 1471 return NO_ERROR; 1472} 1473 1474status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir) 1475{ 1476 if (mDirs.indexOfKey(name) >= 0) { 1477 return ALREADY_EXISTS; 1478 } 1479 mDirs.add(name, dir); 1480 return NO_ERROR; 1481} 1482 1483sp<AaptDir> AaptDir::makeDir(const String8& path) 1484{ 1485 String8 name; 1486 String8 remain = path; 1487 1488 sp<AaptDir> subdir = this; 1489 while (name = remain.walkPath(&remain), remain != "") { 1490 subdir = subdir->makeDir(name); 1491 } 1492 1493 ssize_t i = subdir->mDirs.indexOfKey(name); 1494 if (i >= 0) { 1495 return subdir->mDirs.valueAt(i); 1496 } 1497 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name)); 1498 subdir->mDirs.add(name, dir); 1499 return dir; 1500} 1501 1502void AaptDir::removeFile(const String8& name) 1503{ 1504 mFiles.removeItem(name); 1505} 1506 1507void AaptDir::removeDir(const String8& name) 1508{ 1509 mDirs.removeItem(name); 1510} 1511 1512status_t AaptDir::renameFile(const sp<AaptFile>& file, const String8& newName) 1513{ 1514 sp<AaptGroup> origGroup; 1515 1516 // Find and remove the given file with shear, brute force! 1517 const size_t NG = mFiles.size(); 1518 size_t i; 1519 for (i=0; origGroup == NULL && i<NG; i++) { 1520 sp<AaptGroup> g = mFiles.valueAt(i); 1521 const size_t NF = g->getFiles().size(); 1522 for (size_t j=0; j<NF; j++) { 1523 if (g->getFiles().valueAt(j) == file) { 1524 origGroup = g; 1525 g->removeFile(j); 1526 if (NF == 1) { 1527 mFiles.removeItemsAt(i); 1528 } 1529 break; 1530 } 1531 } 1532 } 1533 1534 //printf("Renaming %s to %s\n", file->getPath().getPathName(), newName.string()); 1535 1536 // Place the file under its new name. 1537 if (origGroup != NULL) { 1538 return addLeafFile(newName, file); 1539 } 1540 1541 return NO_ERROR; 1542} 1543 1544status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file) 1545{ 1546 sp<AaptGroup> group; 1547 if (mFiles.indexOfKey(leafName) >= 0) { 1548 group = mFiles.valueFor(leafName); 1549 } else { 1550 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName)); 1551 mFiles.add(leafName, group); 1552 } 1553 1554 return group->addFile(file); 1555} 1556 1557ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir, 1558 const AaptGroupEntry& kind, const String8& resType) 1559{ 1560 Vector<String8> fileNames; 1561 1562 { 1563 DIR* dir = NULL; 1564 1565 dir = opendir(srcDir.string()); 1566 if (dir == NULL) { 1567 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno)); 1568 return UNKNOWN_ERROR; 1569 } 1570 1571 /* 1572 * Slurp the filenames out of the directory. 1573 */ 1574 while (1) { 1575 struct dirent* entry; 1576 1577 entry = readdir(dir); 1578 if (entry == NULL) 1579 break; 1580 1581 if (isHidden(srcDir.string(), entry->d_name)) 1582 continue; 1583 1584 fileNames.add(String8(entry->d_name)); 1585 } 1586 1587 closedir(dir); 1588 } 1589 1590 ssize_t count = 0; 1591 1592 /* 1593 * Stash away the files and recursively descend into subdirectories. 1594 */ 1595 const size_t N = fileNames.size(); 1596 size_t i; 1597 for (i = 0; i < N; i++) { 1598 String8 pathName(srcDir); 1599 FileType type; 1600 1601 pathName.appendPath(fileNames[i].string()); 1602 type = getFileType(pathName.string()); 1603 if (type == kFileTypeDirectory) { 1604 sp<AaptDir> subdir; 1605 bool notAdded = false; 1606 if (mDirs.indexOfKey(fileNames[i]) >= 0) { 1607 subdir = mDirs.valueFor(fileNames[i]); 1608 } else { 1609 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i])); 1610 notAdded = true; 1611 } 1612 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind, 1613 resType); 1614 if (res < NO_ERROR) { 1615 return res; 1616 } 1617 if (res > 0 && notAdded) { 1618 mDirs.add(fileNames[i], subdir); 1619 } 1620 count += res; 1621 } else if (type == kFileTypeRegular) { 1622 sp<AaptFile> file = new AaptFile(pathName, kind, resType); 1623 status_t err = addLeafFile(fileNames[i], file); 1624 if (err != NO_ERROR) { 1625 return err; 1626 } 1627 1628 count++; 1629 1630 } else { 1631 if (bundle->getVerbose()) 1632 printf(" (ignoring non-file/dir '%s')\n", pathName.string()); 1633 } 1634 } 1635 1636 return count; 1637} 1638 1639status_t AaptDir::validate() const 1640{ 1641 const size_t NF = mFiles.size(); 1642 const size_t ND = mDirs.size(); 1643 size_t i; 1644 for (i = 0; i < NF; i++) { 1645 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) { 1646 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( 1647 "Invalid filename. Unable to add."); 1648 return UNKNOWN_ERROR; 1649 } 1650 1651 size_t j; 1652 for (j = i+1; j < NF; j++) { 1653 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(), 1654 mFiles.valueAt(j)->getLeaf().string()) == 0) { 1655 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( 1656 "File is case-insensitive equivalent to: %s", 1657 mFiles.valueAt(j)->getPrintableSource().string()); 1658 return UNKNOWN_ERROR; 1659 } 1660 1661 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz" 1662 // (this is mostly caught by the "marked" stuff, below) 1663 } 1664 1665 for (j = 0; j < ND; j++) { 1666 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(), 1667 mDirs.valueAt(j)->getLeaf().string()) == 0) { 1668 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( 1669 "File conflicts with dir from: %s", 1670 mDirs.valueAt(j)->getPrintableSource().string()); 1671 return UNKNOWN_ERROR; 1672 } 1673 } 1674 } 1675 1676 for (i = 0; i < ND; i++) { 1677 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) { 1678 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error( 1679 "Invalid directory name, unable to add."); 1680 return UNKNOWN_ERROR; 1681 } 1682 1683 size_t j; 1684 for (j = i+1; j < ND; j++) { 1685 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(), 1686 mDirs.valueAt(j)->getLeaf().string()) == 0) { 1687 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error( 1688 "Directory is case-insensitive equivalent to: %s", 1689 mDirs.valueAt(j)->getPrintableSource().string()); 1690 return UNKNOWN_ERROR; 1691 } 1692 } 1693 1694 status_t err = mDirs.valueAt(i)->validate(); 1695 if (err != NO_ERROR) { 1696 return err; 1697 } 1698 } 1699 1700 return NO_ERROR; 1701} 1702 1703void AaptDir::print() const 1704{ 1705 const size_t ND=getDirs().size(); 1706 size_t i; 1707 for (i=0; i<ND; i++) { 1708 getDirs().valueAt(i)->print(); 1709 } 1710 1711 const size_t NF=getFiles().size(); 1712 for (i=0; i<NF; i++) { 1713 getFiles().valueAt(i)->print(); 1714 } 1715} 1716 1717String8 AaptDir::getPrintableSource() const 1718{ 1719 if (mFiles.size() > 0) { 1720 // Arbitrarily pull the first file out of the list as the source dir. 1721 return mFiles.valueAt(0)->getPrintableSource().getPathDir(); 1722 } 1723 if (mDirs.size() > 0) { 1724 // Or arbitrarily pull the first dir out of the list as the source dir. 1725 return mDirs.valueAt(0)->getPrintableSource().getPathDir(); 1726 } 1727 1728 // Should never hit this case, but to be safe... 1729 return mPath; 1730 1731} 1732 1733// ========================================================================= 1734// ========================================================================= 1735// ========================================================================= 1736 1737sp<AaptFile> AaptAssets::addFile( 1738 const String8& filePath, const AaptGroupEntry& entry, 1739 const String8& srcDir, sp<AaptGroup>* outGroup, 1740 const String8& resType) 1741{ 1742 sp<AaptDir> dir = this; 1743 sp<AaptGroup> group; 1744 sp<AaptFile> file; 1745 String8 root, remain(filePath), partialPath; 1746 while (remain.length() > 0) { 1747 root = remain.walkPath(&remain); 1748 partialPath.appendPath(root); 1749 1750 const String8 rootStr(root); 1751 1752 if (remain.length() == 0) { 1753 ssize_t i = dir->getFiles().indexOfKey(rootStr); 1754 if (i >= 0) { 1755 group = dir->getFiles().valueAt(i); 1756 } else { 1757 group = new AaptGroup(rootStr, filePath); 1758 status_t res = dir->addFile(rootStr, group); 1759 if (res != NO_ERROR) { 1760 return NULL; 1761 } 1762 } 1763 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType); 1764 status_t res = group->addFile(file); 1765 if (res != NO_ERROR) { 1766 return NULL; 1767 } 1768 break; 1769 1770 } else { 1771 ssize_t i = dir->getDirs().indexOfKey(rootStr); 1772 if (i >= 0) { 1773 dir = dir->getDirs().valueAt(i); 1774 } else { 1775 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath); 1776 status_t res = dir->addDir(rootStr, subdir); 1777 if (res != NO_ERROR) { 1778 return NULL; 1779 } 1780 dir = subdir; 1781 } 1782 } 1783 } 1784 1785 mGroupEntries.add(entry); 1786 if (outGroup) *outGroup = group; 1787 return file; 1788} 1789 1790void AaptAssets::addResource(const String8& leafName, const String8& path, 1791 const sp<AaptFile>& file, const String8& resType) 1792{ 1793 sp<AaptDir> res = AaptDir::makeDir(kResString); 1794 String8 dirname = file->getGroupEntry().toDirName(resType); 1795 sp<AaptDir> subdir = res->makeDir(dirname); 1796 sp<AaptGroup> grr = new AaptGroup(leafName, path); 1797 grr->addFile(file); 1798 1799 subdir->addFile(leafName, grr); 1800} 1801 1802 1803ssize_t AaptAssets::slurpFromArgs(Bundle* bundle) 1804{ 1805 int count; 1806 int totalCount = 0; 1807 FileType type; 1808 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs(); 1809 const size_t dirCount =resDirs.size(); 1810 sp<AaptAssets> current = this; 1811 1812 const int N = bundle->getFileSpecCount(); 1813 1814 /* 1815 * If a package manifest was specified, include that first. 1816 */ 1817 if (bundle->getAndroidManifestFile() != NULL) { 1818 // place at root of zip. 1819 String8 srcFile(bundle->getAndroidManifestFile()); 1820 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(), 1821 NULL, String8()); 1822 totalCount++; 1823 } 1824 1825 /* 1826 * If a directory of custom assets was supplied, slurp 'em up. 1827 */ 1828 if (bundle->getAssetSourceDir()) { 1829 const char* assetDir = bundle->getAssetSourceDir(); 1830 1831 FileType type = getFileType(assetDir); 1832 if (type == kFileTypeNonexistent) { 1833 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir); 1834 return UNKNOWN_ERROR; 1835 } 1836 if (type != kFileTypeDirectory) { 1837 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir); 1838 return UNKNOWN_ERROR; 1839 } 1840 1841 String8 assetRoot(assetDir); 1842 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir)); 1843 AaptGroupEntry group; 1844 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group, 1845 String8()); 1846 if (count < 0) { 1847 totalCount = count; 1848 goto bail; 1849 } 1850 if (count > 0) { 1851 mGroupEntries.add(group); 1852 } 1853 totalCount += count; 1854 1855 if (bundle->getVerbose()) 1856 printf("Found %d custom asset file%s in %s\n", 1857 count, (count==1) ? "" : "s", assetDir); 1858 } 1859 1860 /* 1861 * If a directory of resource-specific assets was supplied, slurp 'em up. 1862 */ 1863 for (size_t i=0; i<dirCount; i++) { 1864 const char *res = resDirs[i]; 1865 if (res) { 1866 type = getFileType(res); 1867 if (type == kFileTypeNonexistent) { 1868 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res); 1869 return UNKNOWN_ERROR; 1870 } 1871 if (type == kFileTypeDirectory) { 1872 if (i>0) { 1873 sp<AaptAssets> nextOverlay = new AaptAssets(); 1874 current->setOverlay(nextOverlay); 1875 current = nextOverlay; 1876 } 1877 count = current->slurpResourceTree(bundle, String8(res)); 1878 1879 if (count < 0) { 1880 totalCount = count; 1881 goto bail; 1882 } 1883 totalCount += count; 1884 } 1885 else { 1886 fprintf(stderr, "ERROR: '%s' is not a directory\n", res); 1887 return UNKNOWN_ERROR; 1888 } 1889 } 1890 1891 } 1892 /* 1893 * Now do any additional raw files. 1894 */ 1895 for (int arg=0; arg<N; arg++) { 1896 const char* assetDir = bundle->getFileSpecEntry(arg); 1897 1898 FileType type = getFileType(assetDir); 1899 if (type == kFileTypeNonexistent) { 1900 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir); 1901 return UNKNOWN_ERROR; 1902 } 1903 if (type != kFileTypeDirectory) { 1904 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir); 1905 return UNKNOWN_ERROR; 1906 } 1907 1908 String8 assetRoot(assetDir); 1909 1910 if (bundle->getVerbose()) 1911 printf("Processing raw dir '%s'\n", (const char*) assetDir); 1912 1913 /* 1914 * Do a recursive traversal of subdir tree. We don't make any 1915 * guarantees about ordering, so we're okay with an inorder search 1916 * using whatever order the OS happens to hand back to us. 1917 */ 1918 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8()); 1919 if (count < 0) { 1920 /* failure; report error and remove archive */ 1921 totalCount = count; 1922 goto bail; 1923 } 1924 totalCount += count; 1925 1926 if (bundle->getVerbose()) 1927 printf("Found %d asset file%s in %s\n", 1928 count, (count==1) ? "" : "s", assetDir); 1929 } 1930 1931 count = validate(); 1932 if (count != NO_ERROR) { 1933 totalCount = count; 1934 goto bail; 1935 } 1936 1937 1938bail: 1939 return totalCount; 1940} 1941 1942ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir, 1943 const AaptGroupEntry& kind, 1944 const String8& resType) 1945{ 1946 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType); 1947 if (res > 0) { 1948 mGroupEntries.add(kind); 1949 } 1950 1951 return res; 1952} 1953 1954ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir) 1955{ 1956 ssize_t err = 0; 1957 1958 DIR* dir = opendir(srcDir.string()); 1959 if (dir == NULL) { 1960 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno)); 1961 return UNKNOWN_ERROR; 1962 } 1963 1964 status_t count = 0; 1965 1966 /* 1967 * Run through the directory, looking for dirs that match the 1968 * expected pattern. 1969 */ 1970 while (1) { 1971 struct dirent* entry = readdir(dir); 1972 if (entry == NULL) { 1973 break; 1974 } 1975 1976 if (isHidden(srcDir.string(), entry->d_name)) { 1977 continue; 1978 } 1979 1980 String8 subdirName(srcDir); 1981 subdirName.appendPath(entry->d_name); 1982 1983 AaptGroupEntry group; 1984 String8 resType; 1985 bool b = group.initFromDirName(entry->d_name, &resType); 1986 if (!b) { 1987 fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(), 1988 entry->d_name); 1989 err = -1; 1990 continue; 1991 } 1992 1993 if (bundle->getMaxResVersion() != NULL && group.version.length() != 0) { 1994 int maxResInt = atoi(bundle->getMaxResVersion()); 1995 const char *verString = group.version.string(); 1996 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name 1997 if (dirVersionInt > maxResInt) { 1998 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name); 1999 continue; 2000 } 2001 } 2002 2003 FileType type = getFileType(subdirName.string()); 2004 2005 if (type == kFileTypeDirectory) { 2006 sp<AaptDir> dir = makeDir(String8(entry->d_name)); 2007 ssize_t res = dir->slurpFullTree(bundle, subdirName, group, 2008 resType); 2009 if (res < 0) { 2010 count = res; 2011 goto bail; 2012 } 2013 if (res > 0) { 2014 mGroupEntries.add(group); 2015 count += res; 2016 } 2017 2018 mDirs.add(dir); 2019 } else { 2020 if (bundle->getVerbose()) { 2021 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string()); 2022 } 2023 } 2024 } 2025 2026bail: 2027 closedir(dir); 2028 dir = NULL; 2029 2030 if (err != 0) { 2031 return err; 2032 } 2033 return count; 2034} 2035 2036ssize_t 2037AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename) 2038{ 2039 int count = 0; 2040 SortedVector<AaptGroupEntry> entries; 2041 2042 ZipFile* zip = new ZipFile; 2043 status_t err = zip->open(filename, ZipFile::kOpenReadOnly); 2044 if (err != NO_ERROR) { 2045 fprintf(stderr, "error opening zip file %s\n", filename); 2046 count = err; 2047 delete zip; 2048 return -1; 2049 } 2050 2051 const int N = zip->getNumEntries(); 2052 for (int i=0; i<N; i++) { 2053 ZipEntry* entry = zip->getEntryByIndex(i); 2054 if (entry->getDeleted()) { 2055 continue; 2056 } 2057 2058 String8 entryName(entry->getFileName()); 2059 2060 String8 dirName = entryName.getPathDir(); 2061 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName); 2062 2063 String8 resType; 2064 AaptGroupEntry kind; 2065 2066 String8 remain; 2067 if (entryName.walkPath(&remain) == kResourceDir) { 2068 // these are the resources, pull their type out of the directory name 2069 kind.initFromDirName(remain.walkPath().string(), &resType); 2070 } else { 2071 // these are untyped and don't have an AaptGroupEntry 2072 } 2073 if (entries.indexOf(kind) < 0) { 2074 entries.add(kind); 2075 mGroupEntries.add(kind); 2076 } 2077 2078 // use the one from the zip file if they both exist. 2079 dir->removeFile(entryName.getPathLeaf()); 2080 2081 sp<AaptFile> file = new AaptFile(entryName, kind, resType); 2082 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file); 2083 if (err != NO_ERROR) { 2084 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string()); 2085 count = err; 2086 goto bail; 2087 } 2088 file->setCompressionMethod(entry->getCompressionMethod()); 2089 2090#if 0 2091 if (entryName == "AndroidManifest.xml") { 2092 printf("AndroidManifest.xml\n"); 2093 } 2094 printf("\n\nfile: %s\n", entryName.string()); 2095#endif 2096 2097 size_t len = entry->getUncompressedLen(); 2098 void* data = zip->uncompress(entry); 2099 void* buf = file->editData(len); 2100 memcpy(buf, data, len); 2101 2102#if 0 2103 const int OFF = 0; 2104 const unsigned char* p = (unsigned char*)data; 2105 const unsigned char* end = p+len; 2106 p += OFF; 2107 for (int i=0; i<32 && p < end; i++) { 2108 printf("0x%03x ", i*0x10 + OFF); 2109 for (int j=0; j<0x10 && p < end; j++) { 2110 printf(" %02x", *p); 2111 p++; 2112 } 2113 printf("\n"); 2114 } 2115#endif 2116 2117 free(data); 2118 2119 count++; 2120 } 2121 2122bail: 2123 delete zip; 2124 return count; 2125} 2126 2127sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name) 2128{ 2129 sp<AaptSymbols> sym = mSymbols.valueFor(name); 2130 if (sym == NULL) { 2131 sym = new AaptSymbols(); 2132 mSymbols.add(name, sym); 2133 } 2134 return sym; 2135} 2136 2137status_t AaptAssets::buildIncludedResources(Bundle* bundle) 2138{ 2139 if (!mHaveIncludedAssets) { 2140 // Add in all includes. 2141 const Vector<const char*>& incl = bundle->getPackageIncludes(); 2142 const size_t N=incl.size(); 2143 for (size_t i=0; i<N; i++) { 2144 if (bundle->getVerbose()) 2145 printf("Including resources from package: %s\n", incl[i]); 2146 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) { 2147 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n", 2148 incl[i]); 2149 return UNKNOWN_ERROR; 2150 } 2151 } 2152 mHaveIncludedAssets = true; 2153 } 2154 2155 return NO_ERROR; 2156} 2157 2158status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file) 2159{ 2160 const ResTable& res = getIncludedResources(); 2161 // XXX dirty! 2162 return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL); 2163} 2164 2165const ResTable& AaptAssets::getIncludedResources() const 2166{ 2167 return mIncludedAssets.getResources(false); 2168} 2169 2170void AaptAssets::print() const 2171{ 2172 printf("Locale/Vendor pairs:\n"); 2173 const size_t N=mGroupEntries.size(); 2174 for (size_t i=0; i<N; i++) { 2175 printf(" %s/%s\n", 2176 mGroupEntries.itemAt(i).locale.string(), 2177 mGroupEntries.itemAt(i).vendor.string()); 2178 } 2179 2180 printf("\nFiles:\n"); 2181 AaptDir::print(); 2182} 2183 2184sp<AaptDir> AaptAssets::resDir(const String8& name) 2185{ 2186 const Vector<sp<AaptDir> >& dirs = mDirs; 2187 const size_t N = dirs.size(); 2188 for (size_t i=0; i<N; i++) { 2189 const sp<AaptDir>& d = dirs.itemAt(i); 2190 if (d->getLeaf() == name) { 2191 return d; 2192 } 2193 } 2194 return NULL; 2195} 2196 2197bool 2198valid_symbol_name(const String8& symbol) 2199{ 2200 static char const * const KEYWORDS[] = { 2201 "abstract", "assert", "boolean", "break", 2202 "byte", "case", "catch", "char", "class", "const", "continue", 2203 "default", "do", "double", "else", "enum", "extends", "final", 2204 "finally", "float", "for", "goto", "if", "implements", "import", 2205 "instanceof", "int", "interface", "long", "native", "new", "package", 2206 "private", "protected", "public", "return", "short", "static", 2207 "strictfp", "super", "switch", "synchronized", "this", "throw", 2208 "throws", "transient", "try", "void", "volatile", "while", 2209 "true", "false", "null", 2210 NULL 2211 }; 2212 const char*const* k = KEYWORDS; 2213 const char*const s = symbol.string(); 2214 while (*k) { 2215 if (0 == strcmp(s, *k)) { 2216 return false; 2217 } 2218 k++; 2219 } 2220 return true; 2221} 2222