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