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