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