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