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