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