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