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