1// 2// Copyright 2006 The Android Open Source Project 3// 4// Build resource files from raw assets. 5// 6#include "AaptAssets.h" 7#include "AaptUtil.h" 8#include "AaptXml.h" 9#include "CacheUpdater.h" 10#include "CrunchCache.h" 11#include "FileFinder.h" 12#include "Images.h" 13#include "IndentPrinter.h" 14#include "Main.h" 15#include "ResourceTable.h" 16#include "StringPool.h" 17#include "Symbol.h" 18#include "WorkQueue.h" 19#include "XMLNode.h" 20 21#include <algorithm> 22 23// STATUST: mingw does seem to redefine UNKNOWN_ERROR from our enum value, so a cast is necessary. 24 25#if !defined(_WIN32) 26# define ZD "%zd" 27# define ZD_TYPE ssize_t 28# define STATUST(x) x 29#else 30# define ZD "%ld" 31# define ZD_TYPE long 32# define STATUST(x) (status_t)x 33#endif 34 35// Set to true for noisy debug output. 36static const bool kIsDebug = false; 37 38// Number of threads to use for preprocessing images. 39static const size_t MAX_THREADS = 4; 40 41// ========================================================================== 42// ========================================================================== 43// ========================================================================== 44 45class PackageInfo 46{ 47public: 48 PackageInfo() 49 { 50 } 51 ~PackageInfo() 52 { 53 } 54 55 status_t parsePackage(const sp<AaptGroup>& grp); 56}; 57 58// ========================================================================== 59// ========================================================================== 60// ========================================================================== 61 62String8 parseResourceName(const String8& leaf) 63{ 64 const char* firstDot = strchr(leaf.string(), '.'); 65 const char* str = leaf.string(); 66 67 if (firstDot) { 68 return String8(str, firstDot-str); 69 } else { 70 return String8(str); 71 } 72} 73 74ResourceTypeSet::ResourceTypeSet() 75 :RefBase(), 76 KeyedVector<String8,sp<AaptGroup> >() 77{ 78} 79 80FilePathStore::FilePathStore() 81 :RefBase(), 82 Vector<String8>() 83{ 84} 85 86class ResourceDirIterator 87{ 88public: 89 ResourceDirIterator(const sp<ResourceTypeSet>& set, const String8& resType) 90 : mResType(resType), mSet(set), mSetPos(0), mGroupPos(0) 91 { 92 memset(&mParams, 0, sizeof(ResTable_config)); 93 } 94 95 inline const sp<AaptGroup>& getGroup() const { return mGroup; } 96 inline const sp<AaptFile>& getFile() const { return mFile; } 97 98 inline const String8& getBaseName() const { return mBaseName; } 99 inline const String8& getLeafName() const { return mLeafName; } 100 inline String8 getPath() const { return mPath; } 101 inline const ResTable_config& getParams() const { return mParams; } 102 103 enum { 104 EOD = 1 105 }; 106 107 ssize_t next() 108 { 109 while (true) { 110 sp<AaptGroup> group; 111 sp<AaptFile> file; 112 113 // Try to get next file in this current group. 114 if (mGroup != NULL && mGroupPos < mGroup->getFiles().size()) { 115 group = mGroup; 116 file = group->getFiles().valueAt(mGroupPos++); 117 118 // Try to get the next group/file in this directory 119 } else if (mSetPos < mSet->size()) { 120 mGroup = group = mSet->valueAt(mSetPos++); 121 if (group->getFiles().size() < 1) { 122 continue; 123 } 124 file = group->getFiles().valueAt(0); 125 mGroupPos = 1; 126 127 // All done! 128 } else { 129 return EOD; 130 } 131 132 mFile = file; 133 134 String8 leaf(group->getLeaf()); 135 mLeafName = String8(leaf); 136 mParams = file->getGroupEntry().toParams(); 137 if (kIsDebug) { 138 printf("Dir %s: mcc=%d mnc=%d lang=%c%c cnt=%c%c orient=%d ui=%d density=%d touch=%d key=%d inp=%d nav=%d\n", 139 group->getPath().string(), mParams.mcc, mParams.mnc, 140 mParams.language[0] ? mParams.language[0] : '-', 141 mParams.language[1] ? mParams.language[1] : '-', 142 mParams.country[0] ? mParams.country[0] : '-', 143 mParams.country[1] ? mParams.country[1] : '-', 144 mParams.orientation, mParams.uiMode, 145 mParams.density, mParams.touchscreen, mParams.keyboard, 146 mParams.inputFlags, mParams.navigation); 147 } 148 mPath = "res"; 149 mPath.appendPath(file->getGroupEntry().toDirName(mResType)); 150 mPath.appendPath(leaf); 151 mBaseName = parseResourceName(leaf); 152 if (mBaseName == "") { 153 fprintf(stderr, "Error: malformed resource filename %s\n", 154 file->getPrintableSource().string()); 155 return UNKNOWN_ERROR; 156 } 157 158 if (kIsDebug) { 159 printf("file name=%s\n", mBaseName.string()); 160 } 161 162 return NO_ERROR; 163 } 164 } 165 166private: 167 String8 mResType; 168 169 const sp<ResourceTypeSet> mSet; 170 size_t mSetPos; 171 172 sp<AaptGroup> mGroup; 173 size_t mGroupPos; 174 175 sp<AaptFile> mFile; 176 String8 mBaseName; 177 String8 mLeafName; 178 String8 mPath; 179 ResTable_config mParams; 180}; 181 182class AnnotationProcessor { 183public: 184 AnnotationProcessor() : mDeprecated(false), mSystemApi(false) { } 185 186 void preprocessComment(String8& comment) { 187 if (comment.size() > 0) { 188 if (comment.contains("@deprecated")) { 189 mDeprecated = true; 190 } 191 if (comment.removeAll("@SystemApi")) { 192 mSystemApi = true; 193 } 194 } 195 } 196 197 void printAnnotations(FILE* fp, const char* indentStr) { 198 if (mDeprecated) { 199 fprintf(fp, "%s@Deprecated\n", indentStr); 200 } 201 if (mSystemApi) { 202 fprintf(fp, "%s@android.annotation.SystemApi\n", indentStr); 203 } 204 } 205 206private: 207 bool mDeprecated; 208 bool mSystemApi; 209}; 210 211// ========================================================================== 212// ========================================================================== 213// ========================================================================== 214 215bool isValidResourceType(const String8& type) 216{ 217 return type == "anim" || type == "animator" || type == "interpolator" 218 || type == "transition" 219 || type == "drawable" || type == "layout" 220 || type == "values" || type == "xml" || type == "raw" 221 || type == "color" || type == "menu" || type == "mipmap"; 222} 223 224static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets, 225 const sp<AaptGroup>& grp) 226{ 227 if (grp->getFiles().size() != 1) { 228 fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n", 229 grp->getFiles().valueAt(0)->getPrintableSource().string()); 230 } 231 232 sp<AaptFile> file = grp->getFiles().valueAt(0); 233 234 ResXMLTree block; 235 status_t err = parseXMLResource(file, &block); 236 if (err != NO_ERROR) { 237 return err; 238 } 239 //printXMLBlock(&block); 240 241 ResXMLTree::event_code_t code; 242 while ((code=block.next()) != ResXMLTree::START_TAG 243 && code != ResXMLTree::END_DOCUMENT 244 && code != ResXMLTree::BAD_DOCUMENT) { 245 } 246 247 size_t len; 248 if (code != ResXMLTree::START_TAG) { 249 fprintf(stderr, "%s:%d: No start tag found\n", 250 file->getPrintableSource().string(), block.getLineNumber()); 251 return UNKNOWN_ERROR; 252 } 253 if (strcmp16(block.getElementName(&len), String16("manifest").string()) != 0) { 254 fprintf(stderr, "%s:%d: Invalid start tag %s, expected <manifest>\n", 255 file->getPrintableSource().string(), block.getLineNumber(), 256 String8(block.getElementName(&len)).string()); 257 return UNKNOWN_ERROR; 258 } 259 260 ssize_t nameIndex = block.indexOfAttribute(NULL, "package"); 261 if (nameIndex < 0) { 262 fprintf(stderr, "%s:%d: <manifest> does not have package attribute.\n", 263 file->getPrintableSource().string(), block.getLineNumber()); 264 return UNKNOWN_ERROR; 265 } 266 267 assets->setPackage(String8(block.getAttributeStringValue(nameIndex, &len))); 268 269 ssize_t revisionCodeIndex = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "revisionCode"); 270 if (revisionCodeIndex >= 0) { 271 bundle->setRevisionCode(String8(block.getAttributeStringValue(revisionCodeIndex, &len)).string()); 272 } 273 274 String16 uses_sdk16("uses-sdk"); 275 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 276 && code != ResXMLTree::BAD_DOCUMENT) { 277 if (code == ResXMLTree::START_TAG) { 278 if (strcmp16(block.getElementName(&len), uses_sdk16.string()) == 0) { 279 ssize_t minSdkIndex = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, 280 "minSdkVersion"); 281 if (minSdkIndex >= 0) { 282 const char16_t* minSdk16 = block.getAttributeStringValue(minSdkIndex, &len); 283 const char* minSdk8 = strdup(String8(minSdk16).string()); 284 bundle->setManifestMinSdkVersion(minSdk8); 285 } 286 } 287 } 288 } 289 290 return NO_ERROR; 291} 292 293// ========================================================================== 294// ========================================================================== 295// ========================================================================== 296 297static status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets, 298 ResourceTable* table, 299 const sp<ResourceTypeSet>& set, 300 const char* resType) 301{ 302 String8 type8(resType); 303 String16 type16(resType); 304 305 bool hasErrors = false; 306 307 ResourceDirIterator it(set, String8(resType)); 308 ssize_t res; 309 while ((res=it.next()) == NO_ERROR) { 310 if (bundle->getVerbose()) { 311 printf(" (new resource id %s from %s)\n", 312 it.getBaseName().string(), it.getFile()->getPrintableSource().string()); 313 } 314 String16 baseName(it.getBaseName()); 315 const char16_t* str = baseName.string(); 316 const char16_t* const end = str + baseName.size(); 317 while (str < end) { 318 if (!((*str >= 'a' && *str <= 'z') 319 || (*str >= '0' && *str <= '9') 320 || *str == '_' || *str == '.')) { 321 fprintf(stderr, "%s: Invalid file name: must contain only [a-z0-9_.]\n", 322 it.getPath().string()); 323 hasErrors = true; 324 } 325 str++; 326 } 327 String8 resPath = it.getPath(); 328 resPath.convertToResPath(); 329 table->addEntry(SourcePos(it.getPath(), 0), String16(assets->getPackage()), 330 type16, 331 baseName, 332 String16(resPath), 333 NULL, 334 &it.getParams()); 335 assets->addResource(it.getLeafName(), resPath, it.getFile(), type8); 336 } 337 338 return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR; 339} 340 341class PreProcessImageWorkUnit : public WorkQueue::WorkUnit { 342public: 343 PreProcessImageWorkUnit(const Bundle* bundle, const sp<AaptAssets>& assets, 344 const sp<AaptFile>& file, volatile bool* hasErrors) : 345 mBundle(bundle), mAssets(assets), mFile(file), mHasErrors(hasErrors) { 346 } 347 348 virtual bool run() { 349 status_t status = preProcessImage(mBundle, mAssets, mFile, NULL); 350 if (status) { 351 *mHasErrors = true; 352 } 353 return true; // continue even if there are errors 354 } 355 356private: 357 const Bundle* mBundle; 358 sp<AaptAssets> mAssets; 359 sp<AaptFile> mFile; 360 volatile bool* mHasErrors; 361}; 362 363static status_t preProcessImages(const Bundle* bundle, const sp<AaptAssets>& assets, 364 const sp<ResourceTypeSet>& set, const char* type) 365{ 366 volatile bool hasErrors = false; 367 ssize_t res = NO_ERROR; 368 if (bundle->getUseCrunchCache() == false) { 369 WorkQueue wq(MAX_THREADS, false); 370 ResourceDirIterator it(set, String8(type)); 371 while ((res=it.next()) == NO_ERROR) { 372 PreProcessImageWorkUnit* w = new PreProcessImageWorkUnit( 373 bundle, assets, it.getFile(), &hasErrors); 374 status_t status = wq.schedule(w); 375 if (status) { 376 fprintf(stderr, "preProcessImages failed: schedule() returned %d\n", status); 377 hasErrors = true; 378 delete w; 379 break; 380 } 381 } 382 status_t status = wq.finish(); 383 if (status) { 384 fprintf(stderr, "preProcessImages failed: finish() returned %d\n", status); 385 hasErrors = true; 386 } 387 } 388 return (hasErrors || (res < NO_ERROR)) ? STATUST(UNKNOWN_ERROR) : NO_ERROR; 389} 390 391static void collect_files(const sp<AaptDir>& dir, 392 KeyedVector<String8, sp<ResourceTypeSet> >* resources) 393{ 394 const DefaultKeyedVector<String8, sp<AaptGroup> >& groups = dir->getFiles(); 395 int N = groups.size(); 396 for (int i=0; i<N; i++) { 397 String8 leafName = groups.keyAt(i); 398 const sp<AaptGroup>& group = groups.valueAt(i); 399 400 const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files 401 = group->getFiles(); 402 403 if (files.size() == 0) { 404 continue; 405 } 406 407 String8 resType = files.valueAt(0)->getResourceType(); 408 409 ssize_t index = resources->indexOfKey(resType); 410 411 if (index < 0) { 412 sp<ResourceTypeSet> set = new ResourceTypeSet(); 413 if (kIsDebug) { 414 printf("Creating new resource type set for leaf %s with group %s (%p)\n", 415 leafName.string(), group->getPath().string(), group.get()); 416 } 417 set->add(leafName, group); 418 resources->add(resType, set); 419 } else { 420 sp<ResourceTypeSet> set = resources->valueAt(index); 421 index = set->indexOfKey(leafName); 422 if (index < 0) { 423 if (kIsDebug) { 424 printf("Adding to resource type set for leaf %s group %s (%p)\n", 425 leafName.string(), group->getPath().string(), group.get()); 426 } 427 set->add(leafName, group); 428 } else { 429 sp<AaptGroup> existingGroup = set->valueAt(index); 430 if (kIsDebug) { 431 printf("Extending to resource type set for leaf %s group %s (%p)\n", 432 leafName.string(), group->getPath().string(), group.get()); 433 } 434 for (size_t j=0; j<files.size(); j++) { 435 if (kIsDebug) { 436 printf("Adding file %s in group %s resType %s\n", 437 files.valueAt(j)->getSourceFile().string(), 438 files.keyAt(j).toDirName(String8()).string(), 439 resType.string()); 440 } 441 existingGroup->addFile(files.valueAt(j)); 442 } 443 } 444 } 445 } 446} 447 448static void collect_files(const sp<AaptAssets>& ass, 449 KeyedVector<String8, sp<ResourceTypeSet> >* resources) 450{ 451 const Vector<sp<AaptDir> >& dirs = ass->resDirs(); 452 int N = dirs.size(); 453 454 for (int i=0; i<N; i++) { 455 sp<AaptDir> d = dirs.itemAt(i); 456 if (kIsDebug) { 457 printf("Collecting dir #%d %p: %s, leaf %s\n", i, d.get(), d->getPath().string(), 458 d->getLeaf().string()); 459 } 460 collect_files(d, resources); 461 462 // don't try to include the res dir 463 if (kIsDebug) { 464 printf("Removing dir leaf %s\n", d->getLeaf().string()); 465 } 466 ass->removeDir(d->getLeaf()); 467 } 468} 469 470enum { 471 ATTR_OKAY = -1, 472 ATTR_NOT_FOUND = -2, 473 ATTR_LEADING_SPACES = -3, 474 ATTR_TRAILING_SPACES = -4 475}; 476static int validateAttr(const String8& path, const ResTable& table, 477 const ResXMLParser& parser, 478 const char* ns, const char* attr, const char* validChars, bool required) 479{ 480 size_t len; 481 482 ssize_t index = parser.indexOfAttribute(ns, attr); 483 const char16_t* str; 484 Res_value value; 485 if (index >= 0 && parser.getAttributeValue(index, &value) >= 0) { 486 const ResStringPool* pool = &parser.getStrings(); 487 if (value.dataType == Res_value::TYPE_REFERENCE) { 488 uint32_t specFlags = 0; 489 int strIdx; 490 if ((strIdx=table.resolveReference(&value, 0x10000000, NULL, &specFlags)) < 0) { 491 fprintf(stderr, "%s:%d: Tag <%s> attribute %s references unknown resid 0x%08x.\n", 492 path.string(), parser.getLineNumber(), 493 String8(parser.getElementName(&len)).string(), attr, 494 value.data); 495 return ATTR_NOT_FOUND; 496 } 497 498 pool = table.getTableStringBlock(strIdx); 499 #if 0 500 if (pool != NULL) { 501 str = pool->stringAt(value.data, &len); 502 } 503 printf("***** RES ATTR: %s specFlags=0x%x strIdx=%d: %s\n", attr, 504 specFlags, strIdx, str != NULL ? String8(str).string() : "???"); 505 #endif 506 if ((specFlags&~ResTable_typeSpec::SPEC_PUBLIC) != 0 && false) { 507 fprintf(stderr, "%s:%d: Tag <%s> attribute %s varies by configurations 0x%x.\n", 508 path.string(), parser.getLineNumber(), 509 String8(parser.getElementName(&len)).string(), attr, 510 specFlags); 511 return ATTR_NOT_FOUND; 512 } 513 } 514 if (value.dataType == Res_value::TYPE_STRING) { 515 if (pool == NULL) { 516 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has no string block.\n", 517 path.string(), parser.getLineNumber(), 518 String8(parser.getElementName(&len)).string(), attr); 519 return ATTR_NOT_FOUND; 520 } 521 if ((str=pool->stringAt(value.data, &len)) == NULL) { 522 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has corrupt string value.\n", 523 path.string(), parser.getLineNumber(), 524 String8(parser.getElementName(&len)).string(), attr); 525 return ATTR_NOT_FOUND; 526 } 527 } else { 528 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid type %d.\n", 529 path.string(), parser.getLineNumber(), 530 String8(parser.getElementName(&len)).string(), attr, 531 value.dataType); 532 return ATTR_NOT_FOUND; 533 } 534 if (validChars) { 535 for (size_t i=0; i<len; i++) { 536 char16_t c = str[i]; 537 const char* p = validChars; 538 bool okay = false; 539 while (*p) { 540 if (c == *p) { 541 okay = true; 542 break; 543 } 544 p++; 545 } 546 if (!okay) { 547 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid character '%c'.\n", 548 path.string(), parser.getLineNumber(), 549 String8(parser.getElementName(&len)).string(), attr, (char)str[i]); 550 return (int)i; 551 } 552 } 553 } 554 if (*str == ' ') { 555 fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not start with a space.\n", 556 path.string(), parser.getLineNumber(), 557 String8(parser.getElementName(&len)).string(), attr); 558 return ATTR_LEADING_SPACES; 559 } 560 if (len != 0 && str[len-1] == ' ') { 561 fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not end with a space.\n", 562 path.string(), parser.getLineNumber(), 563 String8(parser.getElementName(&len)).string(), attr); 564 return ATTR_TRAILING_SPACES; 565 } 566 return ATTR_OKAY; 567 } 568 if (required) { 569 fprintf(stderr, "%s:%d: Tag <%s> missing required attribute %s.\n", 570 path.string(), parser.getLineNumber(), 571 String8(parser.getElementName(&len)).string(), attr); 572 return ATTR_NOT_FOUND; 573 } 574 return ATTR_OKAY; 575} 576 577static void checkForIds(const String8& path, ResXMLParser& parser) 578{ 579 ResXMLTree::event_code_t code; 580 while ((code=parser.next()) != ResXMLTree::END_DOCUMENT 581 && code > ResXMLTree::BAD_DOCUMENT) { 582 if (code == ResXMLTree::START_TAG) { 583 ssize_t index = parser.indexOfAttribute(NULL, "id"); 584 if (index >= 0) { 585 fprintf(stderr, "%s:%d: warning: found plain 'id' attribute; did you mean the new 'android:id' name?\n", 586 path.string(), parser.getLineNumber()); 587 } 588 } 589 } 590} 591 592static bool applyFileOverlay(Bundle *bundle, 593 const sp<AaptAssets>& assets, 594 sp<ResourceTypeSet> *baseSet, 595 const char *resType) 596{ 597 if (bundle->getVerbose()) { 598 printf("applyFileOverlay for %s\n", resType); 599 } 600 601 // Replace any base level files in this category with any found from the overlay 602 // Also add any found only in the overlay. 603 sp<AaptAssets> overlay = assets->getOverlay(); 604 String8 resTypeString(resType); 605 606 // work through the linked list of overlays 607 while (overlay.get()) { 608 KeyedVector<String8, sp<ResourceTypeSet> >* overlayRes = overlay->getResources(); 609 610 // get the overlay resources of the requested type 611 ssize_t index = overlayRes->indexOfKey(resTypeString); 612 if (index >= 0) { 613 sp<ResourceTypeSet> overlaySet = overlayRes->valueAt(index); 614 615 // for each of the resources, check for a match in the previously built 616 // non-overlay "baseset". 617 size_t overlayCount = overlaySet->size(); 618 for (size_t overlayIndex=0; overlayIndex<overlayCount; overlayIndex++) { 619 if (bundle->getVerbose()) { 620 printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string()); 621 } 622 ssize_t baseIndex = -1; 623 if (baseSet->get() != NULL) { 624 baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex)); 625 } 626 if (baseIndex >= 0) { 627 // look for same flavor. For a given file (strings.xml, for example) 628 // there may be a locale specific or other flavors - we want to match 629 // the same flavor. 630 sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex); 631 sp<AaptGroup> baseGroup = (*baseSet)->valueAt(baseIndex); 632 633 DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles = 634 overlayGroup->getFiles(); 635 if (bundle->getVerbose()) { 636 DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles = 637 baseGroup->getFiles(); 638 for (size_t i=0; i < baseFiles.size(); i++) { 639 printf("baseFile " ZD " has flavor %s\n", (ZD_TYPE) i, 640 baseFiles.keyAt(i).toString().string()); 641 } 642 for (size_t i=0; i < overlayFiles.size(); i++) { 643 printf("overlayFile " ZD " has flavor %s\n", (ZD_TYPE) i, 644 overlayFiles.keyAt(i).toString().string()); 645 } 646 } 647 648 size_t overlayGroupSize = overlayFiles.size(); 649 for (size_t overlayGroupIndex = 0; 650 overlayGroupIndex<overlayGroupSize; 651 overlayGroupIndex++) { 652 ssize_t baseFileIndex = 653 baseGroup->getFiles().indexOfKey(overlayFiles. 654 keyAt(overlayGroupIndex)); 655 if (baseFileIndex >= 0) { 656 if (bundle->getVerbose()) { 657 printf("found a match (" ZD ") for overlay file %s, for flavor %s\n", 658 (ZD_TYPE) baseFileIndex, 659 overlayGroup->getLeaf().string(), 660 overlayFiles.keyAt(overlayGroupIndex).toString().string()); 661 } 662 baseGroup->removeFile(baseFileIndex); 663 } else { 664 // didn't find a match fall through and add it.. 665 if (true || bundle->getVerbose()) { 666 printf("nothing matches overlay file %s, for flavor %s\n", 667 overlayGroup->getLeaf().string(), 668 overlayFiles.keyAt(overlayGroupIndex).toString().string()); 669 } 670 } 671 baseGroup->addFile(overlayFiles.valueAt(overlayGroupIndex)); 672 assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex)); 673 } 674 } else { 675 if (baseSet->get() == NULL) { 676 *baseSet = new ResourceTypeSet(); 677 assets->getResources()->add(String8(resType), *baseSet); 678 } 679 // this group doesn't exist (a file that's only in the overlay) 680 (*baseSet)->add(overlaySet->keyAt(overlayIndex), 681 overlaySet->valueAt(overlayIndex)); 682 // make sure all flavors are defined in the resources. 683 sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex); 684 DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles = 685 overlayGroup->getFiles(); 686 size_t overlayGroupSize = overlayFiles.size(); 687 for (size_t overlayGroupIndex = 0; 688 overlayGroupIndex<overlayGroupSize; 689 overlayGroupIndex++) { 690 assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex)); 691 } 692 } 693 } 694 // this overlay didn't have resources for this type 695 } 696 // try next overlay 697 overlay = overlay->getOverlay(); 698 } 699 return true; 700} 701 702/* 703 * Inserts an attribute in a given node. 704 * If errorOnFailedInsert is true, and the attribute already exists, returns false. 705 * If replaceExisting is true, the attribute will be updated if it already exists. 706 * Returns true otherwise, even if the attribute already exists, and does not modify 707 * the existing attribute's value. 708 */ 709bool addTagAttribute(const sp<XMLNode>& node, const char* ns8, 710 const char* attr8, const char* value, bool errorOnFailedInsert, 711 bool replaceExisting) 712{ 713 if (value == NULL) { 714 return true; 715 } 716 717 const String16 ns(ns8); 718 const String16 attr(attr8); 719 720 XMLNode::attribute_entry* existingEntry = node->editAttribute(ns, attr); 721 if (existingEntry != NULL) { 722 if (replaceExisting) { 723 if (kIsDebug) { 724 printf("Info: AndroidManifest.xml already defines %s (in %s);" 725 " overwriting existing value from manifest.\n", 726 String8(attr).string(), String8(ns).string()); 727 } 728 existingEntry->string = String16(value); 729 return true; 730 } 731 732 if (errorOnFailedInsert) { 733 fprintf(stderr, "Error: AndroidManifest.xml already defines %s (in %s);" 734 " cannot insert new value %s.\n", 735 String8(attr).string(), String8(ns).string(), value); 736 return false; 737 } 738 739 fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s);" 740 " using existing value in manifest.\n", 741 String8(attr).string(), String8(ns).string()); 742 743 // don't stop the build. 744 return true; 745 } 746 747 node->addAttribute(ns, attr, String16(value)); 748 return true; 749} 750 751/* 752 * Inserts an attribute in a given node, only if the attribute does not 753 * exist. 754 * If errorOnFailedInsert is true, and the attribute already exists, returns false. 755 * Returns true otherwise, even if the attribute already exists. 756 */ 757bool addTagAttribute(const sp<XMLNode>& node, const char* ns8, 758 const char* attr8, const char* value, bool errorOnFailedInsert) 759{ 760 return addTagAttribute(node, ns8, attr8, value, errorOnFailedInsert, false); 761} 762 763static void fullyQualifyClassName(const String8& package, sp<XMLNode> node, 764 const String16& attrName) { 765 XMLNode::attribute_entry* attr = node->editAttribute( 766 String16("http://schemas.android.com/apk/res/android"), attrName); 767 if (attr != NULL) { 768 String8 name(attr->string); 769 770 // asdf --> package.asdf 771 // .asdf .a.b --> package.asdf package.a.b 772 // asdf.adsf --> asdf.asdf 773 String8 className; 774 const char* p = name.string(); 775 const char* q = strchr(p, '.'); 776 if (p == q) { 777 className += package; 778 className += name; 779 } else if (q == NULL) { 780 className += package; 781 className += "."; 782 className += name; 783 } else { 784 className += name; 785 } 786 if (kIsDebug) { 787 printf("Qualifying class '%s' to '%s'", name.string(), className.string()); 788 } 789 attr->string.setTo(String16(className)); 790 } 791} 792 793status_t massageManifest(Bundle* bundle, sp<XMLNode> root) 794{ 795 root = root->searchElement(String16(), String16("manifest")); 796 if (root == NULL) { 797 fprintf(stderr, "No <manifest> tag.\n"); 798 return UNKNOWN_ERROR; 799 } 800 801 bool errorOnFailedInsert = bundle->getErrorOnFailedInsert(); 802 bool replaceVersion = bundle->getReplaceVersion(); 803 804 if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode", 805 bundle->getVersionCode(), errorOnFailedInsert, replaceVersion)) { 806 return UNKNOWN_ERROR; 807 } else { 808 const XMLNode::attribute_entry* attr = root->getAttribute( 809 String16(RESOURCES_ANDROID_NAMESPACE), String16("versionCode")); 810 if (attr != NULL) { 811 bundle->setVersionCode(strdup(String8(attr->string).string())); 812 } 813 } 814 815 if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName", 816 bundle->getVersionName(), errorOnFailedInsert, replaceVersion)) { 817 return UNKNOWN_ERROR; 818 } else { 819 const XMLNode::attribute_entry* attr = root->getAttribute( 820 String16(RESOURCES_ANDROID_NAMESPACE), String16("versionName")); 821 if (attr != NULL) { 822 bundle->setVersionName(strdup(String8(attr->string).string())); 823 } 824 } 825 826 sp<XMLNode> vers = root->getChildElement(String16(), String16("uses-sdk")); 827 if (bundle->getMinSdkVersion() != NULL 828 || bundle->getTargetSdkVersion() != NULL 829 || bundle->getMaxSdkVersion() != NULL) { 830 if (vers == NULL) { 831 vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk")); 832 root->insertChildAt(vers, 0); 833 } 834 835 if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion", 836 bundle->getMinSdkVersion(), errorOnFailedInsert)) { 837 return UNKNOWN_ERROR; 838 } 839 if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion", 840 bundle->getTargetSdkVersion(), errorOnFailedInsert)) { 841 return UNKNOWN_ERROR; 842 } 843 if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion", 844 bundle->getMaxSdkVersion(), errorOnFailedInsert)) { 845 return UNKNOWN_ERROR; 846 } 847 } 848 849 if (vers != NULL) { 850 const XMLNode::attribute_entry* attr = vers->getAttribute( 851 String16(RESOURCES_ANDROID_NAMESPACE), String16("minSdkVersion")); 852 if (attr != NULL) { 853 bundle->setMinSdkVersion(strdup(String8(attr->string).string())); 854 } 855 } 856 857 if (bundle->getPlatformBuildVersionCode() != "") { 858 if (!addTagAttribute(root, "", "platformBuildVersionCode", 859 bundle->getPlatformBuildVersionCode(), errorOnFailedInsert, true)) { 860 return UNKNOWN_ERROR; 861 } 862 } 863 864 if (bundle->getPlatformBuildVersionName() != "") { 865 if (!addTagAttribute(root, "", "platformBuildVersionName", 866 bundle->getPlatformBuildVersionName(), errorOnFailedInsert, true)) { 867 return UNKNOWN_ERROR; 868 } 869 } 870 871 if (bundle->getDebugMode()) { 872 sp<XMLNode> application = root->getChildElement(String16(), String16("application")); 873 if (application != NULL) { 874 if (!addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true", 875 errorOnFailedInsert)) { 876 return UNKNOWN_ERROR; 877 } 878 } 879 } 880 881 // Deal with manifest package name overrides 882 const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride(); 883 if (manifestPackageNameOverride != NULL) { 884 // Update the actual package name 885 XMLNode::attribute_entry* attr = root->editAttribute(String16(), String16("package")); 886 if (attr == NULL) { 887 fprintf(stderr, "package name is required with --rename-manifest-package.\n"); 888 return UNKNOWN_ERROR; 889 } 890 String8 origPackage(attr->string); 891 attr->string.setTo(String16(manifestPackageNameOverride)); 892 if (kIsDebug) { 893 printf("Overriding package '%s' to be '%s'\n", origPackage.string(), 894 manifestPackageNameOverride); 895 } 896 897 // Make class names fully qualified 898 sp<XMLNode> application = root->getChildElement(String16(), String16("application")); 899 if (application != NULL) { 900 fullyQualifyClassName(origPackage, application, String16("name")); 901 fullyQualifyClassName(origPackage, application, String16("backupAgent")); 902 903 Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(application->getChildren()); 904 for (size_t i = 0; i < children.size(); i++) { 905 sp<XMLNode> child = children.editItemAt(i); 906 String8 tag(child->getElementName()); 907 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") { 908 fullyQualifyClassName(origPackage, child, String16("name")); 909 } else if (tag == "activity-alias") { 910 fullyQualifyClassName(origPackage, child, String16("name")); 911 fullyQualifyClassName(origPackage, child, String16("targetActivity")); 912 } 913 } 914 } 915 } 916 917 // Deal with manifest package name overrides 918 const char* instrumentationPackageNameOverride = bundle->getInstrumentationPackageNameOverride(); 919 if (instrumentationPackageNameOverride != NULL) { 920 // Fix up instrumentation targets. 921 Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(root->getChildren()); 922 for (size_t i = 0; i < children.size(); i++) { 923 sp<XMLNode> child = children.editItemAt(i); 924 String8 tag(child->getElementName()); 925 if (tag == "instrumentation") { 926 XMLNode::attribute_entry* attr = child->editAttribute( 927 String16("http://schemas.android.com/apk/res/android"), String16("targetPackage")); 928 if (attr != NULL) { 929 attr->string.setTo(String16(instrumentationPackageNameOverride)); 930 } 931 } 932 } 933 } 934 935 // Generate split name if feature is present. 936 const XMLNode::attribute_entry* attr = root->getAttribute(String16(), String16("featureName")); 937 if (attr != NULL) { 938 String16 splitName("feature_"); 939 splitName.append(attr->string); 940 status_t err = root->addAttribute(String16(), String16("split"), splitName); 941 if (err != NO_ERROR) { 942 ALOGE("Failed to insert split name into AndroidManifest.xml"); 943 return err; 944 } 945 } 946 947 return NO_ERROR; 948} 949 950static int32_t getPlatformAssetCookie(const AssetManager& assets) { 951 // Find the system package (0x01). AAPT always generates attributes 952 // with the type 0x01, so we're looking for the first attribute 953 // resource in the system package. 954 const ResTable& table = assets.getResources(true); 955 Res_value val; 956 ssize_t idx = table.getResource(0x01010000, &val, true); 957 if (idx != NO_ERROR) { 958 // Try as a bag. 959 const ResTable::bag_entry* entry; 960 ssize_t cnt = table.lockBag(0x01010000, &entry); 961 if (cnt >= 0) { 962 idx = entry->stringBlock; 963 } 964 table.unlockBag(entry); 965 } 966 967 if (idx < 0) { 968 return 0; 969 } 970 return table.getTableCookie(idx); 971} 972 973enum { 974 VERSION_CODE_ATTR = 0x0101021b, 975 VERSION_NAME_ATTR = 0x0101021c, 976}; 977 978static ssize_t extractPlatformBuildVersion(ResXMLTree& tree, Bundle* bundle) { 979 size_t len; 980 ResXMLTree::event_code_t code; 981 while ((code = tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 982 if (code != ResXMLTree::START_TAG) { 983 continue; 984 } 985 986 const char16_t* ctag16 = tree.getElementName(&len); 987 if (ctag16 == NULL) { 988 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n"); 989 return UNKNOWN_ERROR; 990 } 991 992 String8 tag(ctag16, len); 993 if (tag != "manifest") { 994 continue; 995 } 996 997 String8 error; 998 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR, &error); 999 if (error != "") { 1000 fprintf(stderr, "ERROR: failed to get platform version code\n"); 1001 return UNKNOWN_ERROR; 1002 } 1003 1004 if (versionCode >= 0 && bundle->getPlatformBuildVersionCode() == "") { 1005 bundle->setPlatformBuildVersionCode(String8::format("%d", versionCode)); 1006 } 1007 1008 String8 versionName = AaptXml::getAttribute(tree, VERSION_NAME_ATTR, &error); 1009 if (error != "") { 1010 fprintf(stderr, "ERROR: failed to get platform version name\n"); 1011 return UNKNOWN_ERROR; 1012 } 1013 1014 if (versionName != "" && bundle->getPlatformBuildVersionName() == "") { 1015 bundle->setPlatformBuildVersionName(versionName); 1016 } 1017 return NO_ERROR; 1018 } 1019 1020 fprintf(stderr, "ERROR: no <manifest> tag found in platform AndroidManifest.xml\n"); 1021 return UNKNOWN_ERROR; 1022} 1023 1024static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) { 1025 int32_t cookie = getPlatformAssetCookie(assets); 1026 if (cookie == 0) { 1027 // No platform was loaded. 1028 return NO_ERROR; 1029 } 1030 1031 ResXMLTree tree; 1032 Asset* asset = assets.openNonAsset(cookie, "AndroidManifest.xml", Asset::ACCESS_STREAMING); 1033 if (asset == NULL) { 1034 fprintf(stderr, "ERROR: Platform AndroidManifest.xml not found\n"); 1035 return UNKNOWN_ERROR; 1036 } 1037 1038 ssize_t result = NO_ERROR; 1039 if (tree.setTo(asset->getBuffer(true), asset->getLength()) != NO_ERROR) { 1040 fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n"); 1041 result = UNKNOWN_ERROR; 1042 } else { 1043 result = extractPlatformBuildVersion(tree, bundle); 1044 } 1045 1046 delete asset; 1047 return result; 1048} 1049 1050#define ASSIGN_IT(n) \ 1051 do { \ 1052 ssize_t index = resources->indexOfKey(String8(#n)); \ 1053 if (index >= 0) { \ 1054 n ## s = resources->valueAt(index); \ 1055 } \ 1056 } while (0) 1057 1058status_t updatePreProcessedCache(Bundle* bundle) 1059{ 1060 #if BENCHMARK 1061 fprintf(stdout, "BENCHMARK: Starting PNG PreProcessing \n"); 1062 long startPNGTime = clock(); 1063 #endif /* BENCHMARK */ 1064 1065 String8 source(bundle->getResourceSourceDirs()[0]); 1066 String8 dest(bundle->getCrunchedOutputDir()); 1067 1068 FileFinder* ff = new SystemFileFinder(); 1069 CrunchCache cc(source,dest,ff); 1070 1071 CacheUpdater* cu = new SystemCacheUpdater(bundle); 1072 size_t numFiles = cc.crunch(cu); 1073 1074 if (bundle->getVerbose()) 1075 fprintf(stdout, "Crunched %d PNG files to update cache\n", (int)numFiles); 1076 1077 delete ff; 1078 delete cu; 1079 1080 #if BENCHMARK 1081 fprintf(stdout, "BENCHMARK: End PNG PreProcessing. Time Elapsed: %f ms \n" 1082 ,(clock() - startPNGTime)/1000.0); 1083 #endif /* BENCHMARK */ 1084 return 0; 1085} 1086 1087status_t generateAndroidManifestForSplit(Bundle* bundle, const sp<AaptAssets>& assets, 1088 const sp<ApkSplit>& split, sp<AaptFile>& outFile, ResourceTable* table) { 1089 const String8 filename("AndroidManifest.xml"); 1090 const String16 androidPrefix("android"); 1091 const String16 androidNSUri("http://schemas.android.com/apk/res/android"); 1092 sp<XMLNode> root = XMLNode::newNamespace(filename, androidPrefix, androidNSUri); 1093 1094 // Build the <manifest> tag 1095 sp<XMLNode> manifest = XMLNode::newElement(filename, String16(), String16("manifest")); 1096 1097 // Add the 'package' attribute which is set to the package name. 1098 const char* packageName = assets->getPackage(); 1099 const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride(); 1100 if (manifestPackageNameOverride != NULL) { 1101 packageName = manifestPackageNameOverride; 1102 } 1103 manifest->addAttribute(String16(), String16("package"), String16(packageName)); 1104 1105 // Add the 'versionCode' attribute which is set to the original version code. 1106 if (!addTagAttribute(manifest, RESOURCES_ANDROID_NAMESPACE, "versionCode", 1107 bundle->getVersionCode(), true, true)) { 1108 return UNKNOWN_ERROR; 1109 } 1110 1111 // Add the 'revisionCode' attribute, which is set to the original revisionCode. 1112 if (bundle->getRevisionCode().size() > 0) { 1113 if (!addTagAttribute(manifest, RESOURCES_ANDROID_NAMESPACE, "revisionCode", 1114 bundle->getRevisionCode().string(), true, true)) { 1115 return UNKNOWN_ERROR; 1116 } 1117 } 1118 1119 // Add the 'split' attribute which describes the configurations included. 1120 String8 splitName("config."); 1121 splitName.append(split->getPackageSafeName()); 1122 manifest->addAttribute(String16(), String16("split"), String16(splitName)); 1123 1124 // Build an empty <application> tag (required). 1125 sp<XMLNode> app = XMLNode::newElement(filename, String16(), String16("application")); 1126 1127 // Add the 'hasCode' attribute which is never true for resource splits. 1128 if (!addTagAttribute(app, RESOURCES_ANDROID_NAMESPACE, "hasCode", 1129 "false", true, true)) { 1130 return UNKNOWN_ERROR; 1131 } 1132 1133 manifest->addChild(app); 1134 root->addChild(manifest); 1135 1136 int err = compileXmlFile(bundle, assets, String16(), root, outFile, table); 1137 if (err < NO_ERROR) { 1138 return err; 1139 } 1140 outFile->setCompressionMethod(ZipEntry::kCompressDeflated); 1141 return NO_ERROR; 1142} 1143 1144status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder) 1145{ 1146 // First, look for a package file to parse. This is required to 1147 // be able to generate the resource information. 1148 sp<AaptGroup> androidManifestFile = 1149 assets->getFiles().valueFor(String8("AndroidManifest.xml")); 1150 if (androidManifestFile == NULL) { 1151 fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n"); 1152 return UNKNOWN_ERROR; 1153 } 1154 1155 status_t err = parsePackage(bundle, assets, androidManifestFile); 1156 if (err != NO_ERROR) { 1157 return err; 1158 } 1159 1160 if (kIsDebug) { 1161 printf("Creating resources for package %s\n", assets->getPackage().string()); 1162 } 1163 1164 // Set the private symbols package if it was declared. 1165 // This can also be declared in XML as <private-symbols name="package" /> 1166 if (bundle->getPrivateSymbolsPackage().size() != 0) { 1167 assets->setSymbolsPrivatePackage(bundle->getPrivateSymbolsPackage()); 1168 } 1169 1170 ResourceTable::PackageType packageType = ResourceTable::App; 1171 if (bundle->getBuildSharedLibrary()) { 1172 packageType = ResourceTable::SharedLibrary; 1173 } else if (bundle->getExtending()) { 1174 packageType = ResourceTable::System; 1175 } else if (!bundle->getFeatureOfPackage().isEmpty()) { 1176 packageType = ResourceTable::AppFeature; 1177 } 1178 1179 ResourceTable table(bundle, String16(assets->getPackage()), packageType); 1180 err = table.addIncludedResources(bundle, assets); 1181 if (err != NO_ERROR) { 1182 return err; 1183 } 1184 1185 if (kIsDebug) { 1186 printf("Found %d included resource packages\n", (int)table.size()); 1187 } 1188 1189 // Standard flags for compiled XML and optional UTF-8 encoding 1190 int xmlFlags = XML_COMPILE_STANDARD_RESOURCE; 1191 1192 /* Only enable UTF-8 if the caller of aapt didn't specifically 1193 * request UTF-16 encoding and the parameters of this package 1194 * allow UTF-8 to be used. 1195 */ 1196 if (!bundle->getUTF16StringsOption()) { 1197 xmlFlags |= XML_COMPILE_UTF8; 1198 } 1199 1200 // -------------------------------------------------------------- 1201 // First, gather all resource information. 1202 // -------------------------------------------------------------- 1203 1204 // resType -> leafName -> group 1205 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 1206 new KeyedVector<String8, sp<ResourceTypeSet> >; 1207 collect_files(assets, resources); 1208 1209 sp<ResourceTypeSet> drawables; 1210 sp<ResourceTypeSet> layouts; 1211 sp<ResourceTypeSet> anims; 1212 sp<ResourceTypeSet> animators; 1213 sp<ResourceTypeSet> interpolators; 1214 sp<ResourceTypeSet> transitions; 1215 sp<ResourceTypeSet> xmls; 1216 sp<ResourceTypeSet> raws; 1217 sp<ResourceTypeSet> colors; 1218 sp<ResourceTypeSet> menus; 1219 sp<ResourceTypeSet> mipmaps; 1220 1221 ASSIGN_IT(drawable); 1222 ASSIGN_IT(layout); 1223 ASSIGN_IT(anim); 1224 ASSIGN_IT(animator); 1225 ASSIGN_IT(interpolator); 1226 ASSIGN_IT(transition); 1227 ASSIGN_IT(xml); 1228 ASSIGN_IT(raw); 1229 ASSIGN_IT(color); 1230 ASSIGN_IT(menu); 1231 ASSIGN_IT(mipmap); 1232 1233 assets->setResources(resources); 1234 // now go through any resource overlays and collect their files 1235 sp<AaptAssets> current = assets->getOverlay(); 1236 while(current.get()) { 1237 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 1238 new KeyedVector<String8, sp<ResourceTypeSet> >; 1239 current->setResources(resources); 1240 collect_files(current, resources); 1241 current = current->getOverlay(); 1242 } 1243 // apply the overlay files to the base set 1244 if (!applyFileOverlay(bundle, assets, &drawables, "drawable") || 1245 !applyFileOverlay(bundle, assets, &layouts, "layout") || 1246 !applyFileOverlay(bundle, assets, &anims, "anim") || 1247 !applyFileOverlay(bundle, assets, &animators, "animator") || 1248 !applyFileOverlay(bundle, assets, &interpolators, "interpolator") || 1249 !applyFileOverlay(bundle, assets, &transitions, "transition") || 1250 !applyFileOverlay(bundle, assets, &xmls, "xml") || 1251 !applyFileOverlay(bundle, assets, &raws, "raw") || 1252 !applyFileOverlay(bundle, assets, &colors, "color") || 1253 !applyFileOverlay(bundle, assets, &menus, "menu") || 1254 !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) { 1255 return UNKNOWN_ERROR; 1256 } 1257 1258 bool hasErrors = false; 1259 1260 if (drawables != NULL) { 1261 if (bundle->getOutputAPKFile() != NULL) { 1262 err = preProcessImages(bundle, assets, drawables, "drawable"); 1263 } 1264 if (err == NO_ERROR) { 1265 err = makeFileResources(bundle, assets, &table, drawables, "drawable"); 1266 if (err != NO_ERROR) { 1267 hasErrors = true; 1268 } 1269 } else { 1270 hasErrors = true; 1271 } 1272 } 1273 1274 if (mipmaps != NULL) { 1275 if (bundle->getOutputAPKFile() != NULL) { 1276 err = preProcessImages(bundle, assets, mipmaps, "mipmap"); 1277 } 1278 if (err == NO_ERROR) { 1279 err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap"); 1280 if (err != NO_ERROR) { 1281 hasErrors = true; 1282 } 1283 } else { 1284 hasErrors = true; 1285 } 1286 } 1287 1288 if (layouts != NULL) { 1289 err = makeFileResources(bundle, assets, &table, layouts, "layout"); 1290 if (err != NO_ERROR) { 1291 hasErrors = true; 1292 } 1293 } 1294 1295 if (anims != NULL) { 1296 err = makeFileResources(bundle, assets, &table, anims, "anim"); 1297 if (err != NO_ERROR) { 1298 hasErrors = true; 1299 } 1300 } 1301 1302 if (animators != NULL) { 1303 err = makeFileResources(bundle, assets, &table, animators, "animator"); 1304 if (err != NO_ERROR) { 1305 hasErrors = true; 1306 } 1307 } 1308 1309 if (transitions != NULL) { 1310 err = makeFileResources(bundle, assets, &table, transitions, "transition"); 1311 if (err != NO_ERROR) { 1312 hasErrors = true; 1313 } 1314 } 1315 1316 if (interpolators != NULL) { 1317 err = makeFileResources(bundle, assets, &table, interpolators, "interpolator"); 1318 if (err != NO_ERROR) { 1319 hasErrors = true; 1320 } 1321 } 1322 1323 if (xmls != NULL) { 1324 err = makeFileResources(bundle, assets, &table, xmls, "xml"); 1325 if (err != NO_ERROR) { 1326 hasErrors = true; 1327 } 1328 } 1329 1330 if (raws != NULL) { 1331 err = makeFileResources(bundle, assets, &table, raws, "raw"); 1332 if (err != NO_ERROR) { 1333 hasErrors = true; 1334 } 1335 } 1336 1337 // compile resources 1338 current = assets; 1339 while(current.get()) { 1340 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 1341 current->getResources(); 1342 1343 ssize_t index = resources->indexOfKey(String8("values")); 1344 if (index >= 0) { 1345 ResourceDirIterator it(resources->valueAt(index), String8("values")); 1346 ssize_t res; 1347 while ((res=it.next()) == NO_ERROR) { 1348 sp<AaptFile> file = it.getFile(); 1349 res = compileResourceFile(bundle, assets, file, it.getParams(), 1350 (current!=assets), &table); 1351 if (res != NO_ERROR) { 1352 hasErrors = true; 1353 } 1354 } 1355 } 1356 current = current->getOverlay(); 1357 } 1358 1359 if (colors != NULL) { 1360 err = makeFileResources(bundle, assets, &table, colors, "color"); 1361 if (err != NO_ERROR) { 1362 hasErrors = true; 1363 } 1364 } 1365 1366 if (menus != NULL) { 1367 err = makeFileResources(bundle, assets, &table, menus, "menu"); 1368 if (err != NO_ERROR) { 1369 hasErrors = true; 1370 } 1371 } 1372 1373 // -------------------------------------------------------------------- 1374 // Assignment of resource IDs and initial generation of resource table. 1375 // -------------------------------------------------------------------- 1376 1377 if (table.hasResources()) { 1378 err = table.assignResourceIds(); 1379 if (err < NO_ERROR) { 1380 return err; 1381 } 1382 } 1383 1384 // -------------------------------------------------------------- 1385 // Finally, we can now we can compile XML files, which may reference 1386 // resources. 1387 // -------------------------------------------------------------- 1388 1389 if (layouts != NULL) { 1390 ResourceDirIterator it(layouts, String8("layout")); 1391 while ((err=it.next()) == NO_ERROR) { 1392 String8 src = it.getFile()->getPrintableSource(); 1393 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1394 it.getFile(), &table, xmlFlags); 1395 if (err == NO_ERROR) { 1396 ResXMLTree block; 1397 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); 1398 checkForIds(src, block); 1399 } else { 1400 hasErrors = true; 1401 } 1402 } 1403 1404 if (err < NO_ERROR) { 1405 hasErrors = true; 1406 } 1407 err = NO_ERROR; 1408 } 1409 1410 if (anims != NULL) { 1411 ResourceDirIterator it(anims, String8("anim")); 1412 while ((err=it.next()) == NO_ERROR) { 1413 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1414 it.getFile(), &table, xmlFlags); 1415 if (err != NO_ERROR) { 1416 hasErrors = true; 1417 } 1418 } 1419 1420 if (err < NO_ERROR) { 1421 hasErrors = true; 1422 } 1423 err = NO_ERROR; 1424 } 1425 1426 if (animators != NULL) { 1427 ResourceDirIterator it(animators, String8("animator")); 1428 while ((err=it.next()) == NO_ERROR) { 1429 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1430 it.getFile(), &table, xmlFlags); 1431 if (err != NO_ERROR) { 1432 hasErrors = true; 1433 } 1434 } 1435 1436 if (err < NO_ERROR) { 1437 hasErrors = true; 1438 } 1439 err = NO_ERROR; 1440 } 1441 1442 if (interpolators != NULL) { 1443 ResourceDirIterator it(interpolators, String8("interpolator")); 1444 while ((err=it.next()) == NO_ERROR) { 1445 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1446 it.getFile(), &table, xmlFlags); 1447 if (err != NO_ERROR) { 1448 hasErrors = true; 1449 } 1450 } 1451 1452 if (err < NO_ERROR) { 1453 hasErrors = true; 1454 } 1455 err = NO_ERROR; 1456 } 1457 1458 if (transitions != NULL) { 1459 ResourceDirIterator it(transitions, String8("transition")); 1460 while ((err=it.next()) == NO_ERROR) { 1461 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1462 it.getFile(), &table, xmlFlags); 1463 if (err != NO_ERROR) { 1464 hasErrors = true; 1465 } 1466 } 1467 1468 if (err < NO_ERROR) { 1469 hasErrors = true; 1470 } 1471 err = NO_ERROR; 1472 } 1473 1474 if (xmls != NULL) { 1475 ResourceDirIterator it(xmls, String8("xml")); 1476 while ((err=it.next()) == NO_ERROR) { 1477 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1478 it.getFile(), &table, xmlFlags); 1479 if (err != NO_ERROR) { 1480 hasErrors = true; 1481 } 1482 } 1483 1484 if (err < NO_ERROR) { 1485 hasErrors = true; 1486 } 1487 err = NO_ERROR; 1488 } 1489 1490 if (drawables != NULL) { 1491 ResourceDirIterator it(drawables, String8("drawable")); 1492 while ((err=it.next()) == NO_ERROR) { 1493 err = postProcessImage(bundle, assets, &table, it.getFile()); 1494 if (err != NO_ERROR) { 1495 hasErrors = true; 1496 } 1497 } 1498 1499 if (err < NO_ERROR) { 1500 hasErrors = true; 1501 } 1502 err = NO_ERROR; 1503 } 1504 1505 if (colors != NULL) { 1506 ResourceDirIterator it(colors, String8("color")); 1507 while ((err=it.next()) == NO_ERROR) { 1508 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1509 it.getFile(), &table, xmlFlags); 1510 if (err != NO_ERROR) { 1511 hasErrors = true; 1512 } 1513 } 1514 1515 if (err < NO_ERROR) { 1516 hasErrors = true; 1517 } 1518 err = NO_ERROR; 1519 } 1520 1521 if (menus != NULL) { 1522 ResourceDirIterator it(menus, String8("menu")); 1523 while ((err=it.next()) == NO_ERROR) { 1524 String8 src = it.getFile()->getPrintableSource(); 1525 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1526 it.getFile(), &table, xmlFlags); 1527 if (err == NO_ERROR) { 1528 ResXMLTree block; 1529 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); 1530 checkForIds(src, block); 1531 } else { 1532 hasErrors = true; 1533 } 1534 } 1535 1536 if (err < NO_ERROR) { 1537 hasErrors = true; 1538 } 1539 err = NO_ERROR; 1540 } 1541 1542 // Now compile any generated resources. 1543 std::queue<CompileResourceWorkItem>& workQueue = table.getWorkQueue(); 1544 while (!workQueue.empty()) { 1545 CompileResourceWorkItem& workItem = workQueue.front(); 1546 int xmlCompilationFlags = xmlFlags | XML_COMPILE_PARSE_VALUES 1547 | XML_COMPILE_ASSIGN_ATTRIBUTE_IDS; 1548 if (!workItem.needsCompiling) { 1549 xmlCompilationFlags &= ~XML_COMPILE_ASSIGN_ATTRIBUTE_IDS; 1550 xmlCompilationFlags &= ~XML_COMPILE_PARSE_VALUES; 1551 } 1552 err = compileXmlFile(bundle, assets, workItem.resourceName, workItem.xmlRoot, 1553 workItem.file, &table, xmlCompilationFlags); 1554 1555 if (err == NO_ERROR) { 1556 assets->addResource(workItem.resPath.getPathLeaf(), 1557 workItem.resPath, 1558 workItem.file, 1559 workItem.file->getResourceType()); 1560 } else { 1561 hasErrors = true; 1562 } 1563 workQueue.pop(); 1564 } 1565 1566 if (table.validateLocalizations()) { 1567 hasErrors = true; 1568 } 1569 1570 if (hasErrors) { 1571 return UNKNOWN_ERROR; 1572 } 1573 1574 // If we're not overriding the platform build versions, 1575 // extract them from the platform APK. 1576 if (packageType != ResourceTable::System && 1577 (bundle->getPlatformBuildVersionCode() == "" || 1578 bundle->getPlatformBuildVersionName() == "")) { 1579 err = extractPlatformBuildVersion(assets->getAssetManager(), bundle); 1580 if (err != NO_ERROR) { 1581 return UNKNOWN_ERROR; 1582 } 1583 } 1584 1585 const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0)); 1586 String8 manifestPath(manifestFile->getPrintableSource()); 1587 1588 // Generate final compiled manifest file. 1589 manifestFile->clearData(); 1590 sp<XMLNode> manifestTree = XMLNode::parse(manifestFile); 1591 if (manifestTree == NULL) { 1592 return UNKNOWN_ERROR; 1593 } 1594 err = massageManifest(bundle, manifestTree); 1595 if (err < NO_ERROR) { 1596 return err; 1597 } 1598 err = compileXmlFile(bundle, assets, String16(), manifestTree, manifestFile, &table); 1599 if (err < NO_ERROR) { 1600 return err; 1601 } 1602 1603 if (table.modifyForCompat(bundle) != NO_ERROR) { 1604 return UNKNOWN_ERROR; 1605 } 1606 1607 //block.restart(); 1608 //printXMLBlock(&block); 1609 1610 // -------------------------------------------------------------- 1611 // Generate the final resource table. 1612 // Re-flatten because we may have added new resource IDs 1613 // -------------------------------------------------------------- 1614 1615 1616 ResTable finalResTable; 1617 sp<AaptFile> resFile; 1618 1619 if (table.hasResources()) { 1620 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R")); 1621 err = table.addSymbols(symbols, bundle->getSkipSymbolsWithoutDefaultLocalization()); 1622 if (err < NO_ERROR) { 1623 return err; 1624 } 1625 1626 KeyedVector<Symbol, Vector<SymbolDefinition> > densityVaryingResources; 1627 if (builder->getSplits().size() > 1) { 1628 // Only look for density varying resources if we're generating 1629 // splits. 1630 table.getDensityVaryingResources(densityVaryingResources); 1631 } 1632 1633 Vector<sp<ApkSplit> >& splits = builder->getSplits(); 1634 const size_t numSplits = splits.size(); 1635 for (size_t i = 0; i < numSplits; i++) { 1636 sp<ApkSplit>& split = splits.editItemAt(i); 1637 sp<AaptFile> flattenedTable = new AaptFile(String8("resources.arsc"), 1638 AaptGroupEntry(), String8()); 1639 err = table.flatten(bundle, split->getResourceFilter(), 1640 flattenedTable, split->isBase()); 1641 if (err != NO_ERROR) { 1642 fprintf(stderr, "Failed to generate resource table for split '%s'\n", 1643 split->getPrintableName().string()); 1644 return err; 1645 } 1646 split->addEntry(String8("resources.arsc"), flattenedTable); 1647 1648 if (split->isBase()) { 1649 resFile = flattenedTable; 1650 err = finalResTable.add(flattenedTable->getData(), flattenedTable->getSize()); 1651 if (err != NO_ERROR) { 1652 fprintf(stderr, "Generated resource table is corrupt.\n"); 1653 return err; 1654 } 1655 } else { 1656 ResTable resTable; 1657 err = resTable.add(flattenedTable->getData(), flattenedTable->getSize()); 1658 if (err != NO_ERROR) { 1659 fprintf(stderr, "Generated resource table for split '%s' is corrupt.\n", 1660 split->getPrintableName().string()); 1661 return err; 1662 } 1663 1664 bool hasError = false; 1665 const std::set<ConfigDescription>& splitConfigs = split->getConfigs(); 1666 for (std::set<ConfigDescription>::const_iterator iter = splitConfigs.begin(); 1667 iter != splitConfigs.end(); 1668 ++iter) { 1669 const ConfigDescription& config = *iter; 1670 if (AaptConfig::isDensityOnly(config)) { 1671 // Each density only split must contain all 1672 // density only resources. 1673 Res_value val; 1674 resTable.setParameters(&config); 1675 const size_t densityVaryingResourceCount = densityVaryingResources.size(); 1676 for (size_t k = 0; k < densityVaryingResourceCount; k++) { 1677 const Symbol& symbol = densityVaryingResources.keyAt(k); 1678 ssize_t block = resTable.getResource(symbol.id, &val, true); 1679 if (block < 0) { 1680 // Maybe it's in the base? 1681 finalResTable.setParameters(&config); 1682 block = finalResTable.getResource(symbol.id, &val, true); 1683 } 1684 1685 if (block < 0) { 1686 hasError = true; 1687 SourcePos().error("%s has no definition for density split '%s'", 1688 symbol.toString().string(), config.toString().string()); 1689 1690 if (bundle->getVerbose()) { 1691 const Vector<SymbolDefinition>& defs = densityVaryingResources[k]; 1692 const size_t defCount = std::min(size_t(5), defs.size()); 1693 for (size_t d = 0; d < defCount; d++) { 1694 const SymbolDefinition& def = defs[d]; 1695 def.source.error("%s has definition for %s", 1696 symbol.toString().string(), def.config.toString().string()); 1697 } 1698 1699 if (defCount < defs.size()) { 1700 SourcePos().error("and %d more ...", (int) (defs.size() - defCount)); 1701 } 1702 } 1703 } 1704 } 1705 } 1706 } 1707 1708 if (hasError) { 1709 return UNKNOWN_ERROR; 1710 } 1711 1712 // Generate the AndroidManifest for this split. 1713 sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"), 1714 AaptGroupEntry(), String8()); 1715 err = generateAndroidManifestForSplit(bundle, assets, split, 1716 generatedManifest, &table); 1717 if (err != NO_ERROR) { 1718 fprintf(stderr, "Failed to generate AndroidManifest.xml for split '%s'\n", 1719 split->getPrintableName().string()); 1720 return err; 1721 } 1722 split->addEntry(String8("AndroidManifest.xml"), generatedManifest); 1723 } 1724 } 1725 1726 if (bundle->getPublicOutputFile()) { 1727 FILE* fp = fopen(bundle->getPublicOutputFile(), "w+"); 1728 if (fp == NULL) { 1729 fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n", 1730 (const char*)bundle->getPublicOutputFile(), strerror(errno)); 1731 return UNKNOWN_ERROR; 1732 } 1733 if (bundle->getVerbose()) { 1734 printf(" Writing public definitions to %s.\n", bundle->getPublicOutputFile()); 1735 } 1736 table.writePublicDefinitions(String16(assets->getPackage()), fp); 1737 fclose(fp); 1738 } 1739 1740 if (finalResTable.getTableCount() == 0 || resFile == NULL) { 1741 fprintf(stderr, "No resource table was generated.\n"); 1742 return UNKNOWN_ERROR; 1743 } 1744 } 1745 1746 // Perform a basic validation of the manifest file. This time we 1747 // parse it with the comments intact, so that we can use them to 1748 // generate java docs... so we are not going to write this one 1749 // back out to the final manifest data. 1750 sp<AaptFile> outManifestFile = new AaptFile(manifestFile->getSourceFile(), 1751 manifestFile->getGroupEntry(), 1752 manifestFile->getResourceType()); 1753 err = compileXmlFile(bundle, assets, String16(), manifestFile, 1754 outManifestFile, &table, XML_COMPILE_STANDARD_RESOURCE & ~XML_COMPILE_STRIP_COMMENTS); 1755 if (err < NO_ERROR) { 1756 return err; 1757 } 1758 ResXMLTree block; 1759 block.setTo(outManifestFile->getData(), outManifestFile->getSize(), true); 1760 String16 manifest16("manifest"); 1761 String16 permission16("permission"); 1762 String16 permission_group16("permission-group"); 1763 String16 uses_permission16("uses-permission"); 1764 String16 instrumentation16("instrumentation"); 1765 String16 application16("application"); 1766 String16 provider16("provider"); 1767 String16 service16("service"); 1768 String16 receiver16("receiver"); 1769 String16 activity16("activity"); 1770 String16 action16("action"); 1771 String16 category16("category"); 1772 String16 data16("scheme"); 1773 String16 feature_group16("feature-group"); 1774 String16 uses_feature16("uses-feature"); 1775 const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz" 1776 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789"; 1777 const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz" 1778 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-"; 1779 const char* classIdentChars = "abcdefghijklmnopqrstuvwxyz" 1780 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789$"; 1781 const char* processIdentChars = "abcdefghijklmnopqrstuvwxyz" 1782 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:"; 1783 const char* authoritiesIdentChars = "abcdefghijklmnopqrstuvwxyz" 1784 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-:;"; 1785 const char* typeIdentChars = "abcdefghijklmnopqrstuvwxyz" 1786 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:-/*+"; 1787 const char* schemeIdentChars = "abcdefghijklmnopqrstuvwxyz" 1788 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-"; 1789 ResXMLTree::event_code_t code; 1790 sp<AaptSymbols> permissionSymbols; 1791 sp<AaptSymbols> permissionGroupSymbols; 1792 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 1793 && code > ResXMLTree::BAD_DOCUMENT) { 1794 if (code == ResXMLTree::START_TAG) { 1795 size_t len; 1796 if (block.getElementNamespace(&len) != NULL) { 1797 continue; 1798 } 1799 if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) { 1800 if (validateAttr(manifestPath, finalResTable, block, NULL, "package", 1801 packageIdentChars, true) != ATTR_OKAY) { 1802 hasErrors = true; 1803 } 1804 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1805 "sharedUserId", packageIdentChars, false) != ATTR_OKAY) { 1806 hasErrors = true; 1807 } 1808 } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0 1809 || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) { 1810 const bool isGroup = strcmp16(block.getElementName(&len), 1811 permission_group16.string()) == 0; 1812 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1813 "name", isGroup ? packageIdentCharsWithTheStupid 1814 : packageIdentChars, true) != ATTR_OKAY) { 1815 hasErrors = true; 1816 } 1817 SourcePos srcPos(manifestPath, block.getLineNumber()); 1818 sp<AaptSymbols> syms; 1819 if (!isGroup) { 1820 syms = permissionSymbols; 1821 if (syms == NULL) { 1822 sp<AaptSymbols> symbols = 1823 assets->getSymbolsFor(String8("Manifest")); 1824 syms = permissionSymbols = symbols->addNestedSymbol( 1825 String8("permission"), srcPos); 1826 } 1827 } else { 1828 syms = permissionGroupSymbols; 1829 if (syms == NULL) { 1830 sp<AaptSymbols> symbols = 1831 assets->getSymbolsFor(String8("Manifest")); 1832 syms = permissionGroupSymbols = symbols->addNestedSymbol( 1833 String8("permission_group"), srcPos); 1834 } 1835 } 1836 size_t len; 1837 ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name"); 1838 const char16_t* id = block.getAttributeStringValue(index, &len); 1839 if (id == NULL) { 1840 fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n", 1841 manifestPath.string(), block.getLineNumber(), 1842 String8(block.getElementName(&len)).string()); 1843 hasErrors = true; 1844 break; 1845 } 1846 String8 idStr(id); 1847 char* p = idStr.lockBuffer(idStr.size()); 1848 char* e = p + idStr.size(); 1849 bool begins_with_digit = true; // init to true so an empty string fails 1850 while (e > p) { 1851 e--; 1852 if (*e >= '0' && *e <= '9') { 1853 begins_with_digit = true; 1854 continue; 1855 } 1856 if ((*e >= 'a' && *e <= 'z') || 1857 (*e >= 'A' && *e <= 'Z') || 1858 (*e == '_')) { 1859 begins_with_digit = false; 1860 continue; 1861 } 1862 if (isGroup && (*e == '-')) { 1863 *e = '_'; 1864 begins_with_digit = false; 1865 continue; 1866 } 1867 e++; 1868 break; 1869 } 1870 idStr.unlockBuffer(); 1871 // verify that we stopped because we hit a period or 1872 // the beginning of the string, and that the 1873 // identifier didn't begin with a digit. 1874 if (begins_with_digit || (e != p && *(e-1) != '.')) { 1875 fprintf(stderr, 1876 "%s:%d: Permission name <%s> is not a valid Java symbol\n", 1877 manifestPath.string(), block.getLineNumber(), idStr.string()); 1878 hasErrors = true; 1879 } 1880 syms->addStringSymbol(String8(e), idStr, srcPos); 1881 const char16_t* cmt = block.getComment(&len); 1882 if (cmt != NULL && *cmt != 0) { 1883 //printf("Comment of %s: %s\n", String8(e).string(), 1884 // String8(cmt).string()); 1885 syms->appendComment(String8(e), String16(cmt), srcPos); 1886 } 1887 syms->makeSymbolPublic(String8(e), srcPos); 1888 } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) { 1889 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1890 "name", packageIdentChars, true) != ATTR_OKAY) { 1891 hasErrors = true; 1892 } 1893 } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) { 1894 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1895 "name", classIdentChars, true) != ATTR_OKAY) { 1896 hasErrors = true; 1897 } 1898 if (validateAttr(manifestPath, finalResTable, block, 1899 RESOURCES_ANDROID_NAMESPACE, "targetPackage", 1900 packageIdentChars, true) != ATTR_OKAY) { 1901 hasErrors = true; 1902 } 1903 } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) { 1904 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1905 "name", classIdentChars, false) != ATTR_OKAY) { 1906 hasErrors = true; 1907 } 1908 if (validateAttr(manifestPath, finalResTable, block, 1909 RESOURCES_ANDROID_NAMESPACE, "permission", 1910 packageIdentChars, false) != ATTR_OKAY) { 1911 hasErrors = true; 1912 } 1913 if (validateAttr(manifestPath, finalResTable, block, 1914 RESOURCES_ANDROID_NAMESPACE, "process", 1915 processIdentChars, false) != ATTR_OKAY) { 1916 hasErrors = true; 1917 } 1918 if (validateAttr(manifestPath, finalResTable, block, 1919 RESOURCES_ANDROID_NAMESPACE, "taskAffinity", 1920 processIdentChars, false) != ATTR_OKAY) { 1921 hasErrors = true; 1922 } 1923 } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) { 1924 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1925 "name", classIdentChars, true) != ATTR_OKAY) { 1926 hasErrors = true; 1927 } 1928 if (validateAttr(manifestPath, finalResTable, block, 1929 RESOURCES_ANDROID_NAMESPACE, "authorities", 1930 authoritiesIdentChars, true) != ATTR_OKAY) { 1931 hasErrors = true; 1932 } 1933 if (validateAttr(manifestPath, finalResTable, block, 1934 RESOURCES_ANDROID_NAMESPACE, "permission", 1935 packageIdentChars, false) != ATTR_OKAY) { 1936 hasErrors = true; 1937 } 1938 if (validateAttr(manifestPath, finalResTable, block, 1939 RESOURCES_ANDROID_NAMESPACE, "process", 1940 processIdentChars, false) != ATTR_OKAY) { 1941 hasErrors = true; 1942 } 1943 } else if (strcmp16(block.getElementName(&len), service16.string()) == 0 1944 || strcmp16(block.getElementName(&len), receiver16.string()) == 0 1945 || strcmp16(block.getElementName(&len), activity16.string()) == 0) { 1946 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1947 "name", classIdentChars, true) != ATTR_OKAY) { 1948 hasErrors = true; 1949 } 1950 if (validateAttr(manifestPath, finalResTable, block, 1951 RESOURCES_ANDROID_NAMESPACE, "permission", 1952 packageIdentChars, false) != ATTR_OKAY) { 1953 hasErrors = true; 1954 } 1955 if (validateAttr(manifestPath, finalResTable, block, 1956 RESOURCES_ANDROID_NAMESPACE, "process", 1957 processIdentChars, false) != ATTR_OKAY) { 1958 hasErrors = true; 1959 } 1960 if (validateAttr(manifestPath, finalResTable, block, 1961 RESOURCES_ANDROID_NAMESPACE, "taskAffinity", 1962 processIdentChars, false) != ATTR_OKAY) { 1963 hasErrors = true; 1964 } 1965 } else if (strcmp16(block.getElementName(&len), action16.string()) == 0 1966 || strcmp16(block.getElementName(&len), category16.string()) == 0) { 1967 if (validateAttr(manifestPath, finalResTable, block, 1968 RESOURCES_ANDROID_NAMESPACE, "name", 1969 packageIdentChars, true) != ATTR_OKAY) { 1970 hasErrors = true; 1971 } 1972 } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) { 1973 if (validateAttr(manifestPath, finalResTable, block, 1974 RESOURCES_ANDROID_NAMESPACE, "mimeType", 1975 typeIdentChars, true) != ATTR_OKAY) { 1976 hasErrors = true; 1977 } 1978 if (validateAttr(manifestPath, finalResTable, block, 1979 RESOURCES_ANDROID_NAMESPACE, "scheme", 1980 schemeIdentChars, true) != ATTR_OKAY) { 1981 hasErrors = true; 1982 } 1983 } else if (strcmp16(block.getElementName(&len), feature_group16.string()) == 0) { 1984 int depth = 1; 1985 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 1986 && code > ResXMLTree::BAD_DOCUMENT) { 1987 if (code == ResXMLTree::START_TAG) { 1988 depth++; 1989 if (strcmp16(block.getElementName(&len), uses_feature16.string()) == 0) { 1990 ssize_t idx = block.indexOfAttribute( 1991 RESOURCES_ANDROID_NAMESPACE, "required"); 1992 if (idx < 0) { 1993 continue; 1994 } 1995 1996 int32_t data = block.getAttributeData(idx); 1997 if (data == 0) { 1998 fprintf(stderr, "%s:%d: Tag <uses-feature> can not have " 1999 "android:required=\"false\" when inside a " 2000 "<feature-group> tag.\n", 2001 manifestPath.string(), block.getLineNumber()); 2002 hasErrors = true; 2003 } 2004 } 2005 } else if (code == ResXMLTree::END_TAG) { 2006 depth--; 2007 if (depth == 0) { 2008 break; 2009 } 2010 } 2011 } 2012 } 2013 } 2014 } 2015 2016 if (hasErrors) { 2017 return UNKNOWN_ERROR; 2018 } 2019 2020 if (resFile != NULL) { 2021 // These resources are now considered to be a part of the included 2022 // resources, for others to reference. 2023 err = assets->addIncludedResources(resFile); 2024 if (err < NO_ERROR) { 2025 fprintf(stderr, "ERROR: Unable to parse generated resources, aborting.\n"); 2026 return err; 2027 } 2028 } 2029 2030 return err; 2031} 2032 2033static const char* getIndentSpace(int indent) 2034{ 2035static const char whitespace[] = 2036" "; 2037 2038 return whitespace + sizeof(whitespace) - 1 - indent*4; 2039} 2040 2041static String8 flattenSymbol(const String8& symbol) { 2042 String8 result(symbol); 2043 ssize_t first; 2044 if ((first = symbol.find(":", 0)) >= 0 2045 || (first = symbol.find(".", 0)) >= 0) { 2046 size_t size = symbol.size(); 2047 char* buf = result.lockBuffer(size); 2048 for (size_t i = first; i < size; i++) { 2049 if (buf[i] == ':' || buf[i] == '.') { 2050 buf[i] = '_'; 2051 } 2052 } 2053 result.unlockBuffer(size); 2054 } 2055 return result; 2056} 2057 2058static String8 getSymbolPackage(const String8& symbol, const sp<AaptAssets>& assets, bool pub) { 2059 ssize_t colon = symbol.find(":", 0); 2060 if (colon >= 0) { 2061 return String8(symbol.string(), colon); 2062 } 2063 return pub ? assets->getPackage() : assets->getSymbolsPrivatePackage(); 2064} 2065 2066static String8 getSymbolName(const String8& symbol) { 2067 ssize_t colon = symbol.find(":", 0); 2068 if (colon >= 0) { 2069 return String8(symbol.string() + colon + 1); 2070 } 2071 return symbol; 2072} 2073 2074static String16 getAttributeComment(const sp<AaptAssets>& assets, 2075 const String8& name, 2076 String16* outTypeComment = NULL) 2077{ 2078 sp<AaptSymbols> asym = assets->getSymbolsFor(String8("R")); 2079 if (asym != NULL) { 2080 //printf("Got R symbols!\n"); 2081 asym = asym->getNestedSymbols().valueFor(String8("attr")); 2082 if (asym != NULL) { 2083 //printf("Got attrs symbols! comment %s=%s\n", 2084 // name.string(), String8(asym->getComment(name)).string()); 2085 if (outTypeComment != NULL) { 2086 *outTypeComment = asym->getTypeComment(name); 2087 } 2088 return asym->getComment(name); 2089 } 2090 } 2091 return String16(); 2092} 2093 2094static status_t writeResourceLoadedCallbackForLayoutClasses( 2095 FILE* fp, const sp<AaptAssets>& assets, 2096 const sp<AaptSymbols>& symbols, int indent, bool /* includePrivate */) 2097{ 2098 String16 attr16("attr"); 2099 String16 package16(assets->getPackage()); 2100 2101 const char* indentStr = getIndentSpace(indent); 2102 bool hasErrors = false; 2103 2104 size_t i; 2105 size_t N = symbols->getNestedSymbols().size(); 2106 for (i=0; i<N; i++) { 2107 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2108 String8 realClassName(symbols->getNestedSymbols().keyAt(i)); 2109 String8 nclassName(flattenSymbol(realClassName)); 2110 2111 fprintf(fp, 2112 "%sfor(int i = 0; i < styleable.%s.length; ++i) {\n" 2113 "%sstyleable.%s[i] = (styleable.%s[i] & 0x00ffffff) | (packageId << 24);\n" 2114 "%s}\n", 2115 indentStr, nclassName.string(), 2116 getIndentSpace(indent+1), nclassName.string(), nclassName.string(), 2117 indentStr); 2118 } 2119 2120 return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR; 2121} 2122 2123static status_t writeResourceLoadedCallback( 2124 FILE* fp, const sp<AaptAssets>& assets, bool includePrivate, 2125 const sp<AaptSymbols>& symbols, const String8& className, int indent) 2126{ 2127 size_t i; 2128 status_t err = NO_ERROR; 2129 2130 size_t N = symbols->getSymbols().size(); 2131 for (i=0; i<N; i++) { 2132 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 2133 if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) { 2134 continue; 2135 } 2136 if (!assets->isJavaSymbol(sym, includePrivate)) { 2137 continue; 2138 } 2139 String8 flat_name(flattenSymbol(sym.name)); 2140 fprintf(fp, 2141 "%s%s.%s = (%s.%s & 0x00ffffff) | (packageId << 24);\n", 2142 getIndentSpace(indent), className.string(), flat_name.string(), 2143 className.string(), flat_name.string()); 2144 } 2145 2146 N = symbols->getNestedSymbols().size(); 2147 for (i=0; i<N; i++) { 2148 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2149 String8 nclassName(symbols->getNestedSymbols().keyAt(i)); 2150 if (nclassName == "styleable") { 2151 err = writeResourceLoadedCallbackForLayoutClasses( 2152 fp, assets, nsymbols, indent, includePrivate); 2153 } else { 2154 err = writeResourceLoadedCallback(fp, assets, includePrivate, nsymbols, 2155 nclassName, indent); 2156 } 2157 if (err != NO_ERROR) { 2158 return err; 2159 } 2160 } 2161 2162 return NO_ERROR; 2163} 2164 2165static status_t writeLayoutClasses( 2166 FILE* fp, const sp<AaptAssets>& assets, 2167 const sp<AaptSymbols>& symbols, int indent, bool includePrivate, bool nonConstantId) 2168{ 2169 const char* indentStr = getIndentSpace(indent); 2170 if (!includePrivate) { 2171 fprintf(fp, "%s/** @doconly */\n", indentStr); 2172 } 2173 fprintf(fp, "%spublic static final class styleable {\n", indentStr); 2174 indent++; 2175 2176 String16 attr16("attr"); 2177 String16 package16(assets->getPackage()); 2178 2179 indentStr = getIndentSpace(indent); 2180 bool hasErrors = false; 2181 2182 size_t i; 2183 size_t N = symbols->getNestedSymbols().size(); 2184 for (i=0; i<N; i++) { 2185 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2186 String8 realClassName(symbols->getNestedSymbols().keyAt(i)); 2187 String8 nclassName(flattenSymbol(realClassName)); 2188 2189 SortedVector<uint32_t> idents; 2190 Vector<uint32_t> origOrder; 2191 Vector<bool> publicFlags; 2192 2193 size_t a; 2194 size_t NA = nsymbols->getSymbols().size(); 2195 for (a=0; a<NA; a++) { 2196 const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a)); 2197 int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32 2198 ? sym.int32Val : 0; 2199 bool isPublic = true; 2200 if (code == 0) { 2201 String16 name16(sym.name); 2202 uint32_t typeSpecFlags; 2203 code = assets->getIncludedResources().identifierForName( 2204 name16.string(), name16.size(), 2205 attr16.string(), attr16.size(), 2206 package16.string(), package16.size(), &typeSpecFlags); 2207 if (code == 0) { 2208 fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n", 2209 nclassName.string(), sym.name.string()); 2210 hasErrors = true; 2211 } 2212 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 2213 } 2214 idents.add(code); 2215 origOrder.add(code); 2216 publicFlags.add(isPublic); 2217 } 2218 2219 NA = idents.size(); 2220 2221 String16 comment = symbols->getComment(realClassName); 2222 AnnotationProcessor ann; 2223 fprintf(fp, "%s/** ", indentStr); 2224 if (comment.size() > 0) { 2225 String8 cmt(comment); 2226 ann.preprocessComment(cmt); 2227 fprintf(fp, "%s\n", cmt.string()); 2228 } else { 2229 fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string()); 2230 } 2231 bool hasTable = false; 2232 for (a=0; a<NA; a++) { 2233 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 2234 if (pos >= 0) { 2235 if (!hasTable) { 2236 hasTable = true; 2237 fprintf(fp, 2238 "%s <p>Includes the following attributes:</p>\n" 2239 "%s <table>\n" 2240 "%s <colgroup align=\"left\" />\n" 2241 "%s <colgroup align=\"left\" />\n" 2242 "%s <tr><th>Attribute</th><th>Description</th></tr>\n", 2243 indentStr, 2244 indentStr, 2245 indentStr, 2246 indentStr, 2247 indentStr); 2248 } 2249 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 2250 if (!publicFlags.itemAt(a) && !includePrivate) { 2251 continue; 2252 } 2253 String8 name8(sym.name); 2254 String16 comment(sym.comment); 2255 if (comment.size() <= 0) { 2256 comment = getAttributeComment(assets, name8); 2257 } 2258 if (comment.contains(u"@removed")) { 2259 continue; 2260 } 2261 if (comment.size() > 0) { 2262 const char16_t* p = comment.string(); 2263 while (*p != 0 && *p != '.') { 2264 if (*p == '{') { 2265 while (*p != 0 && *p != '}') { 2266 p++; 2267 } 2268 } else { 2269 p++; 2270 } 2271 } 2272 if (*p == '.') { 2273 p++; 2274 } 2275 comment = String16(comment.string(), p-comment.string()); 2276 } 2277 fprintf(fp, "%s <tr><td><code>{@link #%s_%s %s:%s}</code></td><td>%s</td></tr>\n", 2278 indentStr, nclassName.string(), 2279 flattenSymbol(name8).string(), 2280 getSymbolPackage(name8, assets, true).string(), 2281 getSymbolName(name8).string(), 2282 String8(comment).string()); 2283 } 2284 } 2285 if (hasTable) { 2286 fprintf(fp, "%s </table>\n", indentStr); 2287 } 2288 for (a=0; a<NA; a++) { 2289 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 2290 if (pos >= 0) { 2291 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 2292 if (!publicFlags.itemAt(a) && !includePrivate) { 2293 continue; 2294 } 2295 fprintf(fp, "%s @see #%s_%s\n", 2296 indentStr, nclassName.string(), 2297 flattenSymbol(sym.name).string()); 2298 } 2299 } 2300 fprintf(fp, "%s */\n", getIndentSpace(indent)); 2301 2302 ann.printAnnotations(fp, indentStr); 2303 2304 fprintf(fp, 2305 "%spublic static final int[] %s = {\n" 2306 "%s", 2307 indentStr, nclassName.string(), 2308 getIndentSpace(indent+1)); 2309 2310 for (a=0; a<NA; a++) { 2311 if (a != 0) { 2312 if ((a&3) == 0) { 2313 fprintf(fp, ",\n%s", getIndentSpace(indent+1)); 2314 } else { 2315 fprintf(fp, ", "); 2316 } 2317 } 2318 fprintf(fp, "0x%08x", idents[a]); 2319 } 2320 2321 fprintf(fp, "\n%s};\n", indentStr); 2322 2323 for (a=0; a<NA; a++) { 2324 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 2325 if (pos >= 0) { 2326 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 2327 if (!publicFlags.itemAt(a) && !includePrivate) { 2328 continue; 2329 } 2330 String8 name8(sym.name); 2331 String16 comment(sym.comment); 2332 String16 typeComment; 2333 if (comment.size() <= 0) { 2334 comment = getAttributeComment(assets, name8, &typeComment); 2335 } else { 2336 getAttributeComment(assets, name8, &typeComment); 2337 } 2338 2339 uint32_t typeSpecFlags = 0; 2340 String16 name16(sym.name); 2341 assets->getIncludedResources().identifierForName( 2342 name16.string(), name16.size(), 2343 attr16.string(), attr16.size(), 2344 package16.string(), package16.size(), &typeSpecFlags); 2345 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(), 2346 // String8(attr16).string(), String8(name16).string(), typeSpecFlags); 2347 const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 2348 2349 AnnotationProcessor ann; 2350 fprintf(fp, "%s/**\n", indentStr); 2351 if (comment.size() > 0) { 2352 String8 cmt(comment); 2353 ann.preprocessComment(cmt); 2354 fprintf(fp, "%s <p>\n%s @attr description\n", indentStr, indentStr); 2355 fprintf(fp, "%s %s\n", indentStr, cmt.string()); 2356 } else { 2357 fprintf(fp, 2358 "%s <p>This symbol is the offset where the {@link %s.R.attr#%s}\n" 2359 "%s attribute's value can be found in the {@link #%s} array.\n", 2360 indentStr, 2361 getSymbolPackage(name8, assets, pub).string(), 2362 getSymbolName(name8).string(), 2363 indentStr, nclassName.string()); 2364 } 2365 if (typeComment.size() > 0) { 2366 String8 cmt(typeComment); 2367 ann.preprocessComment(cmt); 2368 fprintf(fp, "\n\n%s %s\n", indentStr, cmt.string()); 2369 } 2370 if (comment.size() > 0) { 2371 if (pub) { 2372 fprintf(fp, 2373 "%s <p>This corresponds to the global attribute\n" 2374 "%s resource symbol {@link %s.R.attr#%s}.\n", 2375 indentStr, indentStr, 2376 getSymbolPackage(name8, assets, true).string(), 2377 getSymbolName(name8).string()); 2378 } else { 2379 fprintf(fp, 2380 "%s <p>This is a private symbol.\n", indentStr); 2381 } 2382 } 2383 fprintf(fp, "%s @attr name %s:%s\n", indentStr, 2384 getSymbolPackage(name8, assets, pub).string(), 2385 getSymbolName(name8).string()); 2386 fprintf(fp, "%s*/\n", indentStr); 2387 ann.printAnnotations(fp, indentStr); 2388 2389 const char * id_format = nonConstantId ? 2390 "%spublic static int %s_%s = %d;\n" : 2391 "%spublic static final int %s_%s = %d;\n"; 2392 2393 fprintf(fp, 2394 id_format, 2395 indentStr, nclassName.string(), 2396 flattenSymbol(name8).string(), (int)pos); 2397 } 2398 } 2399 } 2400 2401 indent--; 2402 fprintf(fp, "%s};\n", getIndentSpace(indent)); 2403 return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR; 2404} 2405 2406static status_t writeTextLayoutClasses( 2407 FILE* fp, const sp<AaptAssets>& assets, 2408 const sp<AaptSymbols>& symbols, bool includePrivate) 2409{ 2410 String16 attr16("attr"); 2411 String16 package16(assets->getPackage()); 2412 2413 bool hasErrors = false; 2414 2415 size_t i; 2416 size_t N = symbols->getNestedSymbols().size(); 2417 for (i=0; i<N; i++) { 2418 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2419 String8 realClassName(symbols->getNestedSymbols().keyAt(i)); 2420 String8 nclassName(flattenSymbol(realClassName)); 2421 2422 SortedVector<uint32_t> idents; 2423 Vector<uint32_t> origOrder; 2424 Vector<bool> publicFlags; 2425 2426 size_t a; 2427 size_t NA = nsymbols->getSymbols().size(); 2428 for (a=0; a<NA; a++) { 2429 const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a)); 2430 int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32 2431 ? sym.int32Val : 0; 2432 bool isPublic = true; 2433 if (code == 0) { 2434 String16 name16(sym.name); 2435 uint32_t typeSpecFlags; 2436 code = assets->getIncludedResources().identifierForName( 2437 name16.string(), name16.size(), 2438 attr16.string(), attr16.size(), 2439 package16.string(), package16.size(), &typeSpecFlags); 2440 if (code == 0) { 2441 fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n", 2442 nclassName.string(), sym.name.string()); 2443 hasErrors = true; 2444 } 2445 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 2446 } 2447 idents.add(code); 2448 origOrder.add(code); 2449 publicFlags.add(isPublic); 2450 } 2451 2452 NA = idents.size(); 2453 2454 fprintf(fp, "int[] styleable %s {", nclassName.string()); 2455 2456 for (a=0; a<NA; a++) { 2457 if (a != 0) { 2458 fprintf(fp, ","); 2459 } 2460 fprintf(fp, " 0x%08x", idents[a]); 2461 } 2462 2463 fprintf(fp, " }\n"); 2464 2465 for (a=0; a<NA; a++) { 2466 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 2467 if (pos >= 0) { 2468 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 2469 if (!publicFlags.itemAt(a) && !includePrivate) { 2470 continue; 2471 } 2472 String8 name8(sym.name); 2473 String16 comment(sym.comment); 2474 String16 typeComment; 2475 if (comment.size() <= 0) { 2476 comment = getAttributeComment(assets, name8, &typeComment); 2477 } else { 2478 getAttributeComment(assets, name8, &typeComment); 2479 } 2480 2481 uint32_t typeSpecFlags = 0; 2482 String16 name16(sym.name); 2483 assets->getIncludedResources().identifierForName( 2484 name16.string(), name16.size(), 2485 attr16.string(), attr16.size(), 2486 package16.string(), package16.size(), &typeSpecFlags); 2487 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(), 2488 // String8(attr16).string(), String8(name16).string(), typeSpecFlags); 2489 //const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 2490 2491 fprintf(fp, 2492 "int styleable %s_%s %d\n", 2493 nclassName.string(), 2494 flattenSymbol(name8).string(), (int)pos); 2495 } 2496 } 2497 } 2498 2499 return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR; 2500} 2501 2502static status_t writeSymbolClass( 2503 FILE* fp, const sp<AaptAssets>& assets, bool includePrivate, 2504 const sp<AaptSymbols>& symbols, const String8& className, int indent, 2505 bool nonConstantId, bool emitCallback) 2506{ 2507 fprintf(fp, "%spublic %sfinal class %s {\n", 2508 getIndentSpace(indent), 2509 indent != 0 ? "static " : "", className.string()); 2510 indent++; 2511 2512 size_t i; 2513 status_t err = NO_ERROR; 2514 2515 const char * id_format = nonConstantId ? 2516 "%spublic static int %s=0x%08x;\n" : 2517 "%spublic static final int %s=0x%08x;\n"; 2518 2519 size_t N = symbols->getSymbols().size(); 2520 for (i=0; i<N; i++) { 2521 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 2522 if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) { 2523 continue; 2524 } 2525 if (!assets->isJavaSymbol(sym, includePrivate)) { 2526 continue; 2527 } 2528 String8 name8(sym.name); 2529 String16 comment(sym.comment); 2530 bool haveComment = false; 2531 AnnotationProcessor ann; 2532 if (comment.size() > 0) { 2533 haveComment = true; 2534 String8 cmt(comment); 2535 ann.preprocessComment(cmt); 2536 fprintf(fp, 2537 "%s/** %s\n", 2538 getIndentSpace(indent), cmt.string()); 2539 } 2540 String16 typeComment(sym.typeComment); 2541 if (typeComment.size() > 0) { 2542 String8 cmt(typeComment); 2543 ann.preprocessComment(cmt); 2544 if (!haveComment) { 2545 haveComment = true; 2546 fprintf(fp, 2547 "%s/** %s\n", getIndentSpace(indent), cmt.string()); 2548 } else { 2549 fprintf(fp, 2550 "%s %s\n", getIndentSpace(indent), cmt.string()); 2551 } 2552 } 2553 if (haveComment) { 2554 fprintf(fp,"%s */\n", getIndentSpace(indent)); 2555 } 2556 ann.printAnnotations(fp, getIndentSpace(indent)); 2557 fprintf(fp, id_format, 2558 getIndentSpace(indent), 2559 flattenSymbol(name8).string(), (int)sym.int32Val); 2560 } 2561 2562 for (i=0; i<N; i++) { 2563 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 2564 if (sym.typeCode != AaptSymbolEntry::TYPE_STRING) { 2565 continue; 2566 } 2567 if (!assets->isJavaSymbol(sym, includePrivate)) { 2568 continue; 2569 } 2570 String8 name8(sym.name); 2571 String16 comment(sym.comment); 2572 AnnotationProcessor ann; 2573 if (comment.size() > 0) { 2574 String8 cmt(comment); 2575 ann.preprocessComment(cmt); 2576 fprintf(fp, 2577 "%s/** %s\n" 2578 "%s */\n", 2579 getIndentSpace(indent), cmt.string(), 2580 getIndentSpace(indent)); 2581 } 2582 ann.printAnnotations(fp, getIndentSpace(indent)); 2583 fprintf(fp, "%spublic static final String %s=\"%s\";\n", 2584 getIndentSpace(indent), 2585 flattenSymbol(name8).string(), sym.stringVal.string()); 2586 } 2587 2588 sp<AaptSymbols> styleableSymbols; 2589 2590 N = symbols->getNestedSymbols().size(); 2591 for (i=0; i<N; i++) { 2592 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2593 String8 nclassName(symbols->getNestedSymbols().keyAt(i)); 2594 if (nclassName == "styleable") { 2595 styleableSymbols = nsymbols; 2596 } else { 2597 err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, 2598 indent, nonConstantId, false); 2599 } 2600 if (err != NO_ERROR) { 2601 return err; 2602 } 2603 } 2604 2605 if (styleableSymbols != NULL) { 2606 err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate, nonConstantId); 2607 if (err != NO_ERROR) { 2608 return err; 2609 } 2610 } 2611 2612 if (emitCallback) { 2613 fprintf(fp, "%spublic static void onResourcesLoaded(int packageId) {\n", 2614 getIndentSpace(indent)); 2615 writeResourceLoadedCallback(fp, assets, includePrivate, symbols, className, indent + 1); 2616 fprintf(fp, "%s}\n", getIndentSpace(indent)); 2617 } 2618 2619 indent--; 2620 fprintf(fp, "%s}\n", getIndentSpace(indent)); 2621 return NO_ERROR; 2622} 2623 2624static status_t writeTextSymbolClass( 2625 FILE* fp, const sp<AaptAssets>& assets, bool includePrivate, 2626 const sp<AaptSymbols>& symbols, const String8& className) 2627{ 2628 size_t i; 2629 status_t err = NO_ERROR; 2630 2631 size_t N = symbols->getSymbols().size(); 2632 for (i=0; i<N; i++) { 2633 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 2634 if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) { 2635 continue; 2636 } 2637 2638 if (!assets->isJavaSymbol(sym, includePrivate)) { 2639 continue; 2640 } 2641 2642 String8 name8(sym.name); 2643 fprintf(fp, "int %s %s 0x%08x\n", 2644 className.string(), 2645 flattenSymbol(name8).string(), (int)sym.int32Val); 2646 } 2647 2648 N = symbols->getNestedSymbols().size(); 2649 for (i=0; i<N; i++) { 2650 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2651 String8 nclassName(symbols->getNestedSymbols().keyAt(i)); 2652 if (nclassName == "styleable") { 2653 err = writeTextLayoutClasses(fp, assets, nsymbols, includePrivate); 2654 } else { 2655 err = writeTextSymbolClass(fp, assets, includePrivate, nsymbols, nclassName); 2656 } 2657 if (err != NO_ERROR) { 2658 return err; 2659 } 2660 } 2661 2662 return NO_ERROR; 2663} 2664 2665status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets, 2666 const String8& package, bool includePrivate, bool emitCallback) 2667{ 2668 if (!bundle->getRClassDir()) { 2669 return NO_ERROR; 2670 } 2671 2672 const char* textSymbolsDest = bundle->getOutputTextSymbols(); 2673 2674 String8 R("R"); 2675 const size_t N = assets->getSymbols().size(); 2676 for (size_t i=0; i<N; i++) { 2677 sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i); 2678 String8 className(assets->getSymbols().keyAt(i)); 2679 String8 dest(bundle->getRClassDir()); 2680 2681 if (bundle->getMakePackageDirs()) { 2682 String8 pkg(package); 2683 const char* last = pkg.string(); 2684 const char* s = last-1; 2685 do { 2686 s++; 2687 if (s > last && (*s == '.' || *s == 0)) { 2688 String8 part(last, s-last); 2689 dest.appendPath(part); 2690#ifdef _WIN32 2691 _mkdir(dest.string()); 2692#else 2693 mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP); 2694#endif 2695 last = s+1; 2696 } 2697 } while (*s); 2698 } 2699 dest.appendPath(className); 2700 dest.append(".java"); 2701 FILE* fp = fopen(dest.string(), "w+"); 2702 if (fp == NULL) { 2703 fprintf(stderr, "ERROR: Unable to open class file %s: %s\n", 2704 dest.string(), strerror(errno)); 2705 return UNKNOWN_ERROR; 2706 } 2707 if (bundle->getVerbose()) { 2708 printf(" Writing symbols for class %s.\n", className.string()); 2709 } 2710 2711 fprintf(fp, 2712 "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n" 2713 " *\n" 2714 " * This class was automatically generated by the\n" 2715 " * aapt tool from the resource data it found. It\n" 2716 " * should not be modified by hand.\n" 2717 " */\n" 2718 "\n" 2719 "package %s;\n\n", package.string()); 2720 2721 status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, 2722 className, 0, bundle->getNonConstantId(), emitCallback); 2723 fclose(fp); 2724 if (err != NO_ERROR) { 2725 return err; 2726 } 2727 2728 if (textSymbolsDest != NULL && R == className) { 2729 String8 textDest(textSymbolsDest); 2730 textDest.appendPath(className); 2731 textDest.append(".txt"); 2732 2733 FILE* fp = fopen(textDest.string(), "w+"); 2734 if (fp == NULL) { 2735 fprintf(stderr, "ERROR: Unable to open text symbol file %s: %s\n", 2736 textDest.string(), strerror(errno)); 2737 return UNKNOWN_ERROR; 2738 } 2739 if (bundle->getVerbose()) { 2740 printf(" Writing text symbols for class %s.\n", className.string()); 2741 } 2742 2743 status_t err = writeTextSymbolClass(fp, assets, includePrivate, symbols, 2744 className); 2745 fclose(fp); 2746 if (err != NO_ERROR) { 2747 return err; 2748 } 2749 } 2750 2751 // If we were asked to generate a dependency file, we'll go ahead and add this R.java 2752 // as a target in the dependency file right next to it. 2753 if (bundle->getGenDependencies() && R == className) { 2754 // Add this R.java to the dependency file 2755 String8 dependencyFile(bundle->getRClassDir()); 2756 dependencyFile.appendPath("R.java.d"); 2757 2758 FILE *fp = fopen(dependencyFile.string(), "a"); 2759 fprintf(fp,"%s \\\n", dest.string()); 2760 fclose(fp); 2761 } 2762 } 2763 2764 return NO_ERROR; 2765} 2766 2767 2768class ProguardKeepSet 2769{ 2770public: 2771 // { rule --> { file locations } } 2772 KeyedVector<String8, SortedVector<String8> > rules; 2773 2774 void add(const String8& rule, const String8& where); 2775}; 2776 2777void ProguardKeepSet::add(const String8& rule, const String8& where) 2778{ 2779 ssize_t index = rules.indexOfKey(rule); 2780 if (index < 0) { 2781 index = rules.add(rule, SortedVector<String8>()); 2782 } 2783 rules.editValueAt(index).add(where); 2784} 2785 2786void 2787addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName, 2788 const char* pkg, const String8& srcName, int line) 2789{ 2790 String8 className(inClassName); 2791 if (pkg != NULL) { 2792 // asdf --> package.asdf 2793 // .asdf .a.b --> package.asdf package.a.b 2794 // asdf.adsf --> asdf.asdf 2795 const char* p = className.string(); 2796 const char* q = strchr(p, '.'); 2797 if (p == q) { 2798 className = pkg; 2799 className.append(inClassName); 2800 } else if (q == NULL) { 2801 className = pkg; 2802 className.append("."); 2803 className.append(inClassName); 2804 } 2805 } 2806 2807 String8 rule("-keep class "); 2808 rule += className; 2809 rule += " { <init>(...); }"; 2810 2811 String8 location("view "); 2812 location += srcName; 2813 char lineno[20]; 2814 sprintf(lineno, ":%d", line); 2815 location += lineno; 2816 2817 keep->add(rule, location); 2818} 2819 2820void 2821addProguardKeepMethodRule(ProguardKeepSet* keep, const String8& memberName, 2822 const char* /* pkg */, const String8& srcName, int line) 2823{ 2824 String8 rule("-keepclassmembers class * { *** "); 2825 rule += memberName; 2826 rule += "(...); }"; 2827 2828 String8 location("onClick "); 2829 location += srcName; 2830 char lineno[20]; 2831 sprintf(lineno, ":%d", line); 2832 location += lineno; 2833 2834 keep->add(rule, location); 2835} 2836 2837status_t 2838writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets, bool mainDex) 2839{ 2840 status_t err; 2841 ResXMLTree tree; 2842 size_t len; 2843 ResXMLTree::event_code_t code; 2844 int depth = 0; 2845 bool inApplication = false; 2846 String8 error; 2847 sp<AaptGroup> assGroup; 2848 sp<AaptFile> assFile; 2849 String8 pkg; 2850 String8 defaultProcess; 2851 2852 // First, look for a package file to parse. This is required to 2853 // be able to generate the resource information. 2854 assGroup = assets->getFiles().valueFor(String8("AndroidManifest.xml")); 2855 if (assGroup == NULL) { 2856 fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n"); 2857 return -1; 2858 } 2859 2860 if (assGroup->getFiles().size() != 1) { 2861 fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n", 2862 assGroup->getFiles().valueAt(0)->getPrintableSource().string()); 2863 } 2864 2865 assFile = assGroup->getFiles().valueAt(0); 2866 2867 err = parseXMLResource(assFile, &tree); 2868 if (err != NO_ERROR) { 2869 return err; 2870 } 2871 2872 tree.restart(); 2873 2874 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 2875 if (code == ResXMLTree::END_TAG) { 2876 if (/* name == "Application" && */ depth == 2) { 2877 inApplication = false; 2878 } 2879 depth--; 2880 continue; 2881 } 2882 if (code != ResXMLTree::START_TAG) { 2883 continue; 2884 } 2885 depth++; 2886 String8 tag(tree.getElementName(&len)); 2887 // printf("Depth %d tag %s\n", depth, tag.string()); 2888 bool keepTag = false; 2889 if (depth == 1) { 2890 if (tag != "manifest") { 2891 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 2892 return -1; 2893 } 2894 pkg = AaptXml::getAttribute(tree, NULL, "package"); 2895 } else if (depth == 2) { 2896 if (tag == "application") { 2897 inApplication = true; 2898 keepTag = true; 2899 2900 String8 agent = AaptXml::getAttribute(tree, 2901 "http://schemas.android.com/apk/res/android", 2902 "backupAgent", &error); 2903 if (agent.length() > 0) { 2904 addProguardKeepRule(keep, agent, pkg.string(), 2905 assFile->getPrintableSource(), tree.getLineNumber()); 2906 } 2907 2908 if (mainDex) { 2909 defaultProcess = AaptXml::getAttribute(tree, 2910 "http://schemas.android.com/apk/res/android", "process", &error); 2911 if (error != "") { 2912 fprintf(stderr, "ERROR: %s\n", error.string()); 2913 return -1; 2914 } 2915 } 2916 } else if (tag == "instrumentation") { 2917 keepTag = true; 2918 } 2919 } 2920 if (!keepTag && inApplication && depth == 3) { 2921 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") { 2922 keepTag = true; 2923 } 2924 } 2925 if (keepTag) { 2926 String8 name = AaptXml::getAttribute(tree, 2927 "http://schemas.android.com/apk/res/android", "name", &error); 2928 if (error != "") { 2929 fprintf(stderr, "ERROR: %s\n", error.string()); 2930 return -1; 2931 } 2932 2933 keepTag = name.length() > 0; 2934 2935 if (keepTag && mainDex) { 2936 String8 componentProcess = AaptXml::getAttribute(tree, 2937 "http://schemas.android.com/apk/res/android", "process", &error); 2938 if (error != "") { 2939 fprintf(stderr, "ERROR: %s\n", error.string()); 2940 return -1; 2941 } 2942 2943 const String8& process = 2944 componentProcess.length() > 0 ? componentProcess : defaultProcess; 2945 keepTag = process.length() > 0 && process.find(":") != 0; 2946 } 2947 2948 if (keepTag) { 2949 addProguardKeepRule(keep, name, pkg.string(), 2950 assFile->getPrintableSource(), tree.getLineNumber()); 2951 } 2952 } 2953 } 2954 2955 return NO_ERROR; 2956} 2957 2958struct NamespaceAttributePair { 2959 const char* ns; 2960 const char* attr; 2961 2962 NamespaceAttributePair(const char* n, const char* a) : ns(n), attr(a) {} 2963 NamespaceAttributePair() : ns(NULL), attr(NULL) {} 2964}; 2965 2966status_t 2967writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile, 2968 const Vector<String8>& startTags, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs) 2969{ 2970 status_t err; 2971 ResXMLTree tree; 2972 size_t len; 2973 ResXMLTree::event_code_t code; 2974 2975 err = parseXMLResource(layoutFile, &tree); 2976 if (err != NO_ERROR) { 2977 return err; 2978 } 2979 2980 tree.restart(); 2981 2982 if (!startTags.isEmpty()) { 2983 bool haveStart = false; 2984 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 2985 if (code != ResXMLTree::START_TAG) { 2986 continue; 2987 } 2988 String8 tag(tree.getElementName(&len)); 2989 const size_t numStartTags = startTags.size(); 2990 for (size_t i = 0; i < numStartTags; i++) { 2991 if (tag == startTags[i]) { 2992 haveStart = true; 2993 } 2994 } 2995 break; 2996 } 2997 if (!haveStart) { 2998 return NO_ERROR; 2999 } 3000 } 3001 3002 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 3003 if (code != ResXMLTree::START_TAG) { 3004 continue; 3005 } 3006 String8 tag(tree.getElementName(&len)); 3007 3008 // If there is no '.', we'll assume that it's one of the built in names. 3009 if (strchr(tag.string(), '.')) { 3010 addProguardKeepRule(keep, tag, NULL, 3011 layoutFile->getPrintableSource(), tree.getLineNumber()); 3012 } else if (tagAttrPairs != NULL) { 3013 ssize_t tagIndex = tagAttrPairs->indexOfKey(tag); 3014 if (tagIndex >= 0) { 3015 const Vector<NamespaceAttributePair>& nsAttrVector = tagAttrPairs->valueAt(tagIndex); 3016 for (size_t i = 0; i < nsAttrVector.size(); i++) { 3017 const NamespaceAttributePair& nsAttr = nsAttrVector[i]; 3018 3019 ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr); 3020 if (attrIndex < 0) { 3021 // fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n", 3022 // layoutFile->getPrintableSource().string(), tree.getLineNumber(), 3023 // tag.string(), nsAttr.ns, nsAttr.attr); 3024 } else { 3025 size_t len; 3026 addProguardKeepRule(keep, 3027 String8(tree.getAttributeStringValue(attrIndex, &len)), NULL, 3028 layoutFile->getPrintableSource(), tree.getLineNumber()); 3029 } 3030 } 3031 } 3032 } 3033 ssize_t attrIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "onClick"); 3034 if (attrIndex >= 0) { 3035 size_t len; 3036 addProguardKeepMethodRule(keep, 3037 String8(tree.getAttributeStringValue(attrIndex, &len)), NULL, 3038 layoutFile->getPrintableSource(), tree.getLineNumber()); 3039 } 3040 } 3041 3042 return NO_ERROR; 3043} 3044 3045static void addTagAttrPair(KeyedVector<String8, Vector<NamespaceAttributePair> >* dest, 3046 const char* tag, const char* ns, const char* attr) { 3047 String8 tagStr(tag); 3048 ssize_t index = dest->indexOfKey(tagStr); 3049 3050 if (index < 0) { 3051 Vector<NamespaceAttributePair> vector; 3052 vector.add(NamespaceAttributePair(ns, attr)); 3053 dest->add(tagStr, vector); 3054 } else { 3055 dest->editValueAt(index).add(NamespaceAttributePair(ns, attr)); 3056 } 3057} 3058 3059status_t 3060writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets) 3061{ 3062 status_t err; 3063 const char* kClass = "class"; 3064 const char* kFragment = "fragment"; 3065 const String8 kTransition("transition"); 3066 const String8 kTransitionPrefix("transition-"); 3067 3068 // tag:attribute pairs that should be checked in layout files. 3069 KeyedVector<String8, Vector<NamespaceAttributePair> > kLayoutTagAttrPairs; 3070 addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, kClass); 3071 addTagAttrPair(&kLayoutTagAttrPairs, kFragment, NULL, kClass); 3072 addTagAttrPair(&kLayoutTagAttrPairs, kFragment, RESOURCES_ANDROID_NAMESPACE, "name"); 3073 3074 // tag:attribute pairs that should be checked in xml files. 3075 KeyedVector<String8, Vector<NamespaceAttributePair> > kXmlTagAttrPairs; 3076 addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, kFragment); 3077 addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, kFragment); 3078 3079 // tag:attribute pairs that should be checked in transition files. 3080 KeyedVector<String8, Vector<NamespaceAttributePair> > kTransitionTagAttrPairs; 3081 addTagAttrPair(&kTransitionTagAttrPairs, kTransition.string(), NULL, kClass); 3082 addTagAttrPair(&kTransitionTagAttrPairs, "pathMotion", NULL, kClass); 3083 3084 const Vector<sp<AaptDir> >& dirs = assets->resDirs(); 3085 const size_t K = dirs.size(); 3086 for (size_t k=0; k<K; k++) { 3087 const sp<AaptDir>& d = dirs.itemAt(k); 3088 const String8& dirName = d->getLeaf(); 3089 Vector<String8> startTags; 3090 const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs = NULL; 3091 if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) { 3092 tagAttrPairs = &kLayoutTagAttrPairs; 3093 } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) { 3094 startTags.add(String8("PreferenceScreen")); 3095 startTags.add(String8("preference-headers")); 3096 tagAttrPairs = &kXmlTagAttrPairs; 3097 } else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) { 3098 startTags.add(String8("menu")); 3099 tagAttrPairs = NULL; 3100 } else if (dirName == kTransition || (strncmp(dirName.string(), kTransitionPrefix.string(), 3101 kTransitionPrefix.size()) == 0)) { 3102 tagAttrPairs = &kTransitionTagAttrPairs; 3103 } else { 3104 continue; 3105 } 3106 3107 const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles(); 3108 const size_t N = groups.size(); 3109 for (size_t i=0; i<N; i++) { 3110 const sp<AaptGroup>& group = groups.valueAt(i); 3111 const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles(); 3112 const size_t M = files.size(); 3113 for (size_t j=0; j<M; j++) { 3114 err = writeProguardForXml(keep, files.valueAt(j), startTags, tagAttrPairs); 3115 if (err < 0) { 3116 return err; 3117 } 3118 } 3119 } 3120 } 3121 // Handle the overlays 3122 sp<AaptAssets> overlay = assets->getOverlay(); 3123 if (overlay.get()) { 3124 return writeProguardForLayouts(keep, overlay); 3125 } 3126 3127 return NO_ERROR; 3128} 3129 3130status_t 3131writeProguardSpec(const char* filename, const ProguardKeepSet& keep, status_t err) 3132{ 3133 FILE* fp = fopen(filename, "w+"); 3134 if (fp == NULL) { 3135 fprintf(stderr, "ERROR: Unable to open class file %s: %s\n", 3136 filename, strerror(errno)); 3137 return UNKNOWN_ERROR; 3138 } 3139 3140 const KeyedVector<String8, SortedVector<String8> >& rules = keep.rules; 3141 const size_t N = rules.size(); 3142 for (size_t i=0; i<N; i++) { 3143 const SortedVector<String8>& locations = rules.valueAt(i); 3144 const size_t M = locations.size(); 3145 for (size_t j=0; j<M; j++) { 3146 fprintf(fp, "# %s\n", locations.itemAt(j).string()); 3147 } 3148 fprintf(fp, "%s\n\n", rules.keyAt(i).string()); 3149 } 3150 fclose(fp); 3151 3152 return err; 3153} 3154 3155status_t 3156writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets) 3157{ 3158 status_t err = -1; 3159 3160 if (!bundle->getProguardFile()) { 3161 return NO_ERROR; 3162 } 3163 3164 ProguardKeepSet keep; 3165 3166 err = writeProguardForAndroidManifest(&keep, assets, false); 3167 if (err < 0) { 3168 return err; 3169 } 3170 3171 err = writeProguardForLayouts(&keep, assets); 3172 if (err < 0) { 3173 return err; 3174 } 3175 3176 return writeProguardSpec(bundle->getProguardFile(), keep, err); 3177} 3178 3179status_t 3180writeMainDexProguardFile(Bundle* bundle, const sp<AaptAssets>& assets) 3181{ 3182 status_t err = -1; 3183 3184 if (!bundle->getMainDexProguardFile()) { 3185 return NO_ERROR; 3186 } 3187 3188 ProguardKeepSet keep; 3189 3190 err = writeProguardForAndroidManifest(&keep, assets, true); 3191 if (err < 0) { 3192 return err; 3193 } 3194 3195 return writeProguardSpec(bundle->getMainDexProguardFile(), keep, err); 3196} 3197 3198// Loops through the string paths and writes them to the file pointer 3199// Each file path is written on its own line with a terminating backslash. 3200status_t writePathsToFile(const sp<FilePathStore>& files, FILE* fp) 3201{ 3202 status_t deps = -1; 3203 for (size_t file_i = 0; file_i < files->size(); ++file_i) { 3204 // Add the full file path to the dependency file 3205 fprintf(fp, "%s \\\n", files->itemAt(file_i).string()); 3206 deps++; 3207 } 3208 return deps; 3209} 3210 3211status_t 3212writeDependencyPreReqs(Bundle* /* bundle */, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw) 3213{ 3214 status_t deps = -1; 3215 deps += writePathsToFile(assets->getFullResPaths(), fp); 3216 if (includeRaw) { 3217 deps += writePathsToFile(assets->getFullAssetPaths(), fp); 3218 } 3219 return deps; 3220} 3221