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