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