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