1 // 2 // Copyright 2006 The Android Open Source Project 3 // 4 5 #include "AaptAssets.h" 6 #include "AaptConfig.h" 7 #include "AaptUtil.h" 8 #include "Main.h" 9 #include "ResourceFilter.h" 10 11 #include <utils/misc.h> 12 #include <utils/SortedVector.h> 13 14 #include <ctype.h> 15 #include <dirent.h> 16 #include <errno.h> 17 18 static const char* kDefaultLocale = "default"; 19 static const char* kAssetDir = "assets"; 20 static const char* kResourceDir = "res"; 21 static const char* kValuesDir = "values"; 22 static const char* kMipmapDir = "mipmap"; 23 static const char* kInvalidChars = "/\\:"; 24 static const size_t kMaxAssetFileName = 100; 25 26 static const String8 kResString(kResourceDir); 27 28 /* 29 * Names of asset files must meet the following criteria: 30 * 31 * - the filename length must be less than kMaxAssetFileName bytes long 32 * (and can't be empty) 33 * - all characters must be 7-bit printable ASCII 34 * - none of { '/' '\\' ':' } 35 * 36 * Pass in just the filename, not the full path. 37 */ 38 static bool validateFileName(const char* fileName) 39 { 40 const char* cp = fileName; 41 size_t len = 0; 42 43 while (*cp != '\0') { 44 if ((*cp & 0x80) != 0) 45 return false; // reject high ASCII 46 if (*cp < 0x20 || *cp >= 0x7f) 47 return false; // reject control chars and 0x7f 48 if (strchr(kInvalidChars, *cp) != NULL) 49 return false; // reject path sep chars 50 cp++; 51 len++; 52 } 53 54 if (len < 1 || len > kMaxAssetFileName) 55 return false; // reject empty or too long 56 57 return true; 58 } 59 60 // The default to use if no other ignore pattern is defined. 61 const char * const gDefaultIgnoreAssets = 62 "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~"; 63 // The ignore pattern that can be passed via --ignore-assets in Main.cpp 64 const char * gUserIgnoreAssets = NULL; 65 66 static bool isHidden(const char *root, const char *path) 67 { 68 // Patterns syntax: 69 // - Delimiter is : 70 // - Entry can start with the flag ! to avoid printing a warning 71 // about the file being ignored. 72 // - Entry can have the flag "<dir>" to match only directories 73 // or <file> to match only files. Default is to match both. 74 // - Entry can be a simplified glob "<prefix>*" or "*<suffix>" 75 // where prefix/suffix must have at least 1 character (so that 76 // we don't match a '*' catch-all pattern.) 77 // - The special filenames "." and ".." are always ignored. 78 // - Otherwise the full string is matched. 79 // - match is not case-sensitive. 80 81 if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) { 82 return true; 83 } 84 85 const char *delim = ":"; 86 const char *p = gUserIgnoreAssets; 87 if (!p || !p[0]) { 88 p = getenv("ANDROID_AAPT_IGNORE"); 89 } 90 if (!p || !p[0]) { 91 p = gDefaultIgnoreAssets; 92 } 93 char *patterns = strdup(p); 94 95 bool ignore = false; 96 bool chatty = true; 97 char *matchedPattern = NULL; 98 99 String8 fullPath(root); 100 fullPath.appendPath(path); 101 FileType type = getFileType(fullPath); 102 103 int plen = strlen(path); 104 105 // Note: we don't have strtok_r under mingw. 106 for(char *token = strtok(patterns, delim); 107 !ignore && token != NULL; 108 token = strtok(NULL, delim)) { 109 chatty = token[0] != '!'; 110 if (!chatty) token++; // skip ! 111 if (strncasecmp(token, "<dir>" , 5) == 0) { 112 if (type != kFileTypeDirectory) continue; 113 token += 5; 114 } 115 if (strncasecmp(token, "<file>", 6) == 0) { 116 if (type != kFileTypeRegular) continue; 117 token += 6; 118 } 119 120 matchedPattern = token; 121 int n = strlen(token); 122 123 if (token[0] == '*') { 124 // Match *suffix 125 token++; 126 n--; 127 if (n <= plen) { 128 ignore = strncasecmp(token, path + plen - n, n) == 0; 129 } 130 } else if (n > 1 && token[n - 1] == '*') { 131 // Match prefix* 132 ignore = strncasecmp(token, path, n - 1) == 0; 133 } else { 134 ignore = strcasecmp(token, path) == 0; 135 } 136 } 137 138 if (ignore && chatty) { 139 fprintf(stderr, " (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n", 140 type == kFileTypeDirectory ? "dir" : "file", 141 path, 142 matchedPattern ? matchedPattern : ""); 143 } 144 145 free(patterns); 146 return ignore; 147 } 148 149 // ========================================================================= 150 // ========================================================================= 151 // ========================================================================= 152 153 /* static */ 154 inline bool isAlpha(const String8& string) { 155 const size_t length = string.length(); 156 for (size_t i = 0; i < length; ++i) { 157 if (!isalpha(string[i])) { 158 return false; 159 } 160 } 161 162 return true; 163 } 164 165 /* static */ 166 inline bool isNumber(const String8& string) { 167 const size_t length = string.length(); 168 for (size_t i = 0; i < length; ++i) { 169 if (!isdigit(string[i])) { 170 return false; 171 } 172 } 173 174 return true; 175 } 176 177 void AaptLocaleValue::setLanguage(const char* languageChars) { 178 size_t i = 0; 179 while ((*languageChars) != '\0') { 180 language[i++] = tolower(*languageChars); 181 languageChars++; 182 } 183 } 184 185 void AaptLocaleValue::setRegion(const char* regionChars) { 186 size_t i = 0; 187 while ((*regionChars) != '\0') { 188 region[i++] = toupper(*regionChars); 189 regionChars++; 190 } 191 } 192 193 void AaptLocaleValue::setScript(const char* scriptChars) { 194 size_t i = 0; 195 while ((*scriptChars) != '\0') { 196 if (i == 0) { 197 script[i++] = toupper(*scriptChars); 198 } else { 199 script[i++] = tolower(*scriptChars); 200 } 201 scriptChars++; 202 } 203 } 204 205 void AaptLocaleValue::setVariant(const char* variantChars) { 206 size_t i = 0; 207 while ((*variantChars) != '\0') { 208 variant[i++] = *variantChars; 209 variantChars++; 210 } 211 } 212 213 bool AaptLocaleValue::initFromFilterString(const String8& str) { 214 // A locale (as specified in the filter) is an underscore separated name such 215 // as "en_US", "en_Latn_US", or "en_US_POSIX". 216 Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '_'); 217 218 const int numTags = parts.size(); 219 bool valid = false; 220 if (numTags >= 1) { 221 const String8& lang = parts[0]; 222 if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) { 223 setLanguage(lang.string()); 224 valid = true; 225 } 226 } 227 228 if (!valid || numTags == 1) { 229 return valid; 230 } 231 232 // At this point, valid == true && numTags > 1. 233 const String8& part2 = parts[1]; 234 if ((part2.length() == 2 && isAlpha(part2)) || 235 (part2.length() == 3 && isNumber(part2))) { 236 setRegion(part2.string()); 237 } else if (part2.length() == 4 && isAlpha(part2)) { 238 setScript(part2.string()); 239 } else if (part2.length() >= 5 && part2.length() <= 8) { 240 setVariant(part2.string()); 241 } else { 242 valid = false; 243 } 244 245 if (!valid || numTags == 2) { 246 return valid; 247 } 248 249 // At this point, valid == true && numTags > 1. 250 const String8& part3 = parts[2]; 251 if (((part3.length() == 2 && isAlpha(part3)) || 252 (part3.length() == 3 && isNumber(part3))) && script[0]) { 253 setRegion(part3.string()); 254 } else if (part3.length() >= 5 && part3.length() <= 8) { 255 setVariant(part3.string()); 256 } else { 257 valid = false; 258 } 259 260 if (!valid || numTags == 3) { 261 return valid; 262 } 263 264 const String8& part4 = parts[3]; 265 if (part4.length() >= 5 && part4.length() <= 8) { 266 setVariant(part4.string()); 267 } else { 268 valid = false; 269 } 270 271 if (!valid || numTags > 4) { 272 return false; 273 } 274 275 return true; 276 } 277 278 int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int startIndex) { 279 const int size = parts.size(); 280 int currentIndex = startIndex; 281 282 String8 part = parts[currentIndex]; 283 if (part[0] == 'b' && part[1] == '+') { 284 // This is a "modified" BCP-47 language tag. Same semantics as BCP-47 tags, 285 // except that the separator is "+" and not "-". 286 Vector<String8> subtags = AaptUtil::splitAndLowerCase(part, '+'); 287 subtags.removeItemsAt(0); 288 if (subtags.size() == 1) { 289 setLanguage(subtags[0]); 290 } else if (subtags.size() == 2) { 291 setLanguage(subtags[0]); 292 293 // The second tag can either be a region, a variant or a script. 294 switch (subtags[1].size()) { 295 case 2: 296 case 3: 297 setRegion(subtags[1]); 298 break; 299 case 4: 300 setScript(subtags[1]); 301 break; 302 case 5: 303 case 6: 304 case 7: 305 case 8: 306 setVariant(subtags[1]); 307 break; 308 default: 309 fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n", 310 part.string()); 311 return -1; 312 } 313 } else if (subtags.size() == 3) { 314 // The language is always the first subtag. 315 setLanguage(subtags[0]); 316 317 // The second subtag can either be a script or a region code. 318 // If its size is 4, it's a script code, else it's a region code. 319 bool hasRegion = false; 320 if (subtags[1].size() == 4) { 321 setScript(subtags[1]); 322 } else if (subtags[1].size() == 2 || subtags[1].size() == 3) { 323 setRegion(subtags[1]); 324 hasRegion = true; 325 } else { 326 fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n", part.string()); 327 return -1; 328 } 329 330 // The third tag can either be a region code (if the second tag was 331 // a script), else a variant code. 332 if (subtags[2].size() > 4) { 333 setVariant(subtags[2]); 334 } else { 335 setRegion(subtags[2]); 336 } 337 } else if (subtags.size() == 4) { 338 setLanguage(subtags[0]); 339 setScript(subtags[1]); 340 setRegion(subtags[2]); 341 setVariant(subtags[3]); 342 } else { 343 fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name: %s\n", part.string()); 344 return -1; 345 } 346 347 return ++currentIndex; 348 } else { 349 if ((part.length() == 2 || part.length() == 3) && isAlpha(part)) { 350 setLanguage(part); 351 if (++currentIndex == size) { 352 return size; 353 } 354 } else { 355 return currentIndex; 356 } 357 358 part = parts[currentIndex]; 359 if (part.string()[0] == 'r' && part.length() == 3) { 360 setRegion(part.string() + 1); 361 if (++currentIndex == size) { 362 return size; 363 } 364 } 365 } 366 367 return currentIndex; 368 } 369 370 371 String8 AaptLocaleValue::toDirName() const { 372 String8 dirName(""); 373 if (language[0]) { 374 dirName += language; 375 } else { 376 return dirName; 377 } 378 379 if (script[0]) { 380 dirName += "-s"; 381 dirName += script; 382 } 383 384 if (region[0]) { 385 dirName += "-r"; 386 dirName += region; 387 } 388 389 if (variant[0]) { 390 dirName += "-v"; 391 dirName += variant; 392 } 393 394 return dirName; 395 } 396 397 void AaptLocaleValue::initFromResTable(const ResTable_config& config) { 398 config.unpackLanguage(language); 399 config.unpackRegion(region); 400 if (config.localeScript[0]) { 401 memcpy(script, config.localeScript, sizeof(config.localeScript)); 402 } 403 404 if (config.localeVariant[0]) { 405 memcpy(variant, config.localeVariant, sizeof(config.localeVariant)); 406 } 407 } 408 409 void AaptLocaleValue::writeTo(ResTable_config* out) const { 410 out->packLanguage(language); 411 out->packRegion(region); 412 413 if (script[0]) { 414 memcpy(out->localeScript, script, sizeof(out->localeScript)); 415 } 416 417 if (variant[0]) { 418 memcpy(out->localeVariant, variant, sizeof(out->localeVariant)); 419 } 420 } 421 422 bool 423 AaptGroupEntry::initFromDirName(const char* dir, String8* resType) 424 { 425 const char* q = strchr(dir, '-'); 426 size_t typeLen; 427 if (q != NULL) { 428 typeLen = q - dir; 429 } else { 430 typeLen = strlen(dir); 431 } 432 433 String8 type(dir, typeLen); 434 if (!isValidResourceType(type)) { 435 return false; 436 } 437 438 if (q != NULL) { 439 if (!AaptConfig::parse(String8(q + 1), &mParams)) { 440 return false; 441 } 442 } 443 444 *resType = type; 445 return true; 446 } 447 448 String8 449 AaptGroupEntry::toDirName(const String8& resType) const 450 { 451 String8 s = resType; 452 String8 params = mParams.toString(); 453 if (params.length() > 0) { 454 if (s.length() > 0) { 455 s += "-"; 456 } 457 s += params; 458 } 459 return s; 460 } 461 462 463 // ========================================================================= 464 // ========================================================================= 465 // ========================================================================= 466 467 void* AaptFile::editData(size_t size) 468 { 469 if (size <= mBufferSize) { 470 mDataSize = size; 471 return mData; 472 } 473 size_t allocSize = (size*3)/2; 474 void* buf = realloc(mData, allocSize); 475 if (buf == NULL) { 476 return NULL; 477 } 478 mData = buf; 479 mDataSize = size; 480 mBufferSize = allocSize; 481 return buf; 482 } 483 484 void* AaptFile::editDataInRange(size_t offset, size_t size) 485 { 486 return (void*)(((uint8_t*) editData(offset + size)) + offset); 487 } 488 489 void* AaptFile::editData(size_t* outSize) 490 { 491 if (outSize) { 492 *outSize = mDataSize; 493 } 494 return mData; 495 } 496 497 void* AaptFile::padData(size_t wordSize) 498 { 499 const size_t extra = mDataSize%wordSize; 500 if (extra == 0) { 501 return mData; 502 } 503 504 size_t initial = mDataSize; 505 void* data = editData(initial+(wordSize-extra)); 506 if (data != NULL) { 507 memset(((uint8_t*)data) + initial, 0, wordSize-extra); 508 } 509 return data; 510 } 511 512 status_t AaptFile::writeData(const void* data, size_t size) 513 { 514 size_t end = mDataSize; 515 size_t total = size + end; 516 void* buf = editData(total); 517 if (buf == NULL) { 518 return UNKNOWN_ERROR; 519 } 520 memcpy(((char*)buf)+end, data, size); 521 return NO_ERROR; 522 } 523 524 void AaptFile::clearData() 525 { 526 if (mData != NULL) free(mData); 527 mData = NULL; 528 mDataSize = 0; 529 mBufferSize = 0; 530 } 531 532 String8 AaptFile::getPrintableSource() const 533 { 534 if (hasData()) { 535 String8 name(mGroupEntry.toDirName(String8())); 536 name.appendPath(mPath); 537 name.append(" #generated"); 538 return name; 539 } 540 return mSourceFile; 541 } 542 543 // ========================================================================= 544 // ========================================================================= 545 // ========================================================================= 546 547 status_t AaptGroup::addFile(const sp<AaptFile>& file, const bool overwriteDuplicate) 548 { 549 ssize_t index = mFiles.indexOfKey(file->getGroupEntry()); 550 if (index >= 0 && overwriteDuplicate) { 551 fprintf(stderr, "warning: overwriting '%s' with '%s'\n", 552 mFiles[index]->getSourceFile().string(), 553 file->getSourceFile().string()); 554 removeFile(index); 555 index = -1; 556 } 557 558 if (index < 0) { 559 file->mPath = mPath; 560 mFiles.add(file->getGroupEntry(), file); 561 return NO_ERROR; 562 } 563 564 // Check if the version is automatically applied. This is a common source of 565 // error. 566 ConfigDescription withoutVersion = file->getGroupEntry().toParams(); 567 withoutVersion.version = 0; 568 AaptConfig::applyVersionForCompatibility(&withoutVersion); 569 570 const sp<AaptFile>& originalFile = mFiles.valueAt(index); 571 SourcePos(file->getSourceFile(), -1) 572 .error("Duplicate file.\n%s: Original is here. %s", 573 originalFile->getPrintableSource().string(), 574 (withoutVersion.version != 0) ? "The version qualifier may be implied." : ""); 575 return UNKNOWN_ERROR; 576 } 577 578 void AaptGroup::removeFile(size_t index) 579 { 580 mFiles.removeItemsAt(index); 581 } 582 583 void AaptGroup::print(const String8& prefix) const 584 { 585 printf("%s%s\n", prefix.string(), getPath().string()); 586 const size_t N=mFiles.size(); 587 size_t i; 588 for (i=0; i<N; i++) { 589 sp<AaptFile> file = mFiles.valueAt(i); 590 const AaptGroupEntry& e = file->getGroupEntry(); 591 if (file->hasData()) { 592 printf("%s Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(), 593 (int)file->getSize()); 594 } else { 595 printf("%s Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(), 596 file->getPrintableSource().string()); 597 } 598 //printf("%s File Group Entry: %s\n", prefix.string(), 599 // file->getGroupEntry().toDirName(String8()).string()); 600 } 601 } 602 603 String8 AaptGroup::getPrintableSource() const 604 { 605 if (mFiles.size() > 0) { 606 // Arbitrarily pull the first source file out of the list. 607 return mFiles.valueAt(0)->getPrintableSource(); 608 } 609 610 // Should never hit this case, but to be safe... 611 return getPath(); 612 613 } 614 615 // ========================================================================= 616 // ========================================================================= 617 // ========================================================================= 618 619 status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file) 620 { 621 if (mFiles.indexOfKey(name) >= 0) { 622 return ALREADY_EXISTS; 623 } 624 mFiles.add(name, file); 625 return NO_ERROR; 626 } 627 628 status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir) 629 { 630 if (mDirs.indexOfKey(name) >= 0) { 631 return ALREADY_EXISTS; 632 } 633 mDirs.add(name, dir); 634 return NO_ERROR; 635 } 636 637 sp<AaptDir> AaptDir::makeDir(const String8& path) 638 { 639 String8 name; 640 String8 remain = path; 641 642 sp<AaptDir> subdir = this; 643 while (name = remain.walkPath(&remain), remain != "") { 644 subdir = subdir->makeDir(name); 645 } 646 647 ssize_t i = subdir->mDirs.indexOfKey(name); 648 if (i >= 0) { 649 return subdir->mDirs.valueAt(i); 650 } 651 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name)); 652 subdir->mDirs.add(name, dir); 653 return dir; 654 } 655 656 void AaptDir::removeFile(const String8& name) 657 { 658 mFiles.removeItem(name); 659 } 660 661 void AaptDir::removeDir(const String8& name) 662 { 663 mDirs.removeItem(name); 664 } 665 666 status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file, 667 const bool overwrite) 668 { 669 sp<AaptGroup> group; 670 if (mFiles.indexOfKey(leafName) >= 0) { 671 group = mFiles.valueFor(leafName); 672 } else { 673 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName)); 674 mFiles.add(leafName, group); 675 } 676 677 return group->addFile(file, overwrite); 678 } 679 680 ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir, 681 const AaptGroupEntry& kind, const String8& resType, 682 sp<FilePathStore>& fullResPaths, const bool overwrite) 683 { 684 Vector<String8> fileNames; 685 { 686 DIR* dir = NULL; 687 688 dir = opendir(srcDir.string()); 689 if (dir == NULL) { 690 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno)); 691 return UNKNOWN_ERROR; 692 } 693 694 /* 695 * Slurp the filenames out of the directory. 696 */ 697 while (1) { 698 struct dirent* entry; 699 700 entry = readdir(dir); 701 if (entry == NULL) 702 break; 703 704 if (isHidden(srcDir.string(), entry->d_name)) 705 continue; 706 707 String8 name(entry->d_name); 708 fileNames.add(name); 709 // Add fully qualified path for dependency purposes 710 // if we're collecting them 711 if (fullResPaths != NULL) { 712 fullResPaths->add(srcDir.appendPathCopy(name)); 713 } 714 } 715 closedir(dir); 716 } 717 718 ssize_t count = 0; 719 720 /* 721 * Stash away the files and recursively descend into subdirectories. 722 */ 723 const size_t N = fileNames.size(); 724 size_t i; 725 for (i = 0; i < N; i++) { 726 String8 pathName(srcDir); 727 FileType type; 728 729 pathName.appendPath(fileNames[i].string()); 730 type = getFileType(pathName.string()); 731 if (type == kFileTypeDirectory) { 732 sp<AaptDir> subdir; 733 bool notAdded = false; 734 if (mDirs.indexOfKey(fileNames[i]) >= 0) { 735 subdir = mDirs.valueFor(fileNames[i]); 736 } else { 737 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i])); 738 notAdded = true; 739 } 740 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind, 741 resType, fullResPaths, overwrite); 742 if (res < NO_ERROR) { 743 return res; 744 } 745 if (res > 0 && notAdded) { 746 mDirs.add(fileNames[i], subdir); 747 } 748 count += res; 749 } else if (type == kFileTypeRegular) { 750 sp<AaptFile> file = new AaptFile(pathName, kind, resType); 751 status_t err = addLeafFile(fileNames[i], file, overwrite); 752 if (err != NO_ERROR) { 753 return err; 754 } 755 756 count++; 757 758 } else { 759 if (bundle->getVerbose()) 760 printf(" (ignoring non-file/dir '%s')\n", pathName.string()); 761 } 762 } 763 764 return count; 765 } 766 767 status_t AaptDir::validate() const 768 { 769 const size_t NF = mFiles.size(); 770 const size_t ND = mDirs.size(); 771 size_t i; 772 for (i = 0; i < NF; i++) { 773 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) { 774 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( 775 "Invalid filename. Unable to add."); 776 return UNKNOWN_ERROR; 777 } 778 779 size_t j; 780 for (j = i+1; j < NF; j++) { 781 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(), 782 mFiles.valueAt(j)->getLeaf().string()) == 0) { 783 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( 784 "File is case-insensitive equivalent to: %s", 785 mFiles.valueAt(j)->getPrintableSource().string()); 786 return UNKNOWN_ERROR; 787 } 788 789 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz" 790 // (this is mostly caught by the "marked" stuff, below) 791 } 792 793 for (j = 0; j < ND; j++) { 794 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(), 795 mDirs.valueAt(j)->getLeaf().string()) == 0) { 796 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error( 797 "File conflicts with dir from: %s", 798 mDirs.valueAt(j)->getPrintableSource().string()); 799 return UNKNOWN_ERROR; 800 } 801 } 802 } 803 804 for (i = 0; i < ND; i++) { 805 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) { 806 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error( 807 "Invalid directory name, unable to add."); 808 return UNKNOWN_ERROR; 809 } 810 811 size_t j; 812 for (j = i+1; j < ND; j++) { 813 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(), 814 mDirs.valueAt(j)->getLeaf().string()) == 0) { 815 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error( 816 "Directory is case-insensitive equivalent to: %s", 817 mDirs.valueAt(j)->getPrintableSource().string()); 818 return UNKNOWN_ERROR; 819 } 820 } 821 822 status_t err = mDirs.valueAt(i)->validate(); 823 if (err != NO_ERROR) { 824 return err; 825 } 826 } 827 828 return NO_ERROR; 829 } 830 831 void AaptDir::print(const String8& prefix) const 832 { 833 const size_t ND=getDirs().size(); 834 size_t i; 835 for (i=0; i<ND; i++) { 836 getDirs().valueAt(i)->print(prefix); 837 } 838 839 const size_t NF=getFiles().size(); 840 for (i=0; i<NF; i++) { 841 getFiles().valueAt(i)->print(prefix); 842 } 843 } 844 845 String8 AaptDir::getPrintableSource() const 846 { 847 if (mFiles.size() > 0) { 848 // Arbitrarily pull the first file out of the list as the source dir. 849 return mFiles.valueAt(0)->getPrintableSource().getPathDir(); 850 } 851 if (mDirs.size() > 0) { 852 // Or arbitrarily pull the first dir out of the list as the source dir. 853 return mDirs.valueAt(0)->getPrintableSource().getPathDir(); 854 } 855 856 // Should never hit this case, but to be safe... 857 return mPath; 858 859 } 860 861 // ========================================================================= 862 // ========================================================================= 863 // ========================================================================= 864 865 status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols) 866 { 867 status_t err = NO_ERROR; 868 size_t N = javaSymbols->mSymbols.size(); 869 for (size_t i=0; i<N; i++) { 870 const String8& name = javaSymbols->mSymbols.keyAt(i); 871 const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i); 872 ssize_t pos = mSymbols.indexOfKey(name); 873 if (pos < 0) { 874 entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string()); 875 err = UNKNOWN_ERROR; 876 continue; 877 } 878 //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n", 879 // i, N, name.string(), entry.isJavaSymbol ? 1 : 0); 880 mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol; 881 } 882 883 N = javaSymbols->mNestedSymbols.size(); 884 for (size_t i=0; i<N; i++) { 885 const String8& name = javaSymbols->mNestedSymbols.keyAt(i); 886 const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i); 887 ssize_t pos = mNestedSymbols.indexOfKey(name); 888 if (pos < 0) { 889 SourcePos pos; 890 pos.error("Java symbol dir %s not defined\n", name.string()); 891 err = UNKNOWN_ERROR; 892 continue; 893 } 894 //printf("**** applying java symbols in dir %s\n", name.string()); 895 status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols); 896 if (myerr != NO_ERROR) { 897 err = myerr; 898 } 899 } 900 901 return err; 902 } 903 904 // ========================================================================= 905 // ========================================================================= 906 // ========================================================================= 907 908 AaptAssets::AaptAssets() 909 : AaptDir(String8(), String8()), 910 mHavePrivateSymbols(false), 911 mChanged(false), mHaveIncludedAssets(false), 912 mRes(NULL) {} 913 914 const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const { 915 if (mChanged) { 916 } 917 return mGroupEntries; 918 } 919 920 status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file) 921 { 922 mChanged = true; 923 return AaptDir::addFile(name, file); 924 } 925 926 sp<AaptFile> AaptAssets::addFile( 927 const String8& filePath, const AaptGroupEntry& entry, 928 const String8& srcDir, sp<AaptGroup>* outGroup, 929 const String8& resType) 930 { 931 sp<AaptDir> dir = this; 932 sp<AaptGroup> group; 933 sp<AaptFile> file; 934 String8 root, remain(filePath), partialPath; 935 while (remain.length() > 0) { 936 root = remain.walkPath(&remain); 937 partialPath.appendPath(root); 938 939 const String8 rootStr(root); 940 941 if (remain.length() == 0) { 942 ssize_t i = dir->getFiles().indexOfKey(rootStr); 943 if (i >= 0) { 944 group = dir->getFiles().valueAt(i); 945 } else { 946 group = new AaptGroup(rootStr, filePath); 947 status_t res = dir->addFile(rootStr, group); 948 if (res != NO_ERROR) { 949 return NULL; 950 } 951 } 952 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType); 953 status_t res = group->addFile(file); 954 if (res != NO_ERROR) { 955 return NULL; 956 } 957 break; 958 959 } else { 960 ssize_t i = dir->getDirs().indexOfKey(rootStr); 961 if (i >= 0) { 962 dir = dir->getDirs().valueAt(i); 963 } else { 964 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath); 965 status_t res = dir->addDir(rootStr, subdir); 966 if (res != NO_ERROR) { 967 return NULL; 968 } 969 dir = subdir; 970 } 971 } 972 } 973 974 mGroupEntries.add(entry); 975 if (outGroup) *outGroup = group; 976 return file; 977 } 978 979 void AaptAssets::addResource(const String8& leafName, const String8& path, 980 const sp<AaptFile>& file, const String8& resType) 981 { 982 sp<AaptDir> res = AaptDir::makeDir(kResString); 983 String8 dirname = file->getGroupEntry().toDirName(resType); 984 sp<AaptDir> subdir = res->makeDir(dirname); 985 sp<AaptGroup> grr = new AaptGroup(leafName, path); 986 grr->addFile(file); 987 988 subdir->addFile(leafName, grr); 989 } 990 991 992 ssize_t AaptAssets::slurpFromArgs(Bundle* bundle) 993 { 994 int count; 995 int totalCount = 0; 996 FileType type; 997 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs(); 998 const size_t dirCount =resDirs.size(); 999 sp<AaptAssets> current = this; 1000 1001 const int N = bundle->getFileSpecCount(); 1002 1003 /* 1004 * If a package manifest was specified, include that first. 1005 */ 1006 if (bundle->getAndroidManifestFile() != NULL) { 1007 // place at root of zip. 1008 String8 srcFile(bundle->getAndroidManifestFile()); 1009 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(), 1010 NULL, String8()); 1011 totalCount++; 1012 } 1013 1014 /* 1015 * If a directory of custom assets was supplied, slurp 'em up. 1016 */ 1017 const Vector<const char*>& assetDirs = bundle->getAssetSourceDirs(); 1018 const int AN = assetDirs.size(); 1019 for (int i = 0; i < AN; i++) { 1020 FileType type = getFileType(assetDirs[i]); 1021 if (type == kFileTypeNonexistent) { 1022 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDirs[i]); 1023 return UNKNOWN_ERROR; 1024 } 1025 if (type != kFileTypeDirectory) { 1026 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDirs[i]); 1027 return UNKNOWN_ERROR; 1028 } 1029 1030 String8 assetRoot(assetDirs[i]); 1031 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir)); 1032 AaptGroupEntry group; 1033 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group, 1034 String8(), mFullAssetPaths, true); 1035 if (count < 0) { 1036 totalCount = count; 1037 goto bail; 1038 } 1039 if (count > 0) { 1040 mGroupEntries.add(group); 1041 } 1042 totalCount += count; 1043 1044 if (bundle->getVerbose()) { 1045 printf("Found %d custom asset file%s in %s\n", 1046 count, (count==1) ? "" : "s", assetDirs[i]); 1047 } 1048 } 1049 1050 /* 1051 * If a directory of resource-specific assets was supplied, slurp 'em up. 1052 */ 1053 for (size_t i=0; i<dirCount; i++) { 1054 const char *res = resDirs[i]; 1055 if (res) { 1056 type = getFileType(res); 1057 if (type == kFileTypeNonexistent) { 1058 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res); 1059 return UNKNOWN_ERROR; 1060 } 1061 if (type == kFileTypeDirectory) { 1062 if (i>0) { 1063 sp<AaptAssets> nextOverlay = new AaptAssets(); 1064 current->setOverlay(nextOverlay); 1065 current = nextOverlay; 1066 current->setFullResPaths(mFullResPaths); 1067 } 1068 count = current->slurpResourceTree(bundle, String8(res)); 1069 if (i > 0 && count > 0) { 1070 count = current->filter(bundle); 1071 } 1072 1073 if (count < 0) { 1074 totalCount = count; 1075 goto bail; 1076 } 1077 totalCount += count; 1078 } 1079 else { 1080 fprintf(stderr, "ERROR: '%s' is not a directory\n", res); 1081 return UNKNOWN_ERROR; 1082 } 1083 } 1084 1085 } 1086 /* 1087 * Now do any additional raw files. 1088 */ 1089 for (int arg=0; arg<N; arg++) { 1090 const char* assetDir = bundle->getFileSpecEntry(arg); 1091 1092 FileType type = getFileType(assetDir); 1093 if (type == kFileTypeNonexistent) { 1094 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir); 1095 return UNKNOWN_ERROR; 1096 } 1097 if (type != kFileTypeDirectory) { 1098 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir); 1099 return UNKNOWN_ERROR; 1100 } 1101 1102 String8 assetRoot(assetDir); 1103 1104 if (bundle->getVerbose()) 1105 printf("Processing raw dir '%s'\n", (const char*) assetDir); 1106 1107 /* 1108 * Do a recursive traversal of subdir tree. We don't make any 1109 * guarantees about ordering, so we're okay with an inorder search 1110 * using whatever order the OS happens to hand back to us. 1111 */ 1112 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths); 1113 if (count < 0) { 1114 /* failure; report error and remove archive */ 1115 totalCount = count; 1116 goto bail; 1117 } 1118 totalCount += count; 1119 1120 if (bundle->getVerbose()) 1121 printf("Found %d asset file%s in %s\n", 1122 count, (count==1) ? "" : "s", assetDir); 1123 } 1124 1125 count = validate(); 1126 if (count != NO_ERROR) { 1127 totalCount = count; 1128 goto bail; 1129 } 1130 1131 count = filter(bundle); 1132 if (count != NO_ERROR) { 1133 totalCount = count; 1134 goto bail; 1135 } 1136 1137 bail: 1138 return totalCount; 1139 } 1140 1141 ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir, 1142 const AaptGroupEntry& kind, 1143 const String8& resType, 1144 sp<FilePathStore>& fullResPaths, 1145 const bool overwrite) 1146 { 1147 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths, overwrite); 1148 if (res > 0) { 1149 mGroupEntries.add(kind); 1150 } 1151 1152 return res; 1153 } 1154 1155 ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir) 1156 { 1157 ssize_t err = 0; 1158 1159 DIR* dir = opendir(srcDir.string()); 1160 if (dir == NULL) { 1161 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno)); 1162 return UNKNOWN_ERROR; 1163 } 1164 1165 status_t count = 0; 1166 1167 /* 1168 * Run through the directory, looking for dirs that match the 1169 * expected pattern. 1170 */ 1171 while (1) { 1172 struct dirent* entry = readdir(dir); 1173 if (entry == NULL) { 1174 break; 1175 } 1176 1177 if (isHidden(srcDir.string(), entry->d_name)) { 1178 continue; 1179 } 1180 1181 String8 subdirName(srcDir); 1182 subdirName.appendPath(entry->d_name); 1183 1184 AaptGroupEntry group; 1185 String8 resType; 1186 bool b = group.initFromDirName(entry->d_name, &resType); 1187 if (!b) { 1188 fprintf(stderr, "invalid resource directory name: %s %s\n", srcDir.string(), 1189 entry->d_name); 1190 err = -1; 1191 continue; 1192 } 1193 1194 if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) { 1195 int maxResInt = atoi(bundle->getMaxResVersion()); 1196 const char *verString = group.getVersionString().string(); 1197 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name 1198 if (dirVersionInt > maxResInt) { 1199 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name); 1200 continue; 1201 } 1202 } 1203 1204 FileType type = getFileType(subdirName.string()); 1205 1206 if (type == kFileTypeDirectory) { 1207 sp<AaptDir> dir = makeDir(resType); 1208 ssize_t res = dir->slurpFullTree(bundle, subdirName, group, 1209 resType, mFullResPaths); 1210 if (res < 0) { 1211 count = res; 1212 goto bail; 1213 } 1214 if (res > 0) { 1215 mGroupEntries.add(group); 1216 count += res; 1217 } 1218 1219 // Only add this directory if we don't already have a resource dir 1220 // for the current type. This ensures that we only add the dir once 1221 // for all configs. 1222 sp<AaptDir> rdir = resDir(resType); 1223 if (rdir == NULL) { 1224 mResDirs.add(dir); 1225 } 1226 } else { 1227 if (bundle->getVerbose()) { 1228 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string()); 1229 } 1230 } 1231 } 1232 1233 bail: 1234 closedir(dir); 1235 dir = NULL; 1236 1237 if (err != 0) { 1238 return err; 1239 } 1240 return count; 1241 } 1242 1243 ssize_t 1244 AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename) 1245 { 1246 int count = 0; 1247 SortedVector<AaptGroupEntry> entries; 1248 1249 ZipFile* zip = new ZipFile; 1250 status_t err = zip->open(filename, ZipFile::kOpenReadOnly); 1251 if (err != NO_ERROR) { 1252 fprintf(stderr, "error opening zip file %s\n", filename); 1253 count = err; 1254 delete zip; 1255 return -1; 1256 } 1257 1258 const int N = zip->getNumEntries(); 1259 for (int i=0; i<N; i++) { 1260 ZipEntry* entry = zip->getEntryByIndex(i); 1261 if (entry->getDeleted()) { 1262 continue; 1263 } 1264 1265 String8 entryName(entry->getFileName()); 1266 1267 String8 dirName = entryName.getPathDir(); 1268 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName); 1269 1270 String8 resType; 1271 AaptGroupEntry kind; 1272 1273 String8 remain; 1274 if (entryName.walkPath(&remain) == kResourceDir) { 1275 // these are the resources, pull their type out of the directory name 1276 kind.initFromDirName(remain.walkPath().string(), &resType); 1277 } else { 1278 // these are untyped and don't have an AaptGroupEntry 1279 } 1280 if (entries.indexOf(kind) < 0) { 1281 entries.add(kind); 1282 mGroupEntries.add(kind); 1283 } 1284 1285 // use the one from the zip file if they both exist. 1286 dir->removeFile(entryName.getPathLeaf()); 1287 1288 sp<AaptFile> file = new AaptFile(entryName, kind, resType); 1289 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file); 1290 if (err != NO_ERROR) { 1291 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string()); 1292 count = err; 1293 goto bail; 1294 } 1295 file->setCompressionMethod(entry->getCompressionMethod()); 1296 1297 #if 0 1298 if (entryName == "AndroidManifest.xml") { 1299 printf("AndroidManifest.xml\n"); 1300 } 1301 printf("\n\nfile: %s\n", entryName.string()); 1302 #endif 1303 1304 size_t len = entry->getUncompressedLen(); 1305 void* data = zip->uncompress(entry); 1306 void* buf = file->editData(len); 1307 memcpy(buf, data, len); 1308 1309 #if 0 1310 const int OFF = 0; 1311 const unsigned char* p = (unsigned char*)data; 1312 const unsigned char* end = p+len; 1313 p += OFF; 1314 for (int i=0; i<32 && p < end; i++) { 1315 printf("0x%03x ", i*0x10 + OFF); 1316 for (int j=0; j<0x10 && p < end; j++) { 1317 printf(" %02x", *p); 1318 p++; 1319 } 1320 printf("\n"); 1321 } 1322 #endif 1323 1324 free(data); 1325 1326 count++; 1327 } 1328 1329 bail: 1330 delete zip; 1331 return count; 1332 } 1333 1334 status_t AaptAssets::filter(Bundle* bundle) 1335 { 1336 WeakResourceFilter reqFilter; 1337 status_t err = reqFilter.parse(bundle->getConfigurations()); 1338 if (err != NO_ERROR) { 1339 return err; 1340 } 1341 1342 uint32_t preferredDensity = 0; 1343 if (bundle->getPreferredDensity().size() > 0) { 1344 ResTable_config preferredConfig; 1345 if (!AaptConfig::parseDensity(bundle->getPreferredDensity().string(), &preferredConfig)) { 1346 fprintf(stderr, "Error parsing preferred density: %s\n", 1347 bundle->getPreferredDensity().string()); 1348 return UNKNOWN_ERROR; 1349 } 1350 preferredDensity = preferredConfig.density; 1351 } 1352 1353 if (reqFilter.isEmpty() && preferredDensity == 0) { 1354 return NO_ERROR; 1355 } 1356 1357 if (bundle->getVerbose()) { 1358 if (!reqFilter.isEmpty()) { 1359 printf("Applying required filter: %s\n", 1360 bundle->getConfigurations().string()); 1361 } 1362 if (preferredDensity > 0) { 1363 printf("Applying preferred density filter: %s\n", 1364 bundle->getPreferredDensity().string()); 1365 } 1366 } 1367 1368 const Vector<sp<AaptDir> >& resdirs = mResDirs; 1369 const size_t ND = resdirs.size(); 1370 for (size_t i=0; i<ND; i++) { 1371 const sp<AaptDir>& dir = resdirs.itemAt(i); 1372 if (dir->getLeaf() == kValuesDir) { 1373 // The "value" dir is special since a single file defines 1374 // multiple resources, so we can not do filtering on the 1375 // files themselves. 1376 continue; 1377 } 1378 if (dir->getLeaf() == kMipmapDir) { 1379 // We also skip the "mipmap" directory, since the point of this 1380 // is to include all densities without stripping. If you put 1381 // other configurations in here as well they won't be stripped 1382 // either... So don't do that. Seriously. What is wrong with you? 1383 continue; 1384 } 1385 1386 const size_t NG = dir->getFiles().size(); 1387 for (size_t j=0; j<NG; j++) { 1388 sp<AaptGroup> grp = dir->getFiles().valueAt(j); 1389 1390 // First remove any configurations we know we don't need. 1391 for (size_t k=0; k<grp->getFiles().size(); k++) { 1392 sp<AaptFile> file = grp->getFiles().valueAt(k); 1393 if (k == 0 && grp->getFiles().size() == 1) { 1394 // If this is the only file left, we need to keep it. 1395 // Otherwise the resource IDs we are using will be inconsistent 1396 // with what we get when not stripping. Sucky, but at least 1397 // for now we can rely on the back-end doing another filtering 1398 // pass to take this out and leave us with this resource name 1399 // containing no entries. 1400 continue; 1401 } 1402 if (file->getPath().getPathExtension() == ".xml") { 1403 // We can't remove .xml files at this point, because when 1404 // we parse them they may add identifier resources, so 1405 // removing them can cause our resource identifiers to 1406 // become inconsistent. 1407 continue; 1408 } 1409 const ResTable_config& config(file->getGroupEntry().toParams()); 1410 if (!reqFilter.match(config)) { 1411 if (bundle->getVerbose()) { 1412 printf("Pruning unneeded resource: %s\n", 1413 file->getPrintableSource().string()); 1414 } 1415 grp->removeFile(k); 1416 k--; 1417 } 1418 } 1419 1420 // Quick check: no preferred filters, nothing more to do. 1421 if (preferredDensity == 0) { 1422 continue; 1423 } 1424 1425 // Get the preferred density if there is one. We do not match exactly for density. 1426 // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we 1427 // pick xhdpi. 1428 for (size_t k=0; k<grp->getFiles().size(); k++) { 1429 sp<AaptFile> file = grp->getFiles().valueAt(k); 1430 if (k == 0 && grp->getFiles().size() == 1) { 1431 // If this is the only file left, we need to keep it. 1432 // Otherwise the resource IDs we are using will be inconsistent 1433 // with what we get when not stripping. Sucky, but at least 1434 // for now we can rely on the back-end doing another filtering 1435 // pass to take this out and leave us with this resource name 1436 // containing no entries. 1437 continue; 1438 } 1439 if (file->getPath().getPathExtension() == ".xml") { 1440 // We can't remove .xml files at this point, because when 1441 // we parse them they may add identifier resources, so 1442 // removing them can cause our resource identifiers to 1443 // become inconsistent. 1444 continue; 1445 } 1446 const ResTable_config& config(file->getGroupEntry().toParams()); 1447 if (config.density != 0 && config.density != preferredDensity) { 1448 // This is a resource we would prefer not to have. Check 1449 // to see if have a similar variation that we would like 1450 // to have and, if so, we can drop it. 1451 uint32_t bestDensity = config.density; 1452 1453 for (size_t m=0; m<grp->getFiles().size(); m++) { 1454 if (m == k) { 1455 continue; 1456 } 1457 1458 sp<AaptFile> mfile = grp->getFiles().valueAt(m); 1459 const ResTable_config& mconfig(mfile->getGroupEntry().toParams()); 1460 if (AaptConfig::isSameExcept(config, mconfig, ResTable_config::CONFIG_DENSITY)) { 1461 // See if there is a better density resource 1462 if (mconfig.density < bestDensity && 1463 mconfig.density >= preferredDensity && 1464 bestDensity > preferredDensity) { 1465 // This density is our preferred density, or between our best density and 1466 // the preferred density, therefore it is better. 1467 bestDensity = mconfig.density; 1468 } else if (mconfig.density > bestDensity && 1469 bestDensity < preferredDensity) { 1470 // This density is better than our best density and 1471 // our best density was smaller than our preferred 1472 // density, so it is better. 1473 bestDensity = mconfig.density; 1474 } 1475 } 1476 } 1477 1478 if (bestDensity != config.density) { 1479 if (bundle->getVerbose()) { 1480 printf("Pruning unneeded resource: %s\n", 1481 file->getPrintableSource().string()); 1482 } 1483 grp->removeFile(k); 1484 k--; 1485 } 1486 } 1487 } 1488 } 1489 } 1490 1491 return NO_ERROR; 1492 } 1493 1494 sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name) 1495 { 1496 sp<AaptSymbols> sym = mSymbols.valueFor(name); 1497 if (sym == NULL) { 1498 sym = new AaptSymbols(); 1499 mSymbols.add(name, sym); 1500 } 1501 return sym; 1502 } 1503 1504 sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name) 1505 { 1506 sp<AaptSymbols> sym = mJavaSymbols.valueFor(name); 1507 if (sym == NULL) { 1508 sym = new AaptSymbols(); 1509 mJavaSymbols.add(name, sym); 1510 } 1511 return sym; 1512 } 1513 1514 status_t AaptAssets::applyJavaSymbols() 1515 { 1516 size_t N = mJavaSymbols.size(); 1517 for (size_t i=0; i<N; i++) { 1518 const String8& name = mJavaSymbols.keyAt(i); 1519 const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i); 1520 ssize_t pos = mSymbols.indexOfKey(name); 1521 if (pos < 0) { 1522 SourcePos pos; 1523 pos.error("Java symbol dir %s not defined\n", name.string()); 1524 return UNKNOWN_ERROR; 1525 } 1526 //printf("**** applying java symbols in dir %s\n", name.string()); 1527 status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols); 1528 if (err != NO_ERROR) { 1529 return err; 1530 } 1531 } 1532 1533 return NO_ERROR; 1534 } 1535 1536 bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const { 1537 //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n", 1538 // sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0, 1539 // sym.isJavaSymbol ? 1 : 0); 1540 if (!mHavePrivateSymbols) return true; 1541 if (sym.isPublic) return true; 1542 if (includePrivate && sym.isJavaSymbol) return true; 1543 return false; 1544 } 1545 1546 status_t AaptAssets::buildIncludedResources(Bundle* bundle) 1547 { 1548 if (mHaveIncludedAssets) { 1549 return NO_ERROR; 1550 } 1551 1552 // Add in all includes. 1553 const Vector<String8>& includes = bundle->getPackageIncludes(); 1554 const size_t packageIncludeCount = includes.size(); 1555 for (size_t i = 0; i < packageIncludeCount; i++) { 1556 if (bundle->getVerbose()) { 1557 printf("Including resources from package: %s\n", includes[i].string()); 1558 } 1559 1560 if (!mIncludedAssets.addAssetPath(includes[i], NULL)) { 1561 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n", 1562 includes[i].string()); 1563 return UNKNOWN_ERROR; 1564 } 1565 } 1566 1567 const String8& featureOfBase = bundle->getFeatureOfPackage(); 1568 if (!featureOfBase.isEmpty()) { 1569 if (bundle->getVerbose()) { 1570 printf("Including base feature resources from package: %s\n", 1571 featureOfBase.string()); 1572 } 1573 1574 if (!mIncludedAssets.addAssetPath(featureOfBase, NULL)) { 1575 fprintf(stderr, "ERROR: base feature package '%s' not found.\n", 1576 featureOfBase.string()); 1577 return UNKNOWN_ERROR; 1578 } 1579 } 1580 1581 mHaveIncludedAssets = true; 1582 1583 return NO_ERROR; 1584 } 1585 1586 status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file) 1587 { 1588 const ResTable& res = getIncludedResources(); 1589 // XXX dirty! 1590 return const_cast<ResTable&>(res).add(file->getData(), file->getSize()); 1591 } 1592 1593 const ResTable& AaptAssets::getIncludedResources() const 1594 { 1595 return mIncludedAssets.getResources(false); 1596 } 1597 1598 AssetManager& AaptAssets::getAssetManager() 1599 { 1600 return mIncludedAssets; 1601 } 1602 1603 void AaptAssets::print(const String8& prefix) const 1604 { 1605 String8 innerPrefix(prefix); 1606 innerPrefix.append(" "); 1607 String8 innerInnerPrefix(innerPrefix); 1608 innerInnerPrefix.append(" "); 1609 printf("%sConfigurations:\n", prefix.string()); 1610 const size_t N=mGroupEntries.size(); 1611 for (size_t i=0; i<N; i++) { 1612 String8 cname = mGroupEntries.itemAt(i).toDirName(String8()); 1613 printf("%s %s\n", prefix.string(), 1614 cname != "" ? cname.string() : "(default)"); 1615 } 1616 1617 printf("\n%sFiles:\n", prefix.string()); 1618 AaptDir::print(innerPrefix); 1619 1620 printf("\n%sResource Dirs:\n", prefix.string()); 1621 const Vector<sp<AaptDir> >& resdirs = mResDirs; 1622 const size_t NR = resdirs.size(); 1623 for (size_t i=0; i<NR; i++) { 1624 const sp<AaptDir>& d = resdirs.itemAt(i); 1625 printf("%s Type %s\n", prefix.string(), d->getLeaf().string()); 1626 d->print(innerInnerPrefix); 1627 } 1628 } 1629 1630 sp<AaptDir> AaptAssets::resDir(const String8& name) const 1631 { 1632 const Vector<sp<AaptDir> >& resdirs = mResDirs; 1633 const size_t N = resdirs.size(); 1634 for (size_t i=0; i<N; i++) { 1635 const sp<AaptDir>& d = resdirs.itemAt(i); 1636 if (d->getLeaf() == name) { 1637 return d; 1638 } 1639 } 1640 return NULL; 1641 } 1642 1643 bool 1644 valid_symbol_name(const String8& symbol) 1645 { 1646 static char const * const KEYWORDS[] = { 1647 "abstract", "assert", "boolean", "break", 1648 "byte", "case", "catch", "char", "class", "const", "continue", 1649 "default", "do", "double", "else", "enum", "extends", "final", 1650 "finally", "float", "for", "goto", "if", "implements", "import", 1651 "instanceof", "int", "interface", "long", "native", "new", "package", 1652 "private", "protected", "public", "return", "short", "static", 1653 "strictfp", "super", "switch", "synchronized", "this", "throw", 1654 "throws", "transient", "try", "void", "volatile", "while", 1655 "true", "false", "null", 1656 NULL 1657 }; 1658 const char*const* k = KEYWORDS; 1659 const char*const s = symbol.string(); 1660 while (*k) { 1661 if (0 == strcmp(s, *k)) { 1662 return false; 1663 } 1664 k++; 1665 } 1666 return true; 1667 } 1668