Resource.cpp revision ad2d07d2d98a46babb2a9472413fe9ce5080ca76
1// 2// Copyright 2006 The Android Open Source Project 3// 4// Build resource files from raw assets. 5// 6#include "AaptAssets.h" 7#include "AaptXml.h" 8#include "CacheUpdater.h" 9#include "CrunchCache.h" 10#include "FileFinder.h" 11#include "Images.h" 12#include "IndentPrinter.h" 13#include "Main.h" 14#include "ResourceTable.h" 15#include "StringPool.h" 16#include "WorkQueue.h" 17#include "XMLNode.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 } else { 774 const XMLNode::attribute_entry* attr = root->getAttribute( 775 String16(RESOURCES_ANDROID_NAMESPACE), String16("versionCode")); 776 if (attr != NULL) { 777 bundle->setVersionCode(strdup(String8(attr->string).string())); 778 } 779 } 780 781 if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName", 782 bundle->getVersionName(), errorOnFailedInsert, replaceVersion)) { 783 return UNKNOWN_ERROR; 784 } 785 786 if (bundle->getMinSdkVersion() != NULL 787 || bundle->getTargetSdkVersion() != NULL 788 || bundle->getMaxSdkVersion() != NULL) { 789 sp<XMLNode> vers = root->getChildElement(String16(), String16("uses-sdk")); 790 if (vers == NULL) { 791 vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk")); 792 root->insertChildAt(vers, 0); 793 } 794 795 if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion", 796 bundle->getMinSdkVersion(), errorOnFailedInsert)) { 797 return UNKNOWN_ERROR; 798 } 799 if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion", 800 bundle->getTargetSdkVersion(), errorOnFailedInsert)) { 801 return UNKNOWN_ERROR; 802 } 803 if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion", 804 bundle->getMaxSdkVersion(), errorOnFailedInsert)) { 805 return UNKNOWN_ERROR; 806 } 807 } 808 809 if (bundle->getPlatformBuildVersionCode() != "") { 810 if (!addTagAttribute(root, "", "platformBuildVersionCode", 811 bundle->getPlatformBuildVersionCode(), errorOnFailedInsert, true)) { 812 return UNKNOWN_ERROR; 813 } 814 } 815 816 if (bundle->getPlatformBuildVersionName() != "") { 817 if (!addTagAttribute(root, "", "platformBuildVersionName", 818 bundle->getPlatformBuildVersionName(), errorOnFailedInsert, true)) { 819 return UNKNOWN_ERROR; 820 } 821 } 822 823 if (bundle->getDebugMode()) { 824 sp<XMLNode> application = root->getChildElement(String16(), String16("application")); 825 if (application != NULL) { 826 if (!addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true", 827 errorOnFailedInsert)) { 828 return UNKNOWN_ERROR; 829 } 830 } 831 } 832 833 // Deal with manifest package name overrides 834 const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride(); 835 if (manifestPackageNameOverride != NULL) { 836 // Update the actual package name 837 XMLNode::attribute_entry* attr = root->editAttribute(String16(), String16("package")); 838 if (attr == NULL) { 839 fprintf(stderr, "package name is required with --rename-manifest-package.\n"); 840 return UNKNOWN_ERROR; 841 } 842 String8 origPackage(attr->string); 843 attr->string.setTo(String16(manifestPackageNameOverride)); 844 NOISY(printf("Overriding package '%s' to be '%s'\n", origPackage.string(), manifestPackageNameOverride)); 845 846 // Make class names fully qualified 847 sp<XMLNode> application = root->getChildElement(String16(), String16("application")); 848 if (application != NULL) { 849 fullyQualifyClassName(origPackage, application, String16("name")); 850 fullyQualifyClassName(origPackage, application, String16("backupAgent")); 851 852 Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(application->getChildren()); 853 for (size_t i = 0; i < children.size(); i++) { 854 sp<XMLNode> child = children.editItemAt(i); 855 String8 tag(child->getElementName()); 856 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") { 857 fullyQualifyClassName(origPackage, child, String16("name")); 858 } else if (tag == "activity-alias") { 859 fullyQualifyClassName(origPackage, child, String16("name")); 860 fullyQualifyClassName(origPackage, child, String16("targetActivity")); 861 } 862 } 863 } 864 } 865 866 // Deal with manifest package name overrides 867 const char* instrumentationPackageNameOverride = bundle->getInstrumentationPackageNameOverride(); 868 if (instrumentationPackageNameOverride != NULL) { 869 // Fix up instrumentation targets. 870 Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(root->getChildren()); 871 for (size_t i = 0; i < children.size(); i++) { 872 sp<XMLNode> child = children.editItemAt(i); 873 String8 tag(child->getElementName()); 874 if (tag == "instrumentation") { 875 XMLNode::attribute_entry* attr = child->editAttribute( 876 String16("http://schemas.android.com/apk/res/android"), String16("targetPackage")); 877 if (attr != NULL) { 878 attr->string.setTo(String16(instrumentationPackageNameOverride)); 879 } 880 } 881 } 882 } 883 884 // Generate split name if feature is present. 885 const XMLNode::attribute_entry* attr = root->getAttribute(String16(), String16("featureName")); 886 if (attr != NULL) { 887 String16 splitName("feature_"); 888 splitName.append(attr->string); 889 status_t err = root->addAttribute(String16(), String16("split"), splitName); 890 if (err != NO_ERROR) { 891 ALOGE("Failed to insert split name into AndroidManifest.xml"); 892 return err; 893 } 894 } 895 896 return NO_ERROR; 897} 898 899static int32_t getPlatformAssetCookie(const AssetManager& assets) { 900 // Find the system package (0x01). AAPT always generates attributes 901 // with the type 0x01, so we're looking for the first attribute 902 // resource in the system package. 903 const ResTable& table = assets.getResources(true); 904 Res_value val; 905 ssize_t idx = table.getResource(0x01010000, &val, true); 906 if (idx != NO_ERROR) { 907 // Try as a bag. 908 const ResTable::bag_entry* entry; 909 ssize_t cnt = table.lockBag(0x01010000, &entry); 910 if (cnt >= 0) { 911 idx = entry->stringBlock; 912 } 913 table.unlockBag(entry); 914 } 915 916 if (idx < 0) { 917 return 0; 918 } 919 return table.getTableCookie(idx); 920} 921 922enum { 923 VERSION_CODE_ATTR = 0x0101021b, 924 VERSION_NAME_ATTR = 0x0101021c, 925}; 926 927static ssize_t extractPlatformBuildVersion(ResXMLTree& tree, Bundle* bundle) { 928 size_t len; 929 ResXMLTree::event_code_t code; 930 while ((code = tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 931 if (code != ResXMLTree::START_TAG) { 932 continue; 933 } 934 935 const char16_t* ctag16 = tree.getElementName(&len); 936 if (ctag16 == NULL) { 937 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n"); 938 return UNKNOWN_ERROR; 939 } 940 941 String8 tag(ctag16, len); 942 if (tag != "manifest") { 943 continue; 944 } 945 946 String8 error; 947 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR, &error); 948 if (error != "") { 949 fprintf(stderr, "ERROR: failed to get platform version code\n"); 950 return UNKNOWN_ERROR; 951 } 952 953 if (versionCode >= 0 && bundle->getPlatformBuildVersionCode() == "") { 954 bundle->setPlatformBuildVersionCode(String8::format("%d", versionCode)); 955 } 956 957 String8 versionName = AaptXml::getAttribute(tree, VERSION_NAME_ATTR, &error); 958 if (error != "") { 959 fprintf(stderr, "ERROR: failed to get platform version name\n"); 960 return UNKNOWN_ERROR; 961 } 962 963 if (versionName != "" && bundle->getPlatformBuildVersionName() == "") { 964 bundle->setPlatformBuildVersionName(versionName); 965 } 966 return NO_ERROR; 967 } 968 969 fprintf(stderr, "ERROR: no <manifest> tag found in platform AndroidManifest.xml\n"); 970 return UNKNOWN_ERROR; 971} 972 973static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) { 974 int32_t cookie = getPlatformAssetCookie(assets); 975 if (cookie == 0) { 976 fprintf(stderr, "ERROR: Platform package not found\n"); 977 return UNKNOWN_ERROR; 978 } 979 980 ResXMLTree tree; 981 Asset* asset = assets.openNonAsset(cookie, "AndroidManifest.xml", Asset::ACCESS_STREAMING); 982 if (asset == NULL) { 983 fprintf(stderr, "ERROR: Platform AndroidManifest.xml not found\n"); 984 return UNKNOWN_ERROR; 985 } 986 987 ssize_t result = NO_ERROR; 988 if (tree.setTo(asset->getBuffer(true), asset->getLength()) != NO_ERROR) { 989 fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n"); 990 result = UNKNOWN_ERROR; 991 } else { 992 result = extractPlatformBuildVersion(tree, bundle); 993 } 994 995 delete asset; 996 return result; 997} 998 999#define ASSIGN_IT(n) \ 1000 do { \ 1001 ssize_t index = resources->indexOfKey(String8(#n)); \ 1002 if (index >= 0) { \ 1003 n ## s = resources->valueAt(index); \ 1004 } \ 1005 } while (0) 1006 1007status_t updatePreProcessedCache(Bundle* bundle) 1008{ 1009 #if BENCHMARK 1010 fprintf(stdout, "BENCHMARK: Starting PNG PreProcessing \n"); 1011 long startPNGTime = clock(); 1012 #endif /* BENCHMARK */ 1013 1014 String8 source(bundle->getResourceSourceDirs()[0]); 1015 String8 dest(bundle->getCrunchedOutputDir()); 1016 1017 FileFinder* ff = new SystemFileFinder(); 1018 CrunchCache cc(source,dest,ff); 1019 1020 CacheUpdater* cu = new SystemCacheUpdater(bundle); 1021 size_t numFiles = cc.crunch(cu); 1022 1023 if (bundle->getVerbose()) 1024 fprintf(stdout, "Crunched %d PNG files to update cache\n", (int)numFiles); 1025 1026 delete ff; 1027 delete cu; 1028 1029 #if BENCHMARK 1030 fprintf(stdout, "BENCHMARK: End PNG PreProcessing. Time Elapsed: %f ms \n" 1031 ,(clock() - startPNGTime)/1000.0); 1032 #endif /* BENCHMARK */ 1033 return 0; 1034} 1035 1036status_t generateAndroidManifestForSplit(Bundle* bundle, const sp<AaptAssets>& assets, 1037 const sp<ApkSplit>& split, sp<AaptFile>& outFile, ResourceTable* table) { 1038 const String8 filename("AndroidManifest.xml"); 1039 const String16 androidPrefix("android"); 1040 const String16 androidNSUri("http://schemas.android.com/apk/res/android"); 1041 sp<XMLNode> root = XMLNode::newNamespace(filename, androidPrefix, androidNSUri); 1042 1043 // Build the <manifest> tag 1044 sp<XMLNode> manifest = XMLNode::newElement(filename, String16(), String16("manifest")); 1045 1046 // Add the 'package' attribute which is set to the package name. 1047 const char* packageName = assets->getPackage(); 1048 const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride(); 1049 if (manifestPackageNameOverride != NULL) { 1050 packageName = manifestPackageNameOverride; 1051 } 1052 manifest->addAttribute(String16(), String16("package"), String16(packageName)); 1053 1054 // Add the 'versionCode' attribute which is set to the original version code. 1055 if (!addTagAttribute(manifest, RESOURCES_ANDROID_NAMESPACE, "versionCode", 1056 bundle->getVersionCode(), true, true)) { 1057 return UNKNOWN_ERROR; 1058 } 1059 1060 // Add the 'split' attribute which describes the configurations included. 1061 String8 splitName("config."); 1062 splitName.append(split->getPackageSafeName()); 1063 manifest->addAttribute(String16(), String16("split"), String16(splitName)); 1064 1065 // Build an empty <application> tag (required). 1066 sp<XMLNode> app = XMLNode::newElement(filename, String16(), String16("application")); 1067 1068 // Add the 'hasCode' attribute which is never true for resource splits. 1069 if (!addTagAttribute(app, RESOURCES_ANDROID_NAMESPACE, "hasCode", 1070 "false", true, true)) { 1071 return UNKNOWN_ERROR; 1072 } 1073 1074 manifest->addChild(app); 1075 root->addChild(manifest); 1076 1077 int err = compileXmlFile(assets, root, outFile, table); 1078 if (err < NO_ERROR) { 1079 return err; 1080 } 1081 outFile->setCompressionMethod(ZipEntry::kCompressDeflated); 1082 return NO_ERROR; 1083} 1084 1085status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder) 1086{ 1087 // First, look for a package file to parse. This is required to 1088 // be able to generate the resource information. 1089 sp<AaptGroup> androidManifestFile = 1090 assets->getFiles().valueFor(String8("AndroidManifest.xml")); 1091 if (androidManifestFile == NULL) { 1092 fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n"); 1093 return UNKNOWN_ERROR; 1094 } 1095 1096 status_t err = parsePackage(bundle, assets, androidManifestFile); 1097 if (err != NO_ERROR) { 1098 return err; 1099 } 1100 1101 NOISY(printf("Creating resources for package %s\n", 1102 assets->getPackage().string())); 1103 1104 ResourceTable::PackageType packageType = ResourceTable::App; 1105 if (bundle->getBuildSharedLibrary()) { 1106 packageType = ResourceTable::SharedLibrary; 1107 } else if (bundle->getExtending()) { 1108 packageType = ResourceTable::System; 1109 } else if (!bundle->getFeatureOfPackage().isEmpty()) { 1110 packageType = ResourceTable::AppFeature; 1111 } 1112 1113 ResourceTable table(bundle, String16(assets->getPackage()), packageType); 1114 err = table.addIncludedResources(bundle, assets); 1115 if (err != NO_ERROR) { 1116 return err; 1117 } 1118 1119 NOISY(printf("Found %d included resource packages\n", (int)table.size())); 1120 1121 // Standard flags for compiled XML and optional UTF-8 encoding 1122 int xmlFlags = XML_COMPILE_STANDARD_RESOURCE; 1123 1124 /* Only enable UTF-8 if the caller of aapt didn't specifically 1125 * request UTF-16 encoding and the parameters of this package 1126 * allow UTF-8 to be used. 1127 */ 1128 if (!bundle->getUTF16StringsOption()) { 1129 xmlFlags |= XML_COMPILE_UTF8; 1130 } 1131 1132 // -------------------------------------------------------------- 1133 // First, gather all resource information. 1134 // -------------------------------------------------------------- 1135 1136 // resType -> leafName -> group 1137 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 1138 new KeyedVector<String8, sp<ResourceTypeSet> >; 1139 collect_files(assets, resources); 1140 1141 sp<ResourceTypeSet> drawables; 1142 sp<ResourceTypeSet> layouts; 1143 sp<ResourceTypeSet> anims; 1144 sp<ResourceTypeSet> animators; 1145 sp<ResourceTypeSet> interpolators; 1146 sp<ResourceTypeSet> transitions; 1147 sp<ResourceTypeSet> xmls; 1148 sp<ResourceTypeSet> raws; 1149 sp<ResourceTypeSet> colors; 1150 sp<ResourceTypeSet> menus; 1151 sp<ResourceTypeSet> mipmaps; 1152 1153 ASSIGN_IT(drawable); 1154 ASSIGN_IT(layout); 1155 ASSIGN_IT(anim); 1156 ASSIGN_IT(animator); 1157 ASSIGN_IT(interpolator); 1158 ASSIGN_IT(transition); 1159 ASSIGN_IT(xml); 1160 ASSIGN_IT(raw); 1161 ASSIGN_IT(color); 1162 ASSIGN_IT(menu); 1163 ASSIGN_IT(mipmap); 1164 1165 assets->setResources(resources); 1166 // now go through any resource overlays and collect their files 1167 sp<AaptAssets> current = assets->getOverlay(); 1168 while(current.get()) { 1169 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 1170 new KeyedVector<String8, sp<ResourceTypeSet> >; 1171 current->setResources(resources); 1172 collect_files(current, resources); 1173 current = current->getOverlay(); 1174 } 1175 // apply the overlay files to the base set 1176 if (!applyFileOverlay(bundle, assets, &drawables, "drawable") || 1177 !applyFileOverlay(bundle, assets, &layouts, "layout") || 1178 !applyFileOverlay(bundle, assets, &anims, "anim") || 1179 !applyFileOverlay(bundle, assets, &animators, "animator") || 1180 !applyFileOverlay(bundle, assets, &interpolators, "interpolator") || 1181 !applyFileOverlay(bundle, assets, &transitions, "transition") || 1182 !applyFileOverlay(bundle, assets, &xmls, "xml") || 1183 !applyFileOverlay(bundle, assets, &raws, "raw") || 1184 !applyFileOverlay(bundle, assets, &colors, "color") || 1185 !applyFileOverlay(bundle, assets, &menus, "menu") || 1186 !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) { 1187 return UNKNOWN_ERROR; 1188 } 1189 1190 bool hasErrors = false; 1191 1192 if (drawables != NULL) { 1193 if (bundle->getOutputAPKFile() != NULL) { 1194 err = preProcessImages(bundle, assets, drawables, "drawable"); 1195 } 1196 if (err == NO_ERROR) { 1197 err = makeFileResources(bundle, assets, &table, drawables, "drawable"); 1198 if (err != NO_ERROR) { 1199 hasErrors = true; 1200 } 1201 } else { 1202 hasErrors = true; 1203 } 1204 } 1205 1206 if (mipmaps != NULL) { 1207 if (bundle->getOutputAPKFile() != NULL) { 1208 err = preProcessImages(bundle, assets, mipmaps, "mipmap"); 1209 } 1210 if (err == NO_ERROR) { 1211 err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap"); 1212 if (err != NO_ERROR) { 1213 hasErrors = true; 1214 } 1215 } else { 1216 hasErrors = true; 1217 } 1218 } 1219 1220 if (layouts != NULL) { 1221 err = makeFileResources(bundle, assets, &table, layouts, "layout"); 1222 if (err != NO_ERROR) { 1223 hasErrors = true; 1224 } 1225 } 1226 1227 if (anims != NULL) { 1228 err = makeFileResources(bundle, assets, &table, anims, "anim"); 1229 if (err != NO_ERROR) { 1230 hasErrors = true; 1231 } 1232 } 1233 1234 if (animators != NULL) { 1235 err = makeFileResources(bundle, assets, &table, animators, "animator"); 1236 if (err != NO_ERROR) { 1237 hasErrors = true; 1238 } 1239 } 1240 1241 if (transitions != NULL) { 1242 err = makeFileResources(bundle, assets, &table, transitions, "transition"); 1243 if (err != NO_ERROR) { 1244 hasErrors = true; 1245 } 1246 } 1247 1248 if (interpolators != NULL) { 1249 err = makeFileResources(bundle, assets, &table, interpolators, "interpolator"); 1250 if (err != NO_ERROR) { 1251 hasErrors = true; 1252 } 1253 } 1254 1255 if (xmls != NULL) { 1256 err = makeFileResources(bundle, assets, &table, xmls, "xml"); 1257 if (err != NO_ERROR) { 1258 hasErrors = true; 1259 } 1260 } 1261 1262 if (raws != NULL) { 1263 err = makeFileResources(bundle, assets, &table, raws, "raw"); 1264 if (err != NO_ERROR) { 1265 hasErrors = true; 1266 } 1267 } 1268 1269 // compile resources 1270 current = assets; 1271 while(current.get()) { 1272 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 1273 current->getResources(); 1274 1275 ssize_t index = resources->indexOfKey(String8("values")); 1276 if (index >= 0) { 1277 ResourceDirIterator it(resources->valueAt(index), String8("values")); 1278 ssize_t res; 1279 while ((res=it.next()) == NO_ERROR) { 1280 sp<AaptFile> file = it.getFile(); 1281 res = compileResourceFile(bundle, assets, file, it.getParams(), 1282 (current!=assets), &table); 1283 if (res != NO_ERROR) { 1284 hasErrors = true; 1285 } 1286 } 1287 } 1288 current = current->getOverlay(); 1289 } 1290 1291 if (colors != NULL) { 1292 err = makeFileResources(bundle, assets, &table, colors, "color"); 1293 if (err != NO_ERROR) { 1294 hasErrors = true; 1295 } 1296 } 1297 1298 if (menus != NULL) { 1299 err = makeFileResources(bundle, assets, &table, menus, "menu"); 1300 if (err != NO_ERROR) { 1301 hasErrors = true; 1302 } 1303 } 1304 1305 // -------------------------------------------------------------------- 1306 // Assignment of resource IDs and initial generation of resource table. 1307 // -------------------------------------------------------------------- 1308 1309 if (table.hasResources()) { 1310 err = table.assignResourceIds(); 1311 if (err < NO_ERROR) { 1312 return err; 1313 } 1314 } 1315 1316 // -------------------------------------------------------------- 1317 // Finally, we can now we can compile XML files, which may reference 1318 // resources. 1319 // -------------------------------------------------------------- 1320 1321 if (layouts != NULL) { 1322 ResourceDirIterator it(layouts, String8("layout")); 1323 while ((err=it.next()) == NO_ERROR) { 1324 String8 src = it.getFile()->getPrintableSource(); 1325 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1326 if (err == NO_ERROR) { 1327 ResXMLTree block; 1328 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); 1329 checkForIds(src, block); 1330 } else { 1331 hasErrors = true; 1332 } 1333 } 1334 1335 if (err < NO_ERROR) { 1336 hasErrors = true; 1337 } 1338 err = NO_ERROR; 1339 } 1340 1341 if (anims != NULL) { 1342 ResourceDirIterator it(anims, String8("anim")); 1343 while ((err=it.next()) == NO_ERROR) { 1344 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1345 if (err != NO_ERROR) { 1346 hasErrors = true; 1347 } 1348 } 1349 1350 if (err < NO_ERROR) { 1351 hasErrors = true; 1352 } 1353 err = NO_ERROR; 1354 } 1355 1356 if (animators != NULL) { 1357 ResourceDirIterator it(animators, String8("animator")); 1358 while ((err=it.next()) == NO_ERROR) { 1359 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1360 if (err != NO_ERROR) { 1361 hasErrors = true; 1362 } 1363 } 1364 1365 if (err < NO_ERROR) { 1366 hasErrors = true; 1367 } 1368 err = NO_ERROR; 1369 } 1370 1371 if (interpolators != NULL) { 1372 ResourceDirIterator it(interpolators, String8("interpolator")); 1373 while ((err=it.next()) == NO_ERROR) { 1374 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1375 if (err != NO_ERROR) { 1376 hasErrors = true; 1377 } 1378 } 1379 1380 if (err < NO_ERROR) { 1381 hasErrors = true; 1382 } 1383 err = NO_ERROR; 1384 } 1385 1386 if (transitions != NULL) { 1387 ResourceDirIterator it(transitions, String8("transition")); 1388 while ((err=it.next()) == NO_ERROR) { 1389 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1390 if (err != NO_ERROR) { 1391 hasErrors = true; 1392 } 1393 } 1394 1395 if (err < NO_ERROR) { 1396 hasErrors = true; 1397 } 1398 err = NO_ERROR; 1399 } 1400 1401 if (xmls != NULL) { 1402 ResourceDirIterator it(xmls, String8("xml")); 1403 while ((err=it.next()) == NO_ERROR) { 1404 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1405 if (err != NO_ERROR) { 1406 hasErrors = true; 1407 } 1408 } 1409 1410 if (err < NO_ERROR) { 1411 hasErrors = true; 1412 } 1413 err = NO_ERROR; 1414 } 1415 1416 if (drawables != NULL) { 1417 ResourceDirIterator it(drawables, String8("drawable")); 1418 while ((err=it.next()) == NO_ERROR) { 1419 err = postProcessImage(assets, &table, it.getFile()); 1420 if (err != NO_ERROR) { 1421 hasErrors = true; 1422 } 1423 } 1424 1425 if (err < NO_ERROR) { 1426 hasErrors = true; 1427 } 1428 err = NO_ERROR; 1429 } 1430 1431 if (colors != NULL) { 1432 ResourceDirIterator it(colors, String8("color")); 1433 while ((err=it.next()) == NO_ERROR) { 1434 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1435 if (err != NO_ERROR) { 1436 hasErrors = true; 1437 } 1438 } 1439 1440 if (err < NO_ERROR) { 1441 hasErrors = true; 1442 } 1443 err = NO_ERROR; 1444 } 1445 1446 if (menus != NULL) { 1447 ResourceDirIterator it(menus, String8("menu")); 1448 while ((err=it.next()) == NO_ERROR) { 1449 String8 src = it.getFile()->getPrintableSource(); 1450 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1451 if (err == NO_ERROR) { 1452 ResXMLTree block; 1453 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); 1454 checkForIds(src, block); 1455 } else { 1456 hasErrors = true; 1457 } 1458 } 1459 1460 if (err < NO_ERROR) { 1461 hasErrors = true; 1462 } 1463 err = NO_ERROR; 1464 } 1465 1466 if (table.validateLocalizations()) { 1467 hasErrors = true; 1468 } 1469 1470 if (hasErrors) { 1471 return UNKNOWN_ERROR; 1472 } 1473 1474 // If we're not overriding the platform build versions, 1475 // extract them from the platform APK. 1476 if (packageType != ResourceTable::System && 1477 (bundle->getPlatformBuildVersionCode() == "" || 1478 bundle->getPlatformBuildVersionName() == "")) { 1479 err = extractPlatformBuildVersion(assets->getAssetManager(), bundle); 1480 if (err != NO_ERROR) { 1481 return UNKNOWN_ERROR; 1482 } 1483 } 1484 1485 const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0)); 1486 String8 manifestPath(manifestFile->getPrintableSource()); 1487 1488 // Generate final compiled manifest file. 1489 manifestFile->clearData(); 1490 sp<XMLNode> manifestTree = XMLNode::parse(manifestFile); 1491 if (manifestTree == NULL) { 1492 return UNKNOWN_ERROR; 1493 } 1494 err = massageManifest(bundle, manifestTree); 1495 if (err < NO_ERROR) { 1496 return err; 1497 } 1498 err = compileXmlFile(assets, manifestTree, manifestFile, &table); 1499 if (err < NO_ERROR) { 1500 return err; 1501 } 1502 1503 //block.restart(); 1504 //printXMLBlock(&block); 1505 1506 // -------------------------------------------------------------- 1507 // Generate the final resource table. 1508 // Re-flatten because we may have added new resource IDs 1509 // -------------------------------------------------------------- 1510 1511 ResTable finalResTable; 1512 sp<AaptFile> resFile; 1513 1514 if (table.hasResources()) { 1515 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R")); 1516 err = table.addSymbols(symbols); 1517 if (err < NO_ERROR) { 1518 return err; 1519 } 1520 1521 Vector<sp<ApkSplit> >& splits = builder->getSplits(); 1522 const size_t numSplits = splits.size(); 1523 for (size_t i = 0; i < numSplits; i++) { 1524 sp<ApkSplit>& split = splits.editItemAt(i); 1525 sp<AaptFile> flattenedTable = new AaptFile(String8("resources.arsc"), 1526 AaptGroupEntry(), String8()); 1527 err = table.flatten(bundle, split->getResourceFilter(), 1528 flattenedTable, split->isBase()); 1529 if (err != NO_ERROR) { 1530 fprintf(stderr, "Failed to generate resource table for split '%s'\n", 1531 split->getPrintableName().string()); 1532 return err; 1533 } 1534 split->addEntry(String8("resources.arsc"), flattenedTable); 1535 1536 if (split->isBase()) { 1537 resFile = flattenedTable; 1538 err = finalResTable.add(flattenedTable->getData(), flattenedTable->getSize()); 1539 if (err != NO_ERROR) { 1540 fprintf(stderr, "Generated resource table is corrupt.\n"); 1541 return err; 1542 } 1543 } else { 1544 sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"), 1545 AaptGroupEntry(), String8()); 1546 err = generateAndroidManifestForSplit(bundle, assets, split, 1547 generatedManifest, &table); 1548 if (err != NO_ERROR) { 1549 fprintf(stderr, "Failed to generate AndroidManifest.xml for split '%s'\n", 1550 split->getPrintableName().string()); 1551 return err; 1552 } 1553 split->addEntry(String8("AndroidManifest.xml"), generatedManifest); 1554 } 1555 } 1556 1557 if (bundle->getPublicOutputFile()) { 1558 FILE* fp = fopen(bundle->getPublicOutputFile(), "w+"); 1559 if (fp == NULL) { 1560 fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n", 1561 (const char*)bundle->getPublicOutputFile(), strerror(errno)); 1562 return UNKNOWN_ERROR; 1563 } 1564 if (bundle->getVerbose()) { 1565 printf(" Writing public definitions to %s.\n", bundle->getPublicOutputFile()); 1566 } 1567 table.writePublicDefinitions(String16(assets->getPackage()), fp); 1568 fclose(fp); 1569 } 1570 1571 if (finalResTable.getTableCount() == 0 || resFile == NULL) { 1572 fprintf(stderr, "No resource table was generated.\n"); 1573 return UNKNOWN_ERROR; 1574 } 1575 } 1576 1577 // Perform a basic validation of the manifest file. This time we 1578 // parse it with the comments intact, so that we can use them to 1579 // generate java docs... so we are not going to write this one 1580 // back out to the final manifest data. 1581 sp<AaptFile> outManifestFile = new AaptFile(manifestFile->getSourceFile(), 1582 manifestFile->getGroupEntry(), 1583 manifestFile->getResourceType()); 1584 err = compileXmlFile(assets, manifestFile, 1585 outManifestFile, &table, 1586 XML_COMPILE_ASSIGN_ATTRIBUTE_IDS 1587 | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES); 1588 if (err < NO_ERROR) { 1589 return err; 1590 } 1591 ResXMLTree block; 1592 block.setTo(outManifestFile->getData(), outManifestFile->getSize(), true); 1593 String16 manifest16("manifest"); 1594 String16 permission16("permission"); 1595 String16 permission_group16("permission-group"); 1596 String16 uses_permission16("uses-permission"); 1597 String16 instrumentation16("instrumentation"); 1598 String16 application16("application"); 1599 String16 provider16("provider"); 1600 String16 service16("service"); 1601 String16 receiver16("receiver"); 1602 String16 activity16("activity"); 1603 String16 action16("action"); 1604 String16 category16("category"); 1605 String16 data16("scheme"); 1606 String16 feature_group16("feature-group"); 1607 String16 uses_feature16("uses-feature"); 1608 const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz" 1609 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789"; 1610 const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz" 1611 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-"; 1612 const char* classIdentChars = "abcdefghijklmnopqrstuvwxyz" 1613 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789$"; 1614 const char* processIdentChars = "abcdefghijklmnopqrstuvwxyz" 1615 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:"; 1616 const char* authoritiesIdentChars = "abcdefghijklmnopqrstuvwxyz" 1617 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-:;"; 1618 const char* typeIdentChars = "abcdefghijklmnopqrstuvwxyz" 1619 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:-/*+"; 1620 const char* schemeIdentChars = "abcdefghijklmnopqrstuvwxyz" 1621 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-"; 1622 ResXMLTree::event_code_t code; 1623 sp<AaptSymbols> permissionSymbols; 1624 sp<AaptSymbols> permissionGroupSymbols; 1625 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 1626 && code > ResXMLTree::BAD_DOCUMENT) { 1627 if (code == ResXMLTree::START_TAG) { 1628 size_t len; 1629 if (block.getElementNamespace(&len) != NULL) { 1630 continue; 1631 } 1632 if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) { 1633 if (validateAttr(manifestPath, finalResTable, block, NULL, "package", 1634 packageIdentChars, true) != ATTR_OKAY) { 1635 hasErrors = true; 1636 } 1637 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1638 "sharedUserId", packageIdentChars, false) != ATTR_OKAY) { 1639 hasErrors = true; 1640 } 1641 } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0 1642 || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) { 1643 const bool isGroup = strcmp16(block.getElementName(&len), 1644 permission_group16.string()) == 0; 1645 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1646 "name", isGroup ? packageIdentCharsWithTheStupid 1647 : packageIdentChars, true) != ATTR_OKAY) { 1648 hasErrors = true; 1649 } 1650 SourcePos srcPos(manifestPath, block.getLineNumber()); 1651 sp<AaptSymbols> syms; 1652 if (!isGroup) { 1653 syms = permissionSymbols; 1654 if (syms == NULL) { 1655 sp<AaptSymbols> symbols = 1656 assets->getSymbolsFor(String8("Manifest")); 1657 syms = permissionSymbols = symbols->addNestedSymbol( 1658 String8("permission"), srcPos); 1659 } 1660 } else { 1661 syms = permissionGroupSymbols; 1662 if (syms == NULL) { 1663 sp<AaptSymbols> symbols = 1664 assets->getSymbolsFor(String8("Manifest")); 1665 syms = permissionGroupSymbols = symbols->addNestedSymbol( 1666 String8("permission_group"), srcPos); 1667 } 1668 } 1669 size_t len; 1670 ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name"); 1671 const uint16_t* id = block.getAttributeStringValue(index, &len); 1672 if (id == NULL) { 1673 fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n", 1674 manifestPath.string(), block.getLineNumber(), 1675 String8(block.getElementName(&len)).string()); 1676 hasErrors = true; 1677 break; 1678 } 1679 String8 idStr(id); 1680 char* p = idStr.lockBuffer(idStr.size()); 1681 char* e = p + idStr.size(); 1682 bool begins_with_digit = true; // init to true so an empty string fails 1683 while (e > p) { 1684 e--; 1685 if (*e >= '0' && *e <= '9') { 1686 begins_with_digit = true; 1687 continue; 1688 } 1689 if ((*e >= 'a' && *e <= 'z') || 1690 (*e >= 'A' && *e <= 'Z') || 1691 (*e == '_')) { 1692 begins_with_digit = false; 1693 continue; 1694 } 1695 if (isGroup && (*e == '-')) { 1696 *e = '_'; 1697 begins_with_digit = false; 1698 continue; 1699 } 1700 e++; 1701 break; 1702 } 1703 idStr.unlockBuffer(); 1704 // verify that we stopped because we hit a period or 1705 // the beginning of the string, and that the 1706 // identifier didn't begin with a digit. 1707 if (begins_with_digit || (e != p && *(e-1) != '.')) { 1708 fprintf(stderr, 1709 "%s:%d: Permission name <%s> is not a valid Java symbol\n", 1710 manifestPath.string(), block.getLineNumber(), idStr.string()); 1711 hasErrors = true; 1712 } 1713 syms->addStringSymbol(String8(e), idStr, srcPos); 1714 const uint16_t* cmt = block.getComment(&len); 1715 if (cmt != NULL && *cmt != 0) { 1716 //printf("Comment of %s: %s\n", String8(e).string(), 1717 // String8(cmt).string()); 1718 syms->appendComment(String8(e), String16(cmt), srcPos); 1719 } else { 1720 //printf("No comment for %s\n", String8(e).string()); 1721 } 1722 syms->makeSymbolPublic(String8(e), srcPos); 1723 } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) { 1724 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1725 "name", packageIdentChars, true) != ATTR_OKAY) { 1726 hasErrors = true; 1727 } 1728 } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) { 1729 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1730 "name", classIdentChars, true) != ATTR_OKAY) { 1731 hasErrors = true; 1732 } 1733 if (validateAttr(manifestPath, finalResTable, block, 1734 RESOURCES_ANDROID_NAMESPACE, "targetPackage", 1735 packageIdentChars, true) != ATTR_OKAY) { 1736 hasErrors = true; 1737 } 1738 } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) { 1739 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1740 "name", classIdentChars, false) != ATTR_OKAY) { 1741 hasErrors = true; 1742 } 1743 if (validateAttr(manifestPath, finalResTable, block, 1744 RESOURCES_ANDROID_NAMESPACE, "permission", 1745 packageIdentChars, false) != ATTR_OKAY) { 1746 hasErrors = true; 1747 } 1748 if (validateAttr(manifestPath, finalResTable, block, 1749 RESOURCES_ANDROID_NAMESPACE, "process", 1750 processIdentChars, false) != ATTR_OKAY) { 1751 hasErrors = true; 1752 } 1753 if (validateAttr(manifestPath, finalResTable, block, 1754 RESOURCES_ANDROID_NAMESPACE, "taskAffinity", 1755 processIdentChars, false) != ATTR_OKAY) { 1756 hasErrors = true; 1757 } 1758 } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) { 1759 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1760 "name", classIdentChars, true) != ATTR_OKAY) { 1761 hasErrors = true; 1762 } 1763 if (validateAttr(manifestPath, finalResTable, block, 1764 RESOURCES_ANDROID_NAMESPACE, "authorities", 1765 authoritiesIdentChars, true) != ATTR_OKAY) { 1766 hasErrors = true; 1767 } 1768 if (validateAttr(manifestPath, finalResTable, block, 1769 RESOURCES_ANDROID_NAMESPACE, "permission", 1770 packageIdentChars, false) != ATTR_OKAY) { 1771 hasErrors = true; 1772 } 1773 if (validateAttr(manifestPath, finalResTable, block, 1774 RESOURCES_ANDROID_NAMESPACE, "process", 1775 processIdentChars, false) != ATTR_OKAY) { 1776 hasErrors = true; 1777 } 1778 } else if (strcmp16(block.getElementName(&len), service16.string()) == 0 1779 || strcmp16(block.getElementName(&len), receiver16.string()) == 0 1780 || strcmp16(block.getElementName(&len), activity16.string()) == 0) { 1781 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1782 "name", classIdentChars, true) != ATTR_OKAY) { 1783 hasErrors = true; 1784 } 1785 if (validateAttr(manifestPath, finalResTable, block, 1786 RESOURCES_ANDROID_NAMESPACE, "permission", 1787 packageIdentChars, false) != ATTR_OKAY) { 1788 hasErrors = true; 1789 } 1790 if (validateAttr(manifestPath, finalResTable, block, 1791 RESOURCES_ANDROID_NAMESPACE, "process", 1792 processIdentChars, false) != ATTR_OKAY) { 1793 hasErrors = true; 1794 } 1795 if (validateAttr(manifestPath, finalResTable, block, 1796 RESOURCES_ANDROID_NAMESPACE, "taskAffinity", 1797 processIdentChars, false) != ATTR_OKAY) { 1798 hasErrors = true; 1799 } 1800 } else if (strcmp16(block.getElementName(&len), action16.string()) == 0 1801 || strcmp16(block.getElementName(&len), category16.string()) == 0) { 1802 if (validateAttr(manifestPath, finalResTable, block, 1803 RESOURCES_ANDROID_NAMESPACE, "name", 1804 packageIdentChars, true) != ATTR_OKAY) { 1805 hasErrors = true; 1806 } 1807 } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) { 1808 if (validateAttr(manifestPath, finalResTable, block, 1809 RESOURCES_ANDROID_NAMESPACE, "mimeType", 1810 typeIdentChars, true) != ATTR_OKAY) { 1811 hasErrors = true; 1812 } 1813 if (validateAttr(manifestPath, finalResTable, block, 1814 RESOURCES_ANDROID_NAMESPACE, "scheme", 1815 schemeIdentChars, true) != ATTR_OKAY) { 1816 hasErrors = true; 1817 } 1818 } else if (strcmp16(block.getElementName(&len), feature_group16.string()) == 0) { 1819 int depth = 1; 1820 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 1821 && code > ResXMLTree::BAD_DOCUMENT) { 1822 if (code == ResXMLTree::START_TAG) { 1823 depth++; 1824 if (strcmp16(block.getElementName(&len), uses_feature16.string()) == 0) { 1825 ssize_t idx = block.indexOfAttribute( 1826 RESOURCES_ANDROID_NAMESPACE, "required"); 1827 if (idx < 0) { 1828 continue; 1829 } 1830 1831 int32_t data = block.getAttributeData(idx); 1832 if (data == 0) { 1833 fprintf(stderr, "%s:%d: Tag <uses-feature> can not have " 1834 "android:required=\"false\" when inside a " 1835 "<feature-group> tag.\n", 1836 manifestPath.string(), block.getLineNumber()); 1837 hasErrors = true; 1838 } 1839 } 1840 } else if (code == ResXMLTree::END_TAG) { 1841 depth--; 1842 if (depth == 0) { 1843 break; 1844 } 1845 } 1846 } 1847 } 1848 } 1849 } 1850 1851 if (hasErrors) { 1852 return UNKNOWN_ERROR; 1853 } 1854 1855 if (resFile != NULL) { 1856 // These resources are now considered to be a part of the included 1857 // resources, for others to reference. 1858 err = assets->addIncludedResources(resFile); 1859 if (err < NO_ERROR) { 1860 fprintf(stderr, "ERROR: Unable to parse generated resources, aborting.\n"); 1861 return err; 1862 } 1863 } 1864 1865 return err; 1866} 1867 1868static const char* getIndentSpace(int indent) 1869{ 1870static const char whitespace[] = 1871" "; 1872 1873 return whitespace + sizeof(whitespace) - 1 - indent*4; 1874} 1875 1876static String8 flattenSymbol(const String8& symbol) { 1877 String8 result(symbol); 1878 ssize_t first; 1879 if ((first = symbol.find(":", 0)) >= 0 1880 || (first = symbol.find(".", 0)) >= 0) { 1881 size_t size = symbol.size(); 1882 char* buf = result.lockBuffer(size); 1883 for (size_t i = first; i < size; i++) { 1884 if (buf[i] == ':' || buf[i] == '.') { 1885 buf[i] = '_'; 1886 } 1887 } 1888 result.unlockBuffer(size); 1889 } 1890 return result; 1891} 1892 1893static String8 getSymbolPackage(const String8& symbol, const sp<AaptAssets>& assets, bool pub) { 1894 ssize_t colon = symbol.find(":", 0); 1895 if (colon >= 0) { 1896 return String8(symbol.string(), colon); 1897 } 1898 return pub ? assets->getPackage() : assets->getSymbolsPrivatePackage(); 1899} 1900 1901static String8 getSymbolName(const String8& symbol) { 1902 ssize_t colon = symbol.find(":", 0); 1903 if (colon >= 0) { 1904 return String8(symbol.string() + colon + 1); 1905 } 1906 return symbol; 1907} 1908 1909static String16 getAttributeComment(const sp<AaptAssets>& assets, 1910 const String8& name, 1911 String16* outTypeComment = NULL) 1912{ 1913 sp<AaptSymbols> asym = assets->getSymbolsFor(String8("R")); 1914 if (asym != NULL) { 1915 //printf("Got R symbols!\n"); 1916 asym = asym->getNestedSymbols().valueFor(String8("attr")); 1917 if (asym != NULL) { 1918 //printf("Got attrs symbols! comment %s=%s\n", 1919 // name.string(), String8(asym->getComment(name)).string()); 1920 if (outTypeComment != NULL) { 1921 *outTypeComment = asym->getTypeComment(name); 1922 } 1923 return asym->getComment(name); 1924 } 1925 } 1926 return String16(); 1927} 1928 1929static void writeResourceLoadedCallback(FILE* fp, int indent) { 1930 IndentPrinter p(fp, 4); 1931 p.indent(indent); 1932 p.println("private static void rewriteIntArrayField(java.lang.reflect.Field field, int packageId) throws IllegalAccessException {"); 1933 { 1934 p.indent(); 1935 p.println("int requiredModifiers = java.lang.reflect.Modifier.STATIC | java.lang.reflect.Modifier.PUBLIC;"); 1936 p.println("if ((field.getModifiers() & requiredModifiers) != requiredModifiers) {"); 1937 { 1938 p.indent(); 1939 p.println("throw new IllegalArgumentException(\"Field \" + field.getName() + \" is not rewritable\");"); 1940 p.indent(-1); 1941 } 1942 p.println("}"); 1943 p.println("if (field.getType() != int[].class) {"); 1944 { 1945 p.indent(); 1946 p.println("throw new IllegalArgumentException(\"Field \" + field.getName() + \" is not an int array\");"); 1947 p.indent(-1); 1948 } 1949 p.println("}"); 1950 p.println("int[] array = (int[]) field.get(null);"); 1951 p.println("for (int i = 0; i < array.length; i++) {"); 1952 { 1953 p.indent(); 1954 p.println("array[i] = (array[i] & 0x00ffffff) | (packageId << 24);"); 1955 p.indent(-1); 1956 } 1957 p.println("}"); 1958 p.indent(-1); 1959 } 1960 p.println("}"); 1961 p.println(); 1962 p.println("private static void rewriteIntField(java.lang.reflect.Field field, int packageId) throws IllegalAccessException {"); 1963 { 1964 p.indent(); 1965 p.println("int requiredModifiers = java.lang.reflect.Modifier.STATIC | java.lang.reflect.Modifier.PUBLIC;"); 1966 p.println("int bannedModifiers = java.lang.reflect.Modifier.FINAL;"); 1967 p.println("int mod = field.getModifiers();"); 1968 p.println("if ((mod & requiredModifiers) != requiredModifiers || (mod & bannedModifiers) != 0) {"); 1969 { 1970 p.indent(); 1971 p.println("throw new IllegalArgumentException(\"Field \" + field.getName() + \" is not rewritable\");"); 1972 p.indent(-1); 1973 } 1974 p.println("}"); 1975 p.println("if (field.getType() != int.class && field.getType() != Integer.class) {"); 1976 { 1977 p.indent(); 1978 p.println("throw new IllegalArgumentException(\"Field \" + field.getName() + \" is not an int\");"); 1979 p.indent(-1); 1980 } 1981 p.println("}"); 1982 p.println("int resId = field.getInt(null);"); 1983 p.println("field.setInt(null, (resId & 0x00ffffff) | (packageId << 24));"); 1984 p.indent(-1); 1985 } 1986 p.println("}"); 1987 p.println(); 1988 p.println("public static void onResourcesLoaded(int assignedPackageId) throws Exception {"); 1989 { 1990 p.indent(); 1991 p.println("Class<?>[] declaredClasses = R.class.getDeclaredClasses();"); 1992 p.println("for (Class<?> clazz : declaredClasses) {"); 1993 { 1994 p.indent(); 1995 p.println("if (clazz.getSimpleName().equals(\"styleable\")) {"); 1996 { 1997 p.indent(); 1998 p.println("for (java.lang.reflect.Field field : clazz.getDeclaredFields()) {"); 1999 { 2000 p.indent(); 2001 p.println("if (field.getType() == int[].class) {"); 2002 { 2003 p.indent(); 2004 p.println("rewriteIntArrayField(field, assignedPackageId);"); 2005 p.indent(-1); 2006 } 2007 p.println("}"); 2008 p.indent(-1); 2009 } 2010 p.println("}"); 2011 p.indent(-1); 2012 } 2013 p.println("} else {"); 2014 { 2015 p.indent(); 2016 p.println("for (java.lang.reflect.Field field : clazz.getDeclaredFields()) {"); 2017 { 2018 p.indent(); 2019 p.println("rewriteIntField(field, assignedPackageId);"); 2020 p.indent(-1); 2021 } 2022 p.println("}"); 2023 p.indent(-1); 2024 } 2025 p.println("}"); 2026 p.indent(-1); 2027 } 2028 p.println("}"); 2029 p.indent(-1); 2030 } 2031 p.println("}"); 2032 p.println(); 2033} 2034 2035static status_t writeLayoutClasses( 2036 FILE* fp, const sp<AaptAssets>& assets, 2037 const sp<AaptSymbols>& symbols, int indent, bool includePrivate, bool nonConstantId) 2038{ 2039 const char* indentStr = getIndentSpace(indent); 2040 if (!includePrivate) { 2041 fprintf(fp, "%s/** @doconly */\n", indentStr); 2042 } 2043 fprintf(fp, "%spublic static final class styleable {\n", indentStr); 2044 indent++; 2045 2046 String16 attr16("attr"); 2047 String16 package16(assets->getPackage()); 2048 2049 indentStr = getIndentSpace(indent); 2050 bool hasErrors = false; 2051 2052 size_t i; 2053 size_t N = symbols->getNestedSymbols().size(); 2054 for (i=0; i<N; i++) { 2055 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2056 String8 realClassName(symbols->getNestedSymbols().keyAt(i)); 2057 String8 nclassName(flattenSymbol(realClassName)); 2058 2059 SortedVector<uint32_t> idents; 2060 Vector<uint32_t> origOrder; 2061 Vector<bool> publicFlags; 2062 2063 size_t a; 2064 size_t NA = nsymbols->getSymbols().size(); 2065 for (a=0; a<NA; a++) { 2066 const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a)); 2067 int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32 2068 ? sym.int32Val : 0; 2069 bool isPublic = true; 2070 if (code == 0) { 2071 String16 name16(sym.name); 2072 uint32_t typeSpecFlags; 2073 code = assets->getIncludedResources().identifierForName( 2074 name16.string(), name16.size(), 2075 attr16.string(), attr16.size(), 2076 package16.string(), package16.size(), &typeSpecFlags); 2077 if (code == 0) { 2078 fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n", 2079 nclassName.string(), sym.name.string()); 2080 hasErrors = true; 2081 } 2082 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 2083 } 2084 idents.add(code); 2085 origOrder.add(code); 2086 publicFlags.add(isPublic); 2087 } 2088 2089 NA = idents.size(); 2090 2091 String16 comment = symbols->getComment(realClassName); 2092 AnnotationProcessor ann; 2093 fprintf(fp, "%s/** ", indentStr); 2094 if (comment.size() > 0) { 2095 String8 cmt(comment); 2096 ann.preprocessComment(cmt); 2097 fprintf(fp, "%s\n", cmt.string()); 2098 } else { 2099 fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string()); 2100 } 2101 bool hasTable = false; 2102 for (a=0; a<NA; a++) { 2103 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 2104 if (pos >= 0) { 2105 if (!hasTable) { 2106 hasTable = true; 2107 fprintf(fp, 2108 "%s <p>Includes the following attributes:</p>\n" 2109 "%s <table>\n" 2110 "%s <colgroup align=\"left\" />\n" 2111 "%s <colgroup align=\"left\" />\n" 2112 "%s <tr><th>Attribute</th><th>Description</th></tr>\n", 2113 indentStr, 2114 indentStr, 2115 indentStr, 2116 indentStr, 2117 indentStr); 2118 } 2119 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 2120 if (!publicFlags.itemAt(a) && !includePrivate) { 2121 continue; 2122 } 2123 String8 name8(sym.name); 2124 String16 comment(sym.comment); 2125 if (comment.size() <= 0) { 2126 comment = getAttributeComment(assets, name8); 2127 } 2128 if (comment.size() > 0) { 2129 const char16_t* p = comment.string(); 2130 while (*p != 0 && *p != '.') { 2131 if (*p == '{') { 2132 while (*p != 0 && *p != '}') { 2133 p++; 2134 } 2135 } else { 2136 p++; 2137 } 2138 } 2139 if (*p == '.') { 2140 p++; 2141 } 2142 comment = String16(comment.string(), p-comment.string()); 2143 } 2144 fprintf(fp, "%s <tr><td><code>{@link #%s_%s %s:%s}</code></td><td>%s</td></tr>\n", 2145 indentStr, nclassName.string(), 2146 flattenSymbol(name8).string(), 2147 getSymbolPackage(name8, assets, true).string(), 2148 getSymbolName(name8).string(), 2149 String8(comment).string()); 2150 } 2151 } 2152 if (hasTable) { 2153 fprintf(fp, "%s </table>\n", indentStr); 2154 } 2155 for (a=0; a<NA; a++) { 2156 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 2157 if (pos >= 0) { 2158 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 2159 if (!publicFlags.itemAt(a) && !includePrivate) { 2160 continue; 2161 } 2162 fprintf(fp, "%s @see #%s_%s\n", 2163 indentStr, nclassName.string(), 2164 flattenSymbol(sym.name).string()); 2165 } 2166 } 2167 fprintf(fp, "%s */\n", getIndentSpace(indent)); 2168 2169 ann.printAnnotations(fp, indentStr); 2170 2171 fprintf(fp, 2172 "%spublic static final int[] %s = {\n" 2173 "%s", 2174 indentStr, nclassName.string(), 2175 getIndentSpace(indent+1)); 2176 2177 for (a=0; a<NA; a++) { 2178 if (a != 0) { 2179 if ((a&3) == 0) { 2180 fprintf(fp, ",\n%s", getIndentSpace(indent+1)); 2181 } else { 2182 fprintf(fp, ", "); 2183 } 2184 } 2185 fprintf(fp, "0x%08x", idents[a]); 2186 } 2187 2188 fprintf(fp, "\n%s};\n", indentStr); 2189 2190 for (a=0; a<NA; a++) { 2191 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 2192 if (pos >= 0) { 2193 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 2194 if (!publicFlags.itemAt(a) && !includePrivate) { 2195 continue; 2196 } 2197 String8 name8(sym.name); 2198 String16 comment(sym.comment); 2199 String16 typeComment; 2200 if (comment.size() <= 0) { 2201 comment = getAttributeComment(assets, name8, &typeComment); 2202 } else { 2203 getAttributeComment(assets, name8, &typeComment); 2204 } 2205 2206 uint32_t typeSpecFlags = 0; 2207 String16 name16(sym.name); 2208 assets->getIncludedResources().identifierForName( 2209 name16.string(), name16.size(), 2210 attr16.string(), attr16.size(), 2211 package16.string(), package16.size(), &typeSpecFlags); 2212 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(), 2213 // String8(attr16).string(), String8(name16).string(), typeSpecFlags); 2214 const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 2215 2216 AnnotationProcessor ann; 2217 fprintf(fp, "%s/**\n", indentStr); 2218 if (comment.size() > 0) { 2219 String8 cmt(comment); 2220 ann.preprocessComment(cmt); 2221 fprintf(fp, "%s <p>\n%s @attr description\n", indentStr, indentStr); 2222 fprintf(fp, "%s %s\n", indentStr, cmt.string()); 2223 } else { 2224 fprintf(fp, 2225 "%s <p>This symbol is the offset where the {@link %s.R.attr#%s}\n" 2226 "%s attribute's value can be found in the {@link #%s} array.\n", 2227 indentStr, 2228 getSymbolPackage(name8, assets, pub).string(), 2229 getSymbolName(name8).string(), 2230 indentStr, nclassName.string()); 2231 } 2232 if (typeComment.size() > 0) { 2233 String8 cmt(typeComment); 2234 ann.preprocessComment(cmt); 2235 fprintf(fp, "\n\n%s %s\n", indentStr, cmt.string()); 2236 } 2237 if (comment.size() > 0) { 2238 if (pub) { 2239 fprintf(fp, 2240 "%s <p>This corresponds to the global attribute\n" 2241 "%s resource symbol {@link %s.R.attr#%s}.\n", 2242 indentStr, indentStr, 2243 getSymbolPackage(name8, assets, true).string(), 2244 getSymbolName(name8).string()); 2245 } else { 2246 fprintf(fp, 2247 "%s <p>This is a private symbol.\n", indentStr); 2248 } 2249 } 2250 fprintf(fp, "%s @attr name %s:%s\n", indentStr, 2251 getSymbolPackage(name8, assets, pub).string(), 2252 getSymbolName(name8).string()); 2253 fprintf(fp, "%s*/\n", indentStr); 2254 ann.printAnnotations(fp, indentStr); 2255 2256 const char * id_format = nonConstantId ? 2257 "%spublic static int %s_%s = %d;\n" : 2258 "%spublic static final int %s_%s = %d;\n"; 2259 2260 fprintf(fp, 2261 id_format, 2262 indentStr, nclassName.string(), 2263 flattenSymbol(name8).string(), (int)pos); 2264 } 2265 } 2266 } 2267 2268 indent--; 2269 fprintf(fp, "%s};\n", getIndentSpace(indent)); 2270 return hasErrors ? UNKNOWN_ERROR : NO_ERROR; 2271} 2272 2273static status_t writeTextLayoutClasses( 2274 FILE* fp, const sp<AaptAssets>& assets, 2275 const sp<AaptSymbols>& symbols, bool includePrivate) 2276{ 2277 String16 attr16("attr"); 2278 String16 package16(assets->getPackage()); 2279 2280 bool hasErrors = false; 2281 2282 size_t i; 2283 size_t N = symbols->getNestedSymbols().size(); 2284 for (i=0; i<N; i++) { 2285 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2286 String8 realClassName(symbols->getNestedSymbols().keyAt(i)); 2287 String8 nclassName(flattenSymbol(realClassName)); 2288 2289 SortedVector<uint32_t> idents; 2290 Vector<uint32_t> origOrder; 2291 Vector<bool> publicFlags; 2292 2293 size_t a; 2294 size_t NA = nsymbols->getSymbols().size(); 2295 for (a=0; a<NA; a++) { 2296 const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a)); 2297 int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32 2298 ? sym.int32Val : 0; 2299 bool isPublic = true; 2300 if (code == 0) { 2301 String16 name16(sym.name); 2302 uint32_t typeSpecFlags; 2303 code = assets->getIncludedResources().identifierForName( 2304 name16.string(), name16.size(), 2305 attr16.string(), attr16.size(), 2306 package16.string(), package16.size(), &typeSpecFlags); 2307 if (code == 0) { 2308 fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n", 2309 nclassName.string(), sym.name.string()); 2310 hasErrors = true; 2311 } 2312 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 2313 } 2314 idents.add(code); 2315 origOrder.add(code); 2316 publicFlags.add(isPublic); 2317 } 2318 2319 NA = idents.size(); 2320 2321 fprintf(fp, "int[] styleable %s {", nclassName.string()); 2322 2323 for (a=0; a<NA; a++) { 2324 if (a != 0) { 2325 fprintf(fp, ","); 2326 } 2327 fprintf(fp, " 0x%08x", idents[a]); 2328 } 2329 2330 fprintf(fp, " }\n"); 2331 2332 for (a=0; a<NA; a++) { 2333 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 2334 if (pos >= 0) { 2335 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 2336 if (!publicFlags.itemAt(a) && !includePrivate) { 2337 continue; 2338 } 2339 String8 name8(sym.name); 2340 String16 comment(sym.comment); 2341 String16 typeComment; 2342 if (comment.size() <= 0) { 2343 comment = getAttributeComment(assets, name8, &typeComment); 2344 } else { 2345 getAttributeComment(assets, name8, &typeComment); 2346 } 2347 2348 uint32_t typeSpecFlags = 0; 2349 String16 name16(sym.name); 2350 assets->getIncludedResources().identifierForName( 2351 name16.string(), name16.size(), 2352 attr16.string(), attr16.size(), 2353 package16.string(), package16.size(), &typeSpecFlags); 2354 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(), 2355 // String8(attr16).string(), String8(name16).string(), typeSpecFlags); 2356 const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 2357 2358 fprintf(fp, 2359 "int styleable %s_%s %d\n", 2360 nclassName.string(), 2361 flattenSymbol(name8).string(), (int)pos); 2362 } 2363 } 2364 } 2365 2366 return hasErrors ? UNKNOWN_ERROR : NO_ERROR; 2367} 2368 2369static status_t writeSymbolClass( 2370 FILE* fp, const sp<AaptAssets>& assets, bool includePrivate, 2371 const sp<AaptSymbols>& symbols, const String8& className, int indent, 2372 bool nonConstantId, bool emitCallback) 2373{ 2374 fprintf(fp, "%spublic %sfinal class %s {\n", 2375 getIndentSpace(indent), 2376 indent != 0 ? "static " : "", className.string()); 2377 indent++; 2378 2379 size_t i; 2380 status_t err = NO_ERROR; 2381 2382 const char * id_format = nonConstantId ? 2383 "%spublic static int %s=0x%08x;\n" : 2384 "%spublic static final int %s=0x%08x;\n"; 2385 2386 size_t N = symbols->getSymbols().size(); 2387 for (i=0; i<N; i++) { 2388 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 2389 if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) { 2390 continue; 2391 } 2392 if (!assets->isJavaSymbol(sym, includePrivate)) { 2393 continue; 2394 } 2395 String8 name8(sym.name); 2396 String16 comment(sym.comment); 2397 bool haveComment = false; 2398 AnnotationProcessor ann; 2399 if (comment.size() > 0) { 2400 haveComment = true; 2401 String8 cmt(comment); 2402 ann.preprocessComment(cmt); 2403 fprintf(fp, 2404 "%s/** %s\n", 2405 getIndentSpace(indent), cmt.string()); 2406 } else if (sym.isPublic && !includePrivate) { 2407 sym.sourcePos.warning("No comment for public symbol %s:%s/%s", 2408 assets->getPackage().string(), className.string(), 2409 String8(sym.name).string()); 2410 } 2411 String16 typeComment(sym.typeComment); 2412 if (typeComment.size() > 0) { 2413 String8 cmt(typeComment); 2414 ann.preprocessComment(cmt); 2415 if (!haveComment) { 2416 haveComment = true; 2417 fprintf(fp, 2418 "%s/** %s\n", getIndentSpace(indent), cmt.string()); 2419 } else { 2420 fprintf(fp, 2421 "%s %s\n", getIndentSpace(indent), cmt.string()); 2422 } 2423 } 2424 if (haveComment) { 2425 fprintf(fp,"%s */\n", getIndentSpace(indent)); 2426 } 2427 ann.printAnnotations(fp, getIndentSpace(indent)); 2428 fprintf(fp, id_format, 2429 getIndentSpace(indent), 2430 flattenSymbol(name8).string(), (int)sym.int32Val); 2431 } 2432 2433 for (i=0; i<N; i++) { 2434 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 2435 if (sym.typeCode != AaptSymbolEntry::TYPE_STRING) { 2436 continue; 2437 } 2438 if (!assets->isJavaSymbol(sym, includePrivate)) { 2439 continue; 2440 } 2441 String8 name8(sym.name); 2442 String16 comment(sym.comment); 2443 AnnotationProcessor ann; 2444 if (comment.size() > 0) { 2445 String8 cmt(comment); 2446 ann.preprocessComment(cmt); 2447 fprintf(fp, 2448 "%s/** %s\n" 2449 "%s */\n", 2450 getIndentSpace(indent), cmt.string(), 2451 getIndentSpace(indent)); 2452 } else if (sym.isPublic && !includePrivate) { 2453 sym.sourcePos.warning("No comment for public symbol %s:%s/%s", 2454 assets->getPackage().string(), className.string(), 2455 String8(sym.name).string()); 2456 } 2457 ann.printAnnotations(fp, getIndentSpace(indent)); 2458 fprintf(fp, "%spublic static final String %s=\"%s\";\n", 2459 getIndentSpace(indent), 2460 flattenSymbol(name8).string(), sym.stringVal.string()); 2461 } 2462 2463 sp<AaptSymbols> styleableSymbols; 2464 2465 N = symbols->getNestedSymbols().size(); 2466 for (i=0; i<N; i++) { 2467 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2468 String8 nclassName(symbols->getNestedSymbols().keyAt(i)); 2469 if (nclassName == "styleable") { 2470 styleableSymbols = nsymbols; 2471 } else { 2472 err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, 2473 indent, nonConstantId, false); 2474 } 2475 if (err != NO_ERROR) { 2476 return err; 2477 } 2478 } 2479 2480 if (styleableSymbols != NULL) { 2481 err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate, nonConstantId); 2482 if (err != NO_ERROR) { 2483 return err; 2484 } 2485 } 2486 2487 if (emitCallback) { 2488 writeResourceLoadedCallback(fp, indent); 2489 } 2490 2491 indent--; 2492 fprintf(fp, "%s}\n", getIndentSpace(indent)); 2493 return NO_ERROR; 2494} 2495 2496static status_t writeTextSymbolClass( 2497 FILE* fp, const sp<AaptAssets>& assets, bool includePrivate, 2498 const sp<AaptSymbols>& symbols, const String8& className) 2499{ 2500 size_t i; 2501 status_t err = NO_ERROR; 2502 2503 size_t N = symbols->getSymbols().size(); 2504 for (i=0; i<N; i++) { 2505 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 2506 if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) { 2507 continue; 2508 } 2509 2510 if (!assets->isJavaSymbol(sym, includePrivate)) { 2511 continue; 2512 } 2513 2514 String8 name8(sym.name); 2515 fprintf(fp, "int %s %s 0x%08x\n", 2516 className.string(), 2517 flattenSymbol(name8).string(), (int)sym.int32Val); 2518 } 2519 2520 N = symbols->getNestedSymbols().size(); 2521 for (i=0; i<N; i++) { 2522 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2523 String8 nclassName(symbols->getNestedSymbols().keyAt(i)); 2524 if (nclassName == "styleable") { 2525 err = writeTextLayoutClasses(fp, assets, nsymbols, includePrivate); 2526 } else { 2527 err = writeTextSymbolClass(fp, assets, includePrivate, nsymbols, nclassName); 2528 } 2529 if (err != NO_ERROR) { 2530 return err; 2531 } 2532 } 2533 2534 return NO_ERROR; 2535} 2536 2537status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets, 2538 const String8& package, bool includePrivate, bool emitCallback) 2539{ 2540 if (!bundle->getRClassDir()) { 2541 return NO_ERROR; 2542 } 2543 2544 const char* textSymbolsDest = bundle->getOutputTextSymbols(); 2545 2546 String8 R("R"); 2547 const size_t N = assets->getSymbols().size(); 2548 for (size_t i=0; i<N; i++) { 2549 sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i); 2550 String8 className(assets->getSymbols().keyAt(i)); 2551 String8 dest(bundle->getRClassDir()); 2552 2553 if (bundle->getMakePackageDirs()) { 2554 String8 pkg(package); 2555 const char* last = pkg.string(); 2556 const char* s = last-1; 2557 do { 2558 s++; 2559 if (s > last && (*s == '.' || *s == 0)) { 2560 String8 part(last, s-last); 2561 dest.appendPath(part); 2562#ifdef HAVE_MS_C_RUNTIME 2563 _mkdir(dest.string()); 2564#else 2565 mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP); 2566#endif 2567 last = s+1; 2568 } 2569 } while (*s); 2570 } 2571 dest.appendPath(className); 2572 dest.append(".java"); 2573 FILE* fp = fopen(dest.string(), "w+"); 2574 if (fp == NULL) { 2575 fprintf(stderr, "ERROR: Unable to open class file %s: %s\n", 2576 dest.string(), strerror(errno)); 2577 return UNKNOWN_ERROR; 2578 } 2579 if (bundle->getVerbose()) { 2580 printf(" Writing symbols for class %s.\n", className.string()); 2581 } 2582 2583 fprintf(fp, 2584 "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n" 2585 " *\n" 2586 " * This class was automatically generated by the\n" 2587 " * aapt tool from the resource data it found. It\n" 2588 " * should not be modified by hand.\n" 2589 " */\n" 2590 "\n" 2591 "package %s;\n\n", package.string()); 2592 2593 status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, 2594 className, 0, bundle->getNonConstantId(), emitCallback); 2595 fclose(fp); 2596 if (err != NO_ERROR) { 2597 return err; 2598 } 2599 2600 if (textSymbolsDest != NULL && R == className) { 2601 String8 textDest(textSymbolsDest); 2602 textDest.appendPath(className); 2603 textDest.append(".txt"); 2604 2605 FILE* fp = fopen(textDest.string(), "w+"); 2606 if (fp == NULL) { 2607 fprintf(stderr, "ERROR: Unable to open text symbol file %s: %s\n", 2608 textDest.string(), strerror(errno)); 2609 return UNKNOWN_ERROR; 2610 } 2611 if (bundle->getVerbose()) { 2612 printf(" Writing text symbols for class %s.\n", className.string()); 2613 } 2614 2615 status_t err = writeTextSymbolClass(fp, assets, includePrivate, symbols, 2616 className); 2617 fclose(fp); 2618 if (err != NO_ERROR) { 2619 return err; 2620 } 2621 } 2622 2623 // If we were asked to generate a dependency file, we'll go ahead and add this R.java 2624 // as a target in the dependency file right next to it. 2625 if (bundle->getGenDependencies() && R == className) { 2626 // Add this R.java to the dependency file 2627 String8 dependencyFile(bundle->getRClassDir()); 2628 dependencyFile.appendPath("R.java.d"); 2629 2630 FILE *fp = fopen(dependencyFile.string(), "a"); 2631 fprintf(fp,"%s \\\n", dest.string()); 2632 fclose(fp); 2633 } 2634 } 2635 2636 return NO_ERROR; 2637} 2638 2639 2640class ProguardKeepSet 2641{ 2642public: 2643 // { rule --> { file locations } } 2644 KeyedVector<String8, SortedVector<String8> > rules; 2645 2646 void add(const String8& rule, const String8& where); 2647}; 2648 2649void ProguardKeepSet::add(const String8& rule, const String8& where) 2650{ 2651 ssize_t index = rules.indexOfKey(rule); 2652 if (index < 0) { 2653 index = rules.add(rule, SortedVector<String8>()); 2654 } 2655 rules.editValueAt(index).add(where); 2656} 2657 2658void 2659addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName, 2660 const char* pkg, const String8& srcName, int line) 2661{ 2662 String8 className(inClassName); 2663 if (pkg != NULL) { 2664 // asdf --> package.asdf 2665 // .asdf .a.b --> package.asdf package.a.b 2666 // asdf.adsf --> asdf.asdf 2667 const char* p = className.string(); 2668 const char* q = strchr(p, '.'); 2669 if (p == q) { 2670 className = pkg; 2671 className.append(inClassName); 2672 } else if (q == NULL) { 2673 className = pkg; 2674 className.append("."); 2675 className.append(inClassName); 2676 } 2677 } 2678 2679 String8 rule("-keep class "); 2680 rule += className; 2681 rule += " { <init>(...); }"; 2682 2683 String8 location("view "); 2684 location += srcName; 2685 char lineno[20]; 2686 sprintf(lineno, ":%d", line); 2687 location += lineno; 2688 2689 keep->add(rule, location); 2690} 2691 2692void 2693addProguardKeepMethodRule(ProguardKeepSet* keep, const String8& memberName, 2694 const char* pkg, const String8& srcName, int line) 2695{ 2696 String8 rule("-keepclassmembers class * { *** "); 2697 rule += memberName; 2698 rule += "(...); }"; 2699 2700 String8 location("onClick "); 2701 location += srcName; 2702 char lineno[20]; 2703 sprintf(lineno, ":%d", line); 2704 location += lineno; 2705 2706 keep->add(rule, location); 2707} 2708 2709status_t 2710writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets) 2711{ 2712 status_t err; 2713 ResXMLTree tree; 2714 size_t len; 2715 ResXMLTree::event_code_t code; 2716 int depth = 0; 2717 bool inApplication = false; 2718 String8 error; 2719 sp<AaptGroup> assGroup; 2720 sp<AaptFile> assFile; 2721 String8 pkg; 2722 2723 // First, look for a package file to parse. This is required to 2724 // be able to generate the resource information. 2725 assGroup = assets->getFiles().valueFor(String8("AndroidManifest.xml")); 2726 if (assGroup == NULL) { 2727 fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n"); 2728 return -1; 2729 } 2730 2731 if (assGroup->getFiles().size() != 1) { 2732 fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n", 2733 assGroup->getFiles().valueAt(0)->getPrintableSource().string()); 2734 } 2735 2736 assFile = assGroup->getFiles().valueAt(0); 2737 2738 err = parseXMLResource(assFile, &tree); 2739 if (err != NO_ERROR) { 2740 return err; 2741 } 2742 2743 tree.restart(); 2744 2745 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 2746 if (code == ResXMLTree::END_TAG) { 2747 if (/* name == "Application" && */ depth == 2) { 2748 inApplication = false; 2749 } 2750 depth--; 2751 continue; 2752 } 2753 if (code != ResXMLTree::START_TAG) { 2754 continue; 2755 } 2756 depth++; 2757 String8 tag(tree.getElementName(&len)); 2758 // printf("Depth %d tag %s\n", depth, tag.string()); 2759 bool keepTag = false; 2760 if (depth == 1) { 2761 if (tag != "manifest") { 2762 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 2763 return -1; 2764 } 2765 pkg = AaptXml::getAttribute(tree, NULL, "package"); 2766 } else if (depth == 2) { 2767 if (tag == "application") { 2768 inApplication = true; 2769 keepTag = true; 2770 2771 String8 agent = AaptXml::getAttribute(tree, 2772 "http://schemas.android.com/apk/res/android", 2773 "backupAgent", &error); 2774 if (agent.length() > 0) { 2775 addProguardKeepRule(keep, agent, pkg.string(), 2776 assFile->getPrintableSource(), tree.getLineNumber()); 2777 } 2778 } else if (tag == "instrumentation") { 2779 keepTag = true; 2780 } 2781 } 2782 if (!keepTag && inApplication && depth == 3) { 2783 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") { 2784 keepTag = true; 2785 } 2786 } 2787 if (keepTag) { 2788 String8 name = AaptXml::getAttribute(tree, 2789 "http://schemas.android.com/apk/res/android", "name", &error); 2790 if (error != "") { 2791 fprintf(stderr, "ERROR: %s\n", error.string()); 2792 return -1; 2793 } 2794 if (name.length() > 0) { 2795 addProguardKeepRule(keep, name, pkg.string(), 2796 assFile->getPrintableSource(), tree.getLineNumber()); 2797 } 2798 } 2799 } 2800 2801 return NO_ERROR; 2802} 2803 2804struct NamespaceAttributePair { 2805 const char* ns; 2806 const char* attr; 2807 2808 NamespaceAttributePair(const char* n, const char* a) : ns(n), attr(a) {} 2809 NamespaceAttributePair() : ns(NULL), attr(NULL) {} 2810}; 2811 2812status_t 2813writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile, 2814 const Vector<String8>& startTags, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs) 2815{ 2816 status_t err; 2817 ResXMLTree tree; 2818 size_t len; 2819 ResXMLTree::event_code_t code; 2820 2821 err = parseXMLResource(layoutFile, &tree); 2822 if (err != NO_ERROR) { 2823 return err; 2824 } 2825 2826 tree.restart(); 2827 2828 if (!startTags.isEmpty()) { 2829 bool haveStart = false; 2830 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 2831 if (code != ResXMLTree::START_TAG) { 2832 continue; 2833 } 2834 String8 tag(tree.getElementName(&len)); 2835 const size_t numStartTags = startTags.size(); 2836 for (size_t i = 0; i < numStartTags; i++) { 2837 if (tag == startTags[i]) { 2838 haveStart = true; 2839 } 2840 } 2841 break; 2842 } 2843 if (!haveStart) { 2844 return NO_ERROR; 2845 } 2846 } 2847 2848 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 2849 if (code != ResXMLTree::START_TAG) { 2850 continue; 2851 } 2852 String8 tag(tree.getElementName(&len)); 2853 2854 // If there is no '.', we'll assume that it's one of the built in names. 2855 if (strchr(tag.string(), '.')) { 2856 addProguardKeepRule(keep, tag, NULL, 2857 layoutFile->getPrintableSource(), tree.getLineNumber()); 2858 } else if (tagAttrPairs != NULL) { 2859 ssize_t tagIndex = tagAttrPairs->indexOfKey(tag); 2860 if (tagIndex >= 0) { 2861 const Vector<NamespaceAttributePair>& nsAttrVector = tagAttrPairs->valueAt(tagIndex); 2862 for (size_t i = 0; i < nsAttrVector.size(); i++) { 2863 const NamespaceAttributePair& nsAttr = nsAttrVector[i]; 2864 2865 ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr); 2866 if (attrIndex < 0) { 2867 // fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n", 2868 // layoutFile->getPrintableSource().string(), tree.getLineNumber(), 2869 // tag.string(), nsAttr.ns, nsAttr.attr); 2870 } else { 2871 size_t len; 2872 addProguardKeepRule(keep, 2873 String8(tree.getAttributeStringValue(attrIndex, &len)), NULL, 2874 layoutFile->getPrintableSource(), tree.getLineNumber()); 2875 } 2876 } 2877 } 2878 } 2879 ssize_t attrIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "onClick"); 2880 if (attrIndex >= 0) { 2881 size_t len; 2882 addProguardKeepMethodRule(keep, 2883 String8(tree.getAttributeStringValue(attrIndex, &len)), NULL, 2884 layoutFile->getPrintableSource(), tree.getLineNumber()); 2885 } 2886 } 2887 2888 return NO_ERROR; 2889} 2890 2891static void addTagAttrPair(KeyedVector<String8, Vector<NamespaceAttributePair> >* dest, 2892 const char* tag, const char* ns, const char* attr) { 2893 String8 tagStr(tag); 2894 ssize_t index = dest->indexOfKey(tagStr); 2895 2896 if (index < 0) { 2897 Vector<NamespaceAttributePair> vector; 2898 vector.add(NamespaceAttributePair(ns, attr)); 2899 dest->add(tagStr, vector); 2900 } else { 2901 dest->editValueAt(index).add(NamespaceAttributePair(ns, attr)); 2902 } 2903} 2904 2905status_t 2906writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets) 2907{ 2908 status_t err; 2909 2910 // tag:attribute pairs that should be checked in layout files. 2911 KeyedVector<String8, Vector<NamespaceAttributePair> > kLayoutTagAttrPairs; 2912 addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class"); 2913 addTagAttrPair(&kLayoutTagAttrPairs, "fragment", NULL, "class"); 2914 addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name"); 2915 2916 // tag:attribute pairs that should be checked in xml files. 2917 KeyedVector<String8, Vector<NamespaceAttributePair> > kXmlTagAttrPairs; 2918 addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment"); 2919 addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment"); 2920 2921 const Vector<sp<AaptDir> >& dirs = assets->resDirs(); 2922 const size_t K = dirs.size(); 2923 for (size_t k=0; k<K; k++) { 2924 const sp<AaptDir>& d = dirs.itemAt(k); 2925 const String8& dirName = d->getLeaf(); 2926 Vector<String8> startTags; 2927 const char* startTag = NULL; 2928 const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs = NULL; 2929 if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) { 2930 tagAttrPairs = &kLayoutTagAttrPairs; 2931 } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) { 2932 startTags.add(String8("PreferenceScreen")); 2933 startTags.add(String8("preference-headers")); 2934 tagAttrPairs = &kXmlTagAttrPairs; 2935 } else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) { 2936 startTags.add(String8("menu")); 2937 tagAttrPairs = NULL; 2938 } else { 2939 continue; 2940 } 2941 2942 const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles(); 2943 const size_t N = groups.size(); 2944 for (size_t i=0; i<N; i++) { 2945 const sp<AaptGroup>& group = groups.valueAt(i); 2946 const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles(); 2947 const size_t M = files.size(); 2948 for (size_t j=0; j<M; j++) { 2949 err = writeProguardForXml(keep, files.valueAt(j), startTags, tagAttrPairs); 2950 if (err < 0) { 2951 return err; 2952 } 2953 } 2954 } 2955 } 2956 // Handle the overlays 2957 sp<AaptAssets> overlay = assets->getOverlay(); 2958 if (overlay.get()) { 2959 return writeProguardForLayouts(keep, overlay); 2960 } 2961 2962 return NO_ERROR; 2963} 2964 2965status_t 2966writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets) 2967{ 2968 status_t err = -1; 2969 2970 if (!bundle->getProguardFile()) { 2971 return NO_ERROR; 2972 } 2973 2974 ProguardKeepSet keep; 2975 2976 err = writeProguardForAndroidManifest(&keep, assets); 2977 if (err < 0) { 2978 return err; 2979 } 2980 2981 err = writeProguardForLayouts(&keep, assets); 2982 if (err < 0) { 2983 return err; 2984 } 2985 2986 FILE* fp = fopen(bundle->getProguardFile(), "w+"); 2987 if (fp == NULL) { 2988 fprintf(stderr, "ERROR: Unable to open class file %s: %s\n", 2989 bundle->getProguardFile(), strerror(errno)); 2990 return UNKNOWN_ERROR; 2991 } 2992 2993 const KeyedVector<String8, SortedVector<String8> >& rules = keep.rules; 2994 const size_t N = rules.size(); 2995 for (size_t i=0; i<N; i++) { 2996 const SortedVector<String8>& locations = rules.valueAt(i); 2997 const size_t M = locations.size(); 2998 for (size_t j=0; j<M; j++) { 2999 fprintf(fp, "# %s\n", locations.itemAt(j).string()); 3000 } 3001 fprintf(fp, "%s\n\n", rules.keyAt(i).string()); 3002 } 3003 fclose(fp); 3004 3005 return err; 3006} 3007 3008// Loops through the string paths and writes them to the file pointer 3009// Each file path is written on its own line with a terminating backslash. 3010status_t writePathsToFile(const sp<FilePathStore>& files, FILE* fp) 3011{ 3012 status_t deps = -1; 3013 for (size_t file_i = 0; file_i < files->size(); ++file_i) { 3014 // Add the full file path to the dependency file 3015 fprintf(fp, "%s \\\n", files->itemAt(file_i).string()); 3016 deps++; 3017 } 3018 return deps; 3019} 3020 3021status_t 3022writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw) 3023{ 3024 status_t deps = -1; 3025 deps += writePathsToFile(assets->getFullResPaths(), fp); 3026 if (includeRaw) { 3027 deps += writePathsToFile(assets->getFullAssetPaths(), fp); 3028 } 3029 return deps; 3030} 3031