Resource.cpp revision b0381efaf71e917214cec78ed1c35eb688454e93
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#define NOISY(x) // x 14 15// ========================================================================== 16// ========================================================================== 17// ========================================================================== 18 19class PackageInfo 20{ 21public: 22 PackageInfo() 23 { 24 } 25 ~PackageInfo() 26 { 27 } 28 29 status_t parsePackage(const sp<AaptGroup>& grp); 30}; 31 32// ========================================================================== 33// ========================================================================== 34// ========================================================================== 35 36static String8 parseResourceName(const String8& leaf) 37{ 38 const char* firstDot = strchr(leaf.string(), '.'); 39 const char* str = leaf.string(); 40 41 if (firstDot) { 42 return String8(str, firstDot-str); 43 } else { 44 return String8(str); 45 } 46} 47 48ResourceTypeSet::ResourceTypeSet() 49 :RefBase(), 50 KeyedVector<String8,sp<AaptGroup> >() 51{ 52} 53 54class ResourceDirIterator 55{ 56public: 57 ResourceDirIterator(const sp<ResourceTypeSet>& set, const String8& resType) 58 : mResType(resType), mSet(set), mSetPos(0), mGroupPos(0) 59 { 60 } 61 62 inline const sp<AaptGroup>& getGroup() const { return mGroup; } 63 inline const sp<AaptFile>& getFile() const { return mFile; } 64 65 inline const String8& getBaseName() const { return mBaseName; } 66 inline const String8& getLeafName() const { return mLeafName; } 67 inline String8 getPath() const { return mPath; } 68 inline const ResTable_config& getParams() const { return mParams; } 69 70 enum { 71 EOD = 1 72 }; 73 74 ssize_t next() 75 { 76 while (true) { 77 sp<AaptGroup> group; 78 sp<AaptFile> file; 79 80 // Try to get next file in this current group. 81 if (mGroup != NULL && mGroupPos < mGroup->getFiles().size()) { 82 group = mGroup; 83 file = group->getFiles().valueAt(mGroupPos++); 84 85 // Try to get the next group/file in this directory 86 } else if (mSetPos < mSet->size()) { 87 mGroup = group = mSet->valueAt(mSetPos++); 88 if (group->getFiles().size() < 1) { 89 continue; 90 } 91 file = group->getFiles().valueAt(0); 92 mGroupPos = 1; 93 94 // All done! 95 } else { 96 return EOD; 97 } 98 99 mFile = file; 100 101 String8 leaf(group->getLeaf()); 102 mLeafName = String8(leaf); 103 mParams = file->getGroupEntry().toParams(); 104 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", 105 group->getPath().string(), mParams.mcc, mParams.mnc, 106 mParams.language[0] ? mParams.language[0] : '-', 107 mParams.language[1] ? mParams.language[1] : '-', 108 mParams.country[0] ? mParams.country[0] : '-', 109 mParams.country[1] ? mParams.country[1] : '-', 110 mParams.orientation, mParams.uiMode, 111 mParams.density, mParams.touchscreen, mParams.keyboard, 112 mParams.inputFlags, mParams.navigation)); 113 mPath = "res"; 114 mPath.appendPath(file->getGroupEntry().toDirName(mResType)); 115 mPath.appendPath(leaf); 116 mBaseName = parseResourceName(leaf); 117 if (mBaseName == "") { 118 fprintf(stderr, "Error: malformed resource filename %s\n", 119 file->getPrintableSource().string()); 120 return UNKNOWN_ERROR; 121 } 122 123 NOISY(printf("file name=%s\n", mBaseName.string())); 124 125 return NO_ERROR; 126 } 127 } 128 129private: 130 String8 mResType; 131 132 const sp<ResourceTypeSet> mSet; 133 size_t mSetPos; 134 135 sp<AaptGroup> mGroup; 136 size_t mGroupPos; 137 138 sp<AaptFile> mFile; 139 String8 mBaseName; 140 String8 mLeafName; 141 String8 mPath; 142 ResTable_config mParams; 143}; 144 145// ========================================================================== 146// ========================================================================== 147// ========================================================================== 148 149bool isValidResourceType(const String8& type) 150{ 151 return type == "anim" || type == "drawable" || type == "layout" 152 || type == "values" || type == "xml" || type == "raw" 153 || type == "color" || type == "menu"; 154} 155 156static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true) 157{ 158 sp<AaptGroup> group = assets->getFiles().valueFor(String8("resources.arsc")); 159 sp<AaptFile> file; 160 if (group != NULL) { 161 file = group->getFiles().valueFor(AaptGroupEntry()); 162 if (file != NULL) { 163 return file; 164 } 165 } 166 167 if (!makeIfNecessary) { 168 return NULL; 169 } 170 return assets->addFile(String8("resources.arsc"), AaptGroupEntry(), String8(), 171 NULL, String8()); 172} 173 174static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets, 175 const sp<AaptGroup>& grp) 176{ 177 if (grp->getFiles().size() != 1) { 178 fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n", 179 grp->getFiles().valueAt(0)->getPrintableSource().string()); 180 } 181 182 sp<AaptFile> file = grp->getFiles().valueAt(0); 183 184 ResXMLTree block; 185 status_t err = parseXMLResource(file, &block); 186 if (err != NO_ERROR) { 187 return err; 188 } 189 //printXMLBlock(&block); 190 191 ResXMLTree::event_code_t code; 192 while ((code=block.next()) != ResXMLTree::START_TAG 193 && code != ResXMLTree::END_DOCUMENT 194 && code != ResXMLTree::BAD_DOCUMENT) { 195 } 196 197 size_t len; 198 if (code != ResXMLTree::START_TAG) { 199 fprintf(stderr, "%s:%d: No start tag found\n", 200 file->getPrintableSource().string(), block.getLineNumber()); 201 return UNKNOWN_ERROR; 202 } 203 if (strcmp16(block.getElementName(&len), String16("manifest").string()) != 0) { 204 fprintf(stderr, "%s:%d: Invalid start tag %s, expected <manifest>\n", 205 file->getPrintableSource().string(), block.getLineNumber(), 206 String8(block.getElementName(&len)).string()); 207 return UNKNOWN_ERROR; 208 } 209 210 ssize_t nameIndex = block.indexOfAttribute(NULL, "package"); 211 if (nameIndex < 0) { 212 fprintf(stderr, "%s:%d: <manifest> does not have package attribute.\n", 213 file->getPrintableSource().string(), block.getLineNumber()); 214 return UNKNOWN_ERROR; 215 } 216 217 assets->setPackage(String8(block.getAttributeStringValue(nameIndex, &len))); 218 219 String16 uses_sdk16("uses-sdk"); 220 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 221 && code != ResXMLTree::BAD_DOCUMENT) { 222 if (code == ResXMLTree::START_TAG) { 223 if (strcmp16(block.getElementName(&len), uses_sdk16.string()) == 0) { 224 ssize_t minSdkIndex = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, 225 "minSdkVersion"); 226 if (minSdkIndex >= 0) { 227 const uint16_t* minSdk16 = block.getAttributeStringValue(minSdkIndex, &len); 228 const char* minSdk8 = strdup(String8(minSdk16).string()); 229 bundle->setMinSdkVersion(minSdk8); 230 } 231 } 232 } 233 } 234 235 return NO_ERROR; 236} 237 238// ========================================================================== 239// ========================================================================== 240// ========================================================================== 241 242static status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets, 243 ResourceTable* table, 244 const sp<ResourceTypeSet>& set, 245 const char* resType) 246{ 247 String8 type8(resType); 248 String16 type16(resType); 249 250 bool hasErrors = false; 251 252 ResourceDirIterator it(set, String8(resType)); 253 ssize_t res; 254 while ((res=it.next()) == NO_ERROR) { 255 if (bundle->getVerbose()) { 256 printf(" (new resource id %s from %s)\n", 257 it.getBaseName().string(), it.getFile()->getPrintableSource().string()); 258 } 259 String16 baseName(it.getBaseName()); 260 const char16_t* str = baseName.string(); 261 const char16_t* const end = str + baseName.size(); 262 while (str < end) { 263 if (!((*str >= 'a' && *str <= 'z') 264 || (*str >= '0' && *str <= '9') 265 || *str == '_' || *str == '.')) { 266 fprintf(stderr, "%s: Invalid file name: must contain only [a-z0-9_.]\n", 267 it.getPath().string()); 268 hasErrors = true; 269 } 270 str++; 271 } 272 String8 resPath = it.getPath(); 273 resPath.convertToResPath(); 274 table->addEntry(SourcePos(it.getPath(), 0), String16(assets->getPackage()), 275 type16, 276 baseName, 277 String16(resPath), 278 NULL, 279 &it.getParams()); 280 assets->addResource(it.getLeafName(), resPath, it.getFile(), type8); 281 } 282 283 return hasErrors ? UNKNOWN_ERROR : NO_ERROR; 284} 285 286static status_t preProcessImages(Bundle* bundle, const sp<AaptAssets>& assets, 287 const sp<ResourceTypeSet>& set) 288{ 289 ResourceDirIterator it(set, String8("drawable")); 290 Vector<sp<AaptFile> > newNameFiles; 291 Vector<String8> newNamePaths; 292 bool hasErrors = false; 293 ssize_t res; 294 while ((res=it.next()) == NO_ERROR) { 295 res = preProcessImage(bundle, assets, it.getFile(), NULL); 296 if (res < NO_ERROR) { 297 hasErrors = true; 298 } 299 } 300 301 return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR; 302} 303 304status_t postProcessImages(const sp<AaptAssets>& assets, 305 ResourceTable* table, 306 const sp<ResourceTypeSet>& set) 307{ 308 ResourceDirIterator it(set, String8("drawable")); 309 bool hasErrors = false; 310 ssize_t res; 311 while ((res=it.next()) == NO_ERROR) { 312 res = postProcessImage(assets, table, it.getFile()); 313 if (res < NO_ERROR) { 314 hasErrors = true; 315 } 316 } 317 318 return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR; 319} 320 321static void collect_files(const sp<AaptDir>& dir, 322 KeyedVector<String8, sp<ResourceTypeSet> >* resources) 323{ 324 const DefaultKeyedVector<String8, sp<AaptGroup> >& groups = dir->getFiles(); 325 int N = groups.size(); 326 for (int i=0; i<N; i++) { 327 String8 leafName = groups.keyAt(i); 328 const sp<AaptGroup>& group = groups.valueAt(i); 329 330 const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files 331 = group->getFiles(); 332 333 if (files.size() == 0) { 334 continue; 335 } 336 337 String8 resType = files.valueAt(0)->getResourceType(); 338 339 ssize_t index = resources->indexOfKey(resType); 340 341 if (index < 0) { 342 sp<ResourceTypeSet> set = new ResourceTypeSet(); 343 set->add(leafName, group); 344 resources->add(resType, set); 345 } else { 346 sp<ResourceTypeSet> set = resources->valueAt(index); 347 index = set->indexOfKey(leafName); 348 if (index < 0) { 349 set->add(leafName, group); 350 } else { 351 sp<AaptGroup> existingGroup = set->valueAt(index); 352 int M = files.size(); 353 for (int j=0; j<M; j++) { 354 existingGroup->addFile(files.valueAt(j)); 355 } 356 } 357 } 358 } 359} 360 361static void collect_files(const sp<AaptAssets>& ass, 362 KeyedVector<String8, sp<ResourceTypeSet> >* resources) 363{ 364 const Vector<sp<AaptDir> >& dirs = ass->resDirs(); 365 int N = dirs.size(); 366 367 for (int i=0; i<N; i++) { 368 sp<AaptDir> d = dirs.itemAt(i); 369 collect_files(d, resources); 370 371 // don't try to include the res dir 372 ass->removeDir(d->getLeaf()); 373 } 374} 375 376enum { 377 ATTR_OKAY = -1, 378 ATTR_NOT_FOUND = -2, 379 ATTR_LEADING_SPACES = -3, 380 ATTR_TRAILING_SPACES = -4 381}; 382static int validateAttr(const String8& path, const ResXMLParser& parser, 383 const char* ns, const char* attr, const char* validChars, bool required) 384{ 385 size_t len; 386 387 ssize_t index = parser.indexOfAttribute(ns, attr); 388 const uint16_t* str; 389 if (index >= 0 && (str=parser.getAttributeStringValue(index, &len)) != NULL) { 390 if (validChars) { 391 for (size_t i=0; i<len; i++) { 392 uint16_t c = str[i]; 393 const char* p = validChars; 394 bool okay = false; 395 while (*p) { 396 if (c == *p) { 397 okay = true; 398 break; 399 } 400 p++; 401 } 402 if (!okay) { 403 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid character '%c'.\n", 404 path.string(), parser.getLineNumber(), 405 String8(parser.getElementName(&len)).string(), attr, (char)str[i]); 406 return (int)i; 407 } 408 } 409 } 410 if (*str == ' ') { 411 fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not start with a space.\n", 412 path.string(), parser.getLineNumber(), 413 String8(parser.getElementName(&len)).string(), attr); 414 return ATTR_LEADING_SPACES; 415 } 416 if (str[len-1] == ' ') { 417 fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not end with a space.\n", 418 path.string(), parser.getLineNumber(), 419 String8(parser.getElementName(&len)).string(), attr); 420 return ATTR_TRAILING_SPACES; 421 } 422 return ATTR_OKAY; 423 } 424 if (required) { 425 fprintf(stderr, "%s:%d: Tag <%s> missing required attribute %s.\n", 426 path.string(), parser.getLineNumber(), 427 String8(parser.getElementName(&len)).string(), attr); 428 return ATTR_NOT_FOUND; 429 } 430 return ATTR_OKAY; 431} 432 433static void checkForIds(const String8& path, ResXMLParser& parser) 434{ 435 ResXMLTree::event_code_t code; 436 while ((code=parser.next()) != ResXMLTree::END_DOCUMENT 437 && code > ResXMLTree::BAD_DOCUMENT) { 438 if (code == ResXMLTree::START_TAG) { 439 ssize_t index = parser.indexOfAttribute(NULL, "id"); 440 if (index >= 0) { 441 fprintf(stderr, "%s:%d: warning: found plain 'id' attribute; did you mean the new 'android:id' name?\n", 442 path.string(), parser.getLineNumber()); 443 } 444 } 445 } 446} 447 448static bool applyFileOverlay(Bundle *bundle, 449 const sp<AaptAssets>& assets, 450 const sp<ResourceTypeSet>& baseSet, 451 const char *resType) 452{ 453 if (bundle->getVerbose()) { 454 printf("applyFileOverlay for %s\n", resType); 455 } 456 457 // Replace any base level files in this category with any found from the overlay 458 // Also add any found only in the overlay. 459 sp<AaptAssets> overlay = assets->getOverlay(); 460 String8 resTypeString(resType); 461 462 // work through the linked list of overlays 463 while (overlay.get()) { 464 KeyedVector<String8, sp<ResourceTypeSet> >* overlayRes = overlay->getResources(); 465 466 // get the overlay resources of the requested type 467 ssize_t index = overlayRes->indexOfKey(resTypeString); 468 if (index >= 0) { 469 sp<ResourceTypeSet> overlaySet = overlayRes->valueAt(index); 470 471 // for each of the resources, check for a match in the previously built 472 // non-overlay "baseset". 473 size_t overlayCount = overlaySet->size(); 474 for (size_t overlayIndex=0; overlayIndex<overlayCount; overlayIndex++) { 475 if (bundle->getVerbose()) { 476 printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string()); 477 } 478 size_t baseIndex = baseSet->indexOfKey(overlaySet->keyAt(overlayIndex)); 479 if (baseIndex < UNKNOWN_ERROR) { 480 // look for same flavor. For a given file (strings.xml, for example) 481 // there may be a locale specific or other flavors - we want to match 482 // the same flavor. 483 sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex); 484 sp<AaptGroup> baseGroup = baseSet->valueAt(baseIndex); 485 486 DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles = 487 overlayGroup->getFiles(); 488 if (bundle->getVerbose()) { 489 DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles = 490 baseGroup->getFiles(); 491 for (size_t i=0; i < baseFiles.size(); i++) { 492 printf("baseFile %ld has flavor %s\n", i, 493 baseFiles.keyAt(i).toString().string()); 494 } 495 for (size_t i=0; i < overlayFiles.size(); i++) { 496 printf("overlayFile %ld has flavor %s\n", i, 497 overlayFiles.keyAt(i).toString().string()); 498 } 499 } 500 501 size_t overlayGroupSize = overlayFiles.size(); 502 for (size_t overlayGroupIndex = 0; 503 overlayGroupIndex<overlayGroupSize; 504 overlayGroupIndex++) { 505 size_t baseFileIndex = 506 baseGroup->getFiles().indexOfKey(overlayFiles. 507 keyAt(overlayGroupIndex)); 508 if(baseFileIndex < UNKNOWN_ERROR) { 509 if (bundle->getVerbose()) { 510 printf("found a match (%ld) for overlay file %s, for flavor %s\n", 511 baseFileIndex, 512 overlayGroup->getLeaf().string(), 513 overlayFiles.keyAt(overlayGroupIndex).toString().string()); 514 } 515 baseGroup->removeFile(baseFileIndex); 516 } else { 517 // didn't find a match fall through and add it.. 518 } 519 baseGroup->addFile(overlayFiles.valueAt(overlayGroupIndex)); 520 assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex)); 521 } 522 } else { 523 // this group doesn't exist (a file that's only in the overlay) 524 baseSet->add(overlaySet->keyAt(overlayIndex), 525 overlaySet->valueAt(overlayIndex)); 526 // make sure all flavors are defined in the resources. 527 sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex); 528 DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles = 529 overlayGroup->getFiles(); 530 size_t overlayGroupSize = overlayFiles.size(); 531 for (size_t overlayGroupIndex = 0; 532 overlayGroupIndex<overlayGroupSize; 533 overlayGroupIndex++) { 534 assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex)); 535 } 536 } 537 } 538 // this overlay didn't have resources for this type 539 } 540 // try next overlay 541 overlay = overlay->getOverlay(); 542 } 543 return true; 544} 545 546void addTagAttribute(const sp<XMLNode>& node, const char* ns8, 547 const char* attr8, const char* value) 548{ 549 if (value == NULL) { 550 return; 551 } 552 553 const String16 ns(ns8); 554 const String16 attr(attr8); 555 556 if (node->getAttribute(ns, attr) != NULL) { 557 fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s)\n", 558 String8(attr).string(), String8(ns).string()); 559 return; 560 } 561 562 node->addAttribute(ns, attr, String16(value)); 563} 564 565static void fullyQualifyClassName(const String8& package, sp<XMLNode> node, 566 const String16& attrName) { 567 XMLNode::attribute_entry* attr = node->editAttribute( 568 String16("http://schemas.android.com/apk/res/android"), attrName); 569 if (attr != NULL) { 570 String8 name(attr->string); 571 572 // asdf --> package.asdf 573 // .asdf .a.b --> package.asdf package.a.b 574 // asdf.adsf --> asdf.asdf 575 String8 className; 576 const char* p = name.string(); 577 const char* q = strchr(p, '.'); 578 if (p == q) { 579 className += package; 580 className += name; 581 } else if (q == NULL) { 582 className += package; 583 className += "."; 584 className += name; 585 } else { 586 className += name; 587 } 588 NOISY(printf("Qualifying class '%s' to '%s'", name.string(), className.string())); 589 attr->string.setTo(String16(className)); 590 } 591} 592 593status_t massageManifest(Bundle* bundle, sp<XMLNode> root) 594{ 595 root = root->searchElement(String16(), String16("manifest")); 596 if (root == NULL) { 597 fprintf(stderr, "No <manifest> tag.\n"); 598 return UNKNOWN_ERROR; 599 } 600 601 addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode", 602 bundle->getVersionCode()); 603 addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName", 604 bundle->getVersionName()); 605 606 if (bundle->getMinSdkVersion() != NULL 607 || bundle->getTargetSdkVersion() != NULL 608 || bundle->getMaxSdkVersion() != NULL) { 609 sp<XMLNode> vers = root->getChildElement(String16(), String16("uses-sdk")); 610 if (vers == NULL) { 611 vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk")); 612 root->insertChildAt(vers, 0); 613 } 614 615 addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion", 616 bundle->getMinSdkVersion()); 617 addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion", 618 bundle->getTargetSdkVersion()); 619 addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion", 620 bundle->getMaxSdkVersion()); 621 } 622 623 // Deal with manifest package name overrides 624 const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride(); 625 if (manifestPackageNameOverride != NULL) { 626 // Update the actual package name 627 XMLNode::attribute_entry* attr = root->editAttribute(String16(), String16("package")); 628 if (attr == NULL) { 629 fprintf(stderr, "package name is required with --rename-manifest-package.\n"); 630 return UNKNOWN_ERROR; 631 } 632 String8 origPackage(attr->string); 633 attr->string.setTo(String16(manifestPackageNameOverride)); 634 NOISY(printf("Overriding package '%s' to be '%s'\n", origPackage.string(), manifestPackageNameOverride)); 635 636 // Make class names fully qualified 637 sp<XMLNode> application = root->getChildElement(String16(), String16("application")); 638 if (application != NULL) { 639 fullyQualifyClassName(origPackage, application, String16("name")); 640 fullyQualifyClassName(origPackage, application, String16("backupAgent")); 641 642 Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(application->getChildren()); 643 for (size_t i = 0; i < children.size(); i++) { 644 sp<XMLNode> child = children.editItemAt(i); 645 String8 tag(child->getElementName()); 646 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") { 647 fullyQualifyClassName(origPackage, child, String16("name")); 648 } else if (tag == "activity-alias") { 649 fullyQualifyClassName(origPackage, child, String16("name")); 650 fullyQualifyClassName(origPackage, child, String16("targetActivity")); 651 } 652 } 653 } 654 } 655 656 // Deal with manifest package name overrides 657 const char* instrumentationPackageNameOverride = bundle->getInstrumentationPackageNameOverride(); 658 if (instrumentationPackageNameOverride != NULL) { 659 // Fix up instrumentation targets. 660 Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(root->getChildren()); 661 for (size_t i = 0; i < children.size(); i++) { 662 sp<XMLNode> child = children.editItemAt(i); 663 String8 tag(child->getElementName()); 664 if (tag == "instrumentation") { 665 XMLNode::attribute_entry* attr = child->editAttribute( 666 String16("http://schemas.android.com/apk/res/android"), String16("targetPackage")); 667 if (attr != NULL) { 668 attr->string.setTo(String16(instrumentationPackageNameOverride)); 669 } 670 } 671 } 672 } 673 674 return NO_ERROR; 675} 676 677#define ASSIGN_IT(n) \ 678 do { \ 679 ssize_t index = resources->indexOfKey(String8(#n)); \ 680 if (index >= 0) { \ 681 n ## s = resources->valueAt(index); \ 682 } \ 683 } while (0) 684 685status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) 686{ 687 // First, look for a package file to parse. This is required to 688 // be able to generate the resource information. 689 sp<AaptGroup> androidManifestFile = 690 assets->getFiles().valueFor(String8("AndroidManifest.xml")); 691 if (androidManifestFile == NULL) { 692 fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n"); 693 return UNKNOWN_ERROR; 694 } 695 696 status_t err = parsePackage(bundle, assets, androidManifestFile); 697 if (err != NO_ERROR) { 698 return err; 699 } 700 701 NOISY(printf("Creating resources for package %s\n", 702 assets->getPackage().string())); 703 704 ResourceTable table(bundle, String16(assets->getPackage())); 705 err = table.addIncludedResources(bundle, assets); 706 if (err != NO_ERROR) { 707 return err; 708 } 709 710 NOISY(printf("Found %d included resource packages\n", (int)table.size())); 711 712 // Standard flags for compiled XML and optional UTF-8 encoding 713 int xmlFlags = XML_COMPILE_STANDARD_RESOURCE; 714 if (bundle->getUTF8()) { 715 xmlFlags |= XML_COMPILE_UTF8; 716 } 717 718 // -------------------------------------------------------------- 719 // First, gather all resource information. 720 // -------------------------------------------------------------- 721 722 // resType -> leafName -> group 723 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 724 new KeyedVector<String8, sp<ResourceTypeSet> >; 725 collect_files(assets, resources); 726 727 sp<ResourceTypeSet> drawables; 728 sp<ResourceTypeSet> layouts; 729 sp<ResourceTypeSet> anims; 730 sp<ResourceTypeSet> xmls; 731 sp<ResourceTypeSet> raws; 732 sp<ResourceTypeSet> colors; 733 sp<ResourceTypeSet> menus; 734 735 ASSIGN_IT(drawable); 736 ASSIGN_IT(layout); 737 ASSIGN_IT(anim); 738 ASSIGN_IT(xml); 739 ASSIGN_IT(raw); 740 ASSIGN_IT(color); 741 ASSIGN_IT(menu); 742 743 assets->setResources(resources); 744 // now go through any resource overlays and collect their files 745 sp<AaptAssets> current = assets->getOverlay(); 746 while(current.get()) { 747 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 748 new KeyedVector<String8, sp<ResourceTypeSet> >; 749 current->setResources(resources); 750 collect_files(current, resources); 751 current = current->getOverlay(); 752 } 753 // apply the overlay files to the base set 754 if (!applyFileOverlay(bundle, assets, drawables, "drawable") || 755 !applyFileOverlay(bundle, assets, layouts, "layout") || 756 !applyFileOverlay(bundle, assets, anims, "anim") || 757 !applyFileOverlay(bundle, assets, xmls, "xml") || 758 !applyFileOverlay(bundle, assets, raws, "raw") || 759 !applyFileOverlay(bundle, assets, colors, "color") || 760 !applyFileOverlay(bundle, assets, menus, "menu")) { 761 return UNKNOWN_ERROR; 762 } 763 764 bool hasErrors = false; 765 766 if (drawables != NULL) { 767 err = preProcessImages(bundle, assets, drawables); 768 if (err == NO_ERROR) { 769 err = makeFileResources(bundle, assets, &table, drawables, "drawable"); 770 if (err != NO_ERROR) { 771 hasErrors = true; 772 } 773 } else { 774 hasErrors = true; 775 } 776 } 777 778 if (layouts != NULL) { 779 err = makeFileResources(bundle, assets, &table, layouts, "layout"); 780 if (err != NO_ERROR) { 781 hasErrors = true; 782 } 783 } 784 785 if (anims != NULL) { 786 err = makeFileResources(bundle, assets, &table, anims, "anim"); 787 if (err != NO_ERROR) { 788 hasErrors = true; 789 } 790 } 791 792 if (xmls != NULL) { 793 err = makeFileResources(bundle, assets, &table, xmls, "xml"); 794 if (err != NO_ERROR) { 795 hasErrors = true; 796 } 797 } 798 799 if (raws != NULL) { 800 err = makeFileResources(bundle, assets, &table, raws, "raw"); 801 if (err != NO_ERROR) { 802 hasErrors = true; 803 } 804 } 805 806 // compile resources 807 current = assets; 808 while(current.get()) { 809 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 810 current->getResources(); 811 812 ssize_t index = resources->indexOfKey(String8("values")); 813 if (index >= 0) { 814 ResourceDirIterator it(resources->valueAt(index), String8("values")); 815 ssize_t res; 816 while ((res=it.next()) == NO_ERROR) { 817 sp<AaptFile> file = it.getFile(); 818 res = compileResourceFile(bundle, assets, file, it.getParams(), 819 (current!=assets), &table); 820 if (res != NO_ERROR) { 821 hasErrors = true; 822 } 823 } 824 } 825 current = current->getOverlay(); 826 } 827 828 if (colors != NULL) { 829 err = makeFileResources(bundle, assets, &table, colors, "color"); 830 if (err != NO_ERROR) { 831 hasErrors = true; 832 } 833 } 834 835 if (menus != NULL) { 836 err = makeFileResources(bundle, assets, &table, menus, "menu"); 837 if (err != NO_ERROR) { 838 hasErrors = true; 839 } 840 } 841 842 // -------------------------------------------------------------------- 843 // Assignment of resource IDs and initial generation of resource table. 844 // -------------------------------------------------------------------- 845 846 if (table.hasResources()) { 847 sp<AaptFile> resFile(getResourceFile(assets)); 848 if (resFile == NULL) { 849 fprintf(stderr, "Error: unable to generate entry for resource data\n"); 850 return UNKNOWN_ERROR; 851 } 852 853 err = table.assignResourceIds(); 854 if (err < NO_ERROR) { 855 return err; 856 } 857 } 858 859 // -------------------------------------------------------------- 860 // Finally, we can now we can compile XML files, which may reference 861 // resources. 862 // -------------------------------------------------------------- 863 864 if (layouts != NULL) { 865 ResourceDirIterator it(layouts, String8("layout")); 866 while ((err=it.next()) == NO_ERROR) { 867 String8 src = it.getFile()->getPrintableSource(); 868 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 869 if (err == NO_ERROR) { 870 ResXMLTree block; 871 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); 872 checkForIds(src, block); 873 } else { 874 hasErrors = true; 875 } 876 } 877 878 if (err < NO_ERROR) { 879 hasErrors = true; 880 } 881 err = NO_ERROR; 882 } 883 884 if (anims != NULL) { 885 ResourceDirIterator it(anims, String8("anim")); 886 while ((err=it.next()) == NO_ERROR) { 887 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 888 if (err != NO_ERROR) { 889 hasErrors = true; 890 } 891 } 892 893 if (err < NO_ERROR) { 894 hasErrors = true; 895 } 896 err = NO_ERROR; 897 } 898 899 if (xmls != NULL) { 900 ResourceDirIterator it(xmls, String8("xml")); 901 while ((err=it.next()) == NO_ERROR) { 902 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 903 if (err != NO_ERROR) { 904 hasErrors = true; 905 } 906 } 907 908 if (err < NO_ERROR) { 909 hasErrors = true; 910 } 911 err = NO_ERROR; 912 } 913 914 if (drawables != NULL) { 915 err = postProcessImages(assets, &table, drawables); 916 if (err != NO_ERROR) { 917 hasErrors = true; 918 } 919 } 920 921 if (colors != NULL) { 922 ResourceDirIterator it(colors, String8("color")); 923 while ((err=it.next()) == NO_ERROR) { 924 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 925 if (err != NO_ERROR) { 926 hasErrors = true; 927 } 928 } 929 930 if (err < NO_ERROR) { 931 hasErrors = true; 932 } 933 err = NO_ERROR; 934 } 935 936 if (menus != NULL) { 937 ResourceDirIterator it(menus, String8("menu")); 938 while ((err=it.next()) == NO_ERROR) { 939 String8 src = it.getFile()->getPrintableSource(); 940 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 941 if (err != NO_ERROR) { 942 hasErrors = true; 943 } 944 ResXMLTree block; 945 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); 946 checkForIds(src, block); 947 } 948 949 if (err < NO_ERROR) { 950 hasErrors = true; 951 } 952 err = NO_ERROR; 953 } 954 955 const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0)); 956 String8 manifestPath(manifestFile->getPrintableSource()); 957 958 // Perform a basic validation of the manifest file. This time we 959 // parse it with the comments intact, so that we can use them to 960 // generate java docs... so we are not going to write this one 961 // back out to the final manifest data. 962 err = compileXmlFile(assets, manifestFile, &table, 963 XML_COMPILE_ASSIGN_ATTRIBUTE_IDS 964 | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES); 965 if (err < NO_ERROR) { 966 return err; 967 } 968 ResXMLTree block; 969 block.setTo(manifestFile->getData(), manifestFile->getSize(), true); 970 String16 manifest16("manifest"); 971 String16 permission16("permission"); 972 String16 permission_group16("permission-group"); 973 String16 uses_permission16("uses-permission"); 974 String16 instrumentation16("instrumentation"); 975 String16 application16("application"); 976 String16 provider16("provider"); 977 String16 service16("service"); 978 String16 receiver16("receiver"); 979 String16 activity16("activity"); 980 String16 action16("action"); 981 String16 category16("category"); 982 String16 data16("scheme"); 983 const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz" 984 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789"; 985 const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz" 986 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-"; 987 const char* classIdentChars = "abcdefghijklmnopqrstuvwxyz" 988 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789$"; 989 const char* processIdentChars = "abcdefghijklmnopqrstuvwxyz" 990 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:"; 991 const char* authoritiesIdentChars = "abcdefghijklmnopqrstuvwxyz" 992 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-:;"; 993 const char* typeIdentChars = "abcdefghijklmnopqrstuvwxyz" 994 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:-/*+"; 995 const char* schemeIdentChars = "abcdefghijklmnopqrstuvwxyz" 996 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-"; 997 ResXMLTree::event_code_t code; 998 sp<AaptSymbols> permissionSymbols; 999 sp<AaptSymbols> permissionGroupSymbols; 1000 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 1001 && code > ResXMLTree::BAD_DOCUMENT) { 1002 if (code == ResXMLTree::START_TAG) { 1003 size_t len; 1004 if (block.getElementNamespace(&len) != NULL) { 1005 continue; 1006 } 1007 if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) { 1008 if (validateAttr(manifestPath, block, NULL, "package", 1009 packageIdentChars, true) != ATTR_OKAY) { 1010 hasErrors = true; 1011 } 1012 } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0 1013 || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) { 1014 const bool isGroup = strcmp16(block.getElementName(&len), 1015 permission_group16.string()) == 0; 1016 if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name", 1017 isGroup ? packageIdentCharsWithTheStupid 1018 : packageIdentChars, true) != ATTR_OKAY) { 1019 hasErrors = true; 1020 } 1021 SourcePos srcPos(manifestPath, block.getLineNumber()); 1022 sp<AaptSymbols> syms; 1023 if (!isGroup) { 1024 syms = permissionSymbols; 1025 if (syms == NULL) { 1026 sp<AaptSymbols> symbols = 1027 assets->getSymbolsFor(String8("Manifest")); 1028 syms = permissionSymbols = symbols->addNestedSymbol( 1029 String8("permission"), srcPos); 1030 } 1031 } else { 1032 syms = permissionGroupSymbols; 1033 if (syms == NULL) { 1034 sp<AaptSymbols> symbols = 1035 assets->getSymbolsFor(String8("Manifest")); 1036 syms = permissionGroupSymbols = symbols->addNestedSymbol( 1037 String8("permission_group"), srcPos); 1038 } 1039 } 1040 size_t len; 1041 ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name"); 1042 const uint16_t* id = block.getAttributeStringValue(index, &len); 1043 if (id == NULL) { 1044 fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n", 1045 manifestPath.string(), block.getLineNumber(), 1046 String8(block.getElementName(&len)).string()); 1047 hasErrors = true; 1048 break; 1049 } 1050 String8 idStr(id); 1051 char* p = idStr.lockBuffer(idStr.size()); 1052 char* e = p + idStr.size(); 1053 bool begins_with_digit = true; // init to true so an empty string fails 1054 while (e > p) { 1055 e--; 1056 if (*e >= '0' && *e <= '9') { 1057 begins_with_digit = true; 1058 continue; 1059 } 1060 if ((*e >= 'a' && *e <= 'z') || 1061 (*e >= 'A' && *e <= 'Z') || 1062 (*e == '_')) { 1063 begins_with_digit = false; 1064 continue; 1065 } 1066 if (isGroup && (*e == '-')) { 1067 *e = '_'; 1068 begins_with_digit = false; 1069 continue; 1070 } 1071 e++; 1072 break; 1073 } 1074 idStr.unlockBuffer(); 1075 // verify that we stopped because we hit a period or 1076 // the beginning of the string, and that the 1077 // identifier didn't begin with a digit. 1078 if (begins_with_digit || (e != p && *(e-1) != '.')) { 1079 fprintf(stderr, 1080 "%s:%d: Permission name <%s> is not a valid Java symbol\n", 1081 manifestPath.string(), block.getLineNumber(), idStr.string()); 1082 hasErrors = true; 1083 } 1084 syms->addStringSymbol(String8(e), idStr, srcPos); 1085 const uint16_t* cmt = block.getComment(&len); 1086 if (cmt != NULL && *cmt != 0) { 1087 //printf("Comment of %s: %s\n", String8(e).string(), 1088 // String8(cmt).string()); 1089 syms->appendComment(String8(e), String16(cmt), srcPos); 1090 } else { 1091 //printf("No comment for %s\n", String8(e).string()); 1092 } 1093 syms->makeSymbolPublic(String8(e), srcPos); 1094 } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) { 1095 if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name", 1096 packageIdentChars, true) != ATTR_OKAY) { 1097 hasErrors = true; 1098 } 1099 } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) { 1100 if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name", 1101 classIdentChars, true) != ATTR_OKAY) { 1102 hasErrors = true; 1103 } 1104 if (validateAttr(manifestPath, block, 1105 RESOURCES_ANDROID_NAMESPACE, "targetPackage", 1106 packageIdentChars, true) != ATTR_OKAY) { 1107 hasErrors = true; 1108 } 1109 } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) { 1110 if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name", 1111 classIdentChars, false) != ATTR_OKAY) { 1112 hasErrors = true; 1113 } 1114 if (validateAttr(manifestPath, block, 1115 RESOURCES_ANDROID_NAMESPACE, "permission", 1116 packageIdentChars, false) != ATTR_OKAY) { 1117 hasErrors = true; 1118 } 1119 if (validateAttr(manifestPath, block, 1120 RESOURCES_ANDROID_NAMESPACE, "process", 1121 processIdentChars, false) != ATTR_OKAY) { 1122 hasErrors = true; 1123 } 1124 if (validateAttr(manifestPath, block, 1125 RESOURCES_ANDROID_NAMESPACE, "taskAffinity", 1126 processIdentChars, false) != ATTR_OKAY) { 1127 hasErrors = true; 1128 } 1129 } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) { 1130 if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name", 1131 classIdentChars, true) != ATTR_OKAY) { 1132 hasErrors = true; 1133 } 1134 if (validateAttr(manifestPath, block, 1135 RESOURCES_ANDROID_NAMESPACE, "authorities", 1136 authoritiesIdentChars, true) != ATTR_OKAY) { 1137 hasErrors = true; 1138 } 1139 if (validateAttr(manifestPath, block, 1140 RESOURCES_ANDROID_NAMESPACE, "permission", 1141 packageIdentChars, false) != ATTR_OKAY) { 1142 hasErrors = true; 1143 } 1144 if (validateAttr(manifestPath, block, 1145 RESOURCES_ANDROID_NAMESPACE, "process", 1146 processIdentChars, false) != ATTR_OKAY) { 1147 hasErrors = true; 1148 } 1149 } else if (strcmp16(block.getElementName(&len), service16.string()) == 0 1150 || strcmp16(block.getElementName(&len), receiver16.string()) == 0 1151 || strcmp16(block.getElementName(&len), activity16.string()) == 0) { 1152 if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name", 1153 classIdentChars, true) != ATTR_OKAY) { 1154 hasErrors = true; 1155 } 1156 if (validateAttr(manifestPath, block, 1157 RESOURCES_ANDROID_NAMESPACE, "permission", 1158 packageIdentChars, false) != ATTR_OKAY) { 1159 hasErrors = true; 1160 } 1161 if (validateAttr(manifestPath, block, 1162 RESOURCES_ANDROID_NAMESPACE, "process", 1163 processIdentChars, false) != ATTR_OKAY) { 1164 hasErrors = true; 1165 } 1166 if (validateAttr(manifestPath, block, 1167 RESOURCES_ANDROID_NAMESPACE, "taskAffinity", 1168 processIdentChars, false) != ATTR_OKAY) { 1169 hasErrors = true; 1170 } 1171 } else if (strcmp16(block.getElementName(&len), action16.string()) == 0 1172 || strcmp16(block.getElementName(&len), category16.string()) == 0) { 1173 if (validateAttr(manifestPath, block, 1174 RESOURCES_ANDROID_NAMESPACE, "name", 1175 packageIdentChars, true) != ATTR_OKAY) { 1176 hasErrors = true; 1177 } 1178 } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) { 1179 if (validateAttr(manifestPath, block, 1180 RESOURCES_ANDROID_NAMESPACE, "mimeType", 1181 typeIdentChars, true) != ATTR_OKAY) { 1182 hasErrors = true; 1183 } 1184 if (validateAttr(manifestPath, block, 1185 RESOURCES_ANDROID_NAMESPACE, "scheme", 1186 schemeIdentChars, true) != ATTR_OKAY) { 1187 hasErrors = true; 1188 } 1189 } 1190 } 1191 } 1192 1193 if (table.validateLocalizations()) { 1194 hasErrors = true; 1195 } 1196 1197 if (hasErrors) { 1198 return UNKNOWN_ERROR; 1199 } 1200 1201 // Generate final compiled manifest file. 1202 manifestFile->clearData(); 1203 sp<XMLNode> manifestTree = XMLNode::parse(manifestFile); 1204 if (manifestTree == NULL) { 1205 return UNKNOWN_ERROR; 1206 } 1207 err = massageManifest(bundle, manifestTree); 1208 if (err < NO_ERROR) { 1209 return err; 1210 } 1211 err = compileXmlFile(assets, manifestTree, manifestFile, &table); 1212 if (err < NO_ERROR) { 1213 return err; 1214 } 1215 1216 //block.restart(); 1217 //printXMLBlock(&block); 1218 1219 // -------------------------------------------------------------- 1220 // Generate the final resource table. 1221 // Re-flatten because we may have added new resource IDs 1222 // -------------------------------------------------------------- 1223 1224 if (table.hasResources()) { 1225 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R")); 1226 err = table.addSymbols(symbols); 1227 if (err < NO_ERROR) { 1228 return err; 1229 } 1230 1231 sp<AaptFile> resFile(getResourceFile(assets)); 1232 if (resFile == NULL) { 1233 fprintf(stderr, "Error: unable to generate entry for resource data\n"); 1234 return UNKNOWN_ERROR; 1235 } 1236 1237 err = table.flatten(bundle, resFile); 1238 if (err < NO_ERROR) { 1239 return err; 1240 } 1241 1242 if (bundle->getPublicOutputFile()) { 1243 FILE* fp = fopen(bundle->getPublicOutputFile(), "w+"); 1244 if (fp == NULL) { 1245 fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n", 1246 (const char*)bundle->getPublicOutputFile(), strerror(errno)); 1247 return UNKNOWN_ERROR; 1248 } 1249 if (bundle->getVerbose()) { 1250 printf(" Writing public definitions to %s.\n", bundle->getPublicOutputFile()); 1251 } 1252 table.writePublicDefinitions(String16(assets->getPackage()), fp); 1253 fclose(fp); 1254 } 1255#if 0 1256 NOISY( 1257 ResTable rt; 1258 rt.add(resFile->getData(), resFile->getSize(), NULL); 1259 printf("Generated resources:\n"); 1260 rt.print(); 1261 ) 1262#endif 1263 // These resources are now considered to be a part of the included 1264 // resources, for others to reference. 1265 err = assets->addIncludedResources(resFile); 1266 if (err < NO_ERROR) { 1267 fprintf(stderr, "ERROR: Unable to parse generated resources, aborting.\n"); 1268 return err; 1269 } 1270 } 1271 return err; 1272} 1273 1274static const char* getIndentSpace(int indent) 1275{ 1276static const char whitespace[] = 1277" "; 1278 1279 return whitespace + sizeof(whitespace) - 1 - indent*4; 1280} 1281 1282static status_t fixupSymbol(String16* inoutSymbol) 1283{ 1284 inoutSymbol->replaceAll('.', '_'); 1285 inoutSymbol->replaceAll(':', '_'); 1286 return NO_ERROR; 1287} 1288 1289static String16 getAttributeComment(const sp<AaptAssets>& assets, 1290 const String8& name, 1291 String16* outTypeComment = NULL) 1292{ 1293 sp<AaptSymbols> asym = assets->getSymbolsFor(String8("R")); 1294 if (asym != NULL) { 1295 //printf("Got R symbols!\n"); 1296 asym = asym->getNestedSymbols().valueFor(String8("attr")); 1297 if (asym != NULL) { 1298 //printf("Got attrs symbols! comment %s=%s\n", 1299 // name.string(), String8(asym->getComment(name)).string()); 1300 if (outTypeComment != NULL) { 1301 *outTypeComment = asym->getTypeComment(name); 1302 } 1303 return asym->getComment(name); 1304 } 1305 } 1306 return String16(); 1307} 1308 1309static status_t writeLayoutClasses( 1310 FILE* fp, const sp<AaptAssets>& assets, 1311 const sp<AaptSymbols>& symbols, int indent, bool includePrivate) 1312{ 1313 const char* indentStr = getIndentSpace(indent); 1314 if (!includePrivate) { 1315 fprintf(fp, "%s/** @doconly */\n", indentStr); 1316 } 1317 fprintf(fp, "%spublic static final class styleable {\n", indentStr); 1318 indent++; 1319 1320 String16 attr16("attr"); 1321 String16 package16(assets->getPackage()); 1322 1323 indentStr = getIndentSpace(indent); 1324 bool hasErrors = false; 1325 1326 size_t i; 1327 size_t N = symbols->getNestedSymbols().size(); 1328 for (i=0; i<N; i++) { 1329 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 1330 String16 nclassName16(symbols->getNestedSymbols().keyAt(i)); 1331 String8 realClassName(nclassName16); 1332 if (fixupSymbol(&nclassName16) != NO_ERROR) { 1333 hasErrors = true; 1334 } 1335 String8 nclassName(nclassName16); 1336 1337 SortedVector<uint32_t> idents; 1338 Vector<uint32_t> origOrder; 1339 Vector<bool> publicFlags; 1340 1341 size_t a; 1342 size_t NA = nsymbols->getSymbols().size(); 1343 for (a=0; a<NA; a++) { 1344 const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a)); 1345 int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32 1346 ? sym.int32Val : 0; 1347 bool isPublic = true; 1348 if (code == 0) { 1349 String16 name16(sym.name); 1350 uint32_t typeSpecFlags; 1351 code = assets->getIncludedResources().identifierForName( 1352 name16.string(), name16.size(), 1353 attr16.string(), attr16.size(), 1354 package16.string(), package16.size(), &typeSpecFlags); 1355 if (code == 0) { 1356 fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n", 1357 nclassName.string(), sym.name.string()); 1358 hasErrors = true; 1359 } 1360 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 1361 } 1362 idents.add(code); 1363 origOrder.add(code); 1364 publicFlags.add(isPublic); 1365 } 1366 1367 NA = idents.size(); 1368 1369 bool deprecated = false; 1370 1371 String16 comment = symbols->getComment(realClassName); 1372 fprintf(fp, "%s/** ", indentStr); 1373 if (comment.size() > 0) { 1374 String8 cmt(comment); 1375 fprintf(fp, "%s\n", cmt.string()); 1376 if (strstr(cmt.string(), "@deprecated") != NULL) { 1377 deprecated = true; 1378 } 1379 } else { 1380 fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string()); 1381 } 1382 bool hasTable = false; 1383 for (a=0; a<NA; a++) { 1384 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 1385 if (pos >= 0) { 1386 if (!hasTable) { 1387 hasTable = true; 1388 fprintf(fp, 1389 "%s <p>Includes the following attributes:</p>\n" 1390 "%s <table>\n" 1391 "%s <colgroup align=\"left\" />\n" 1392 "%s <colgroup align=\"left\" />\n" 1393 "%s <tr><th>Attribute</th><th>Description</th></tr>\n", 1394 indentStr, 1395 indentStr, 1396 indentStr, 1397 indentStr, 1398 indentStr); 1399 } 1400 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 1401 if (!publicFlags.itemAt(a) && !includePrivate) { 1402 continue; 1403 } 1404 String8 name8(sym.name); 1405 String16 comment(sym.comment); 1406 if (comment.size() <= 0) { 1407 comment = getAttributeComment(assets, name8); 1408 } 1409 if (comment.size() > 0) { 1410 const char16_t* p = comment.string(); 1411 while (*p != 0 && *p != '.') { 1412 if (*p == '{') { 1413 while (*p != 0 && *p != '}') { 1414 p++; 1415 } 1416 } else { 1417 p++; 1418 } 1419 } 1420 if (*p == '.') { 1421 p++; 1422 } 1423 comment = String16(comment.string(), p-comment.string()); 1424 } 1425 String16 name(name8); 1426 fixupSymbol(&name); 1427 fprintf(fp, "%s <tr><td><code>{@link #%s_%s %s:%s}</code></td><td>%s</td></tr>\n", 1428 indentStr, nclassName.string(), 1429 String8(name).string(), 1430 assets->getPackage().string(), 1431 String8(name).string(), 1432 String8(comment).string()); 1433 } 1434 } 1435 if (hasTable) { 1436 fprintf(fp, "%s </table>\n", indentStr); 1437 } 1438 for (a=0; a<NA; a++) { 1439 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 1440 if (pos >= 0) { 1441 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 1442 if (!publicFlags.itemAt(a) && !includePrivate) { 1443 continue; 1444 } 1445 String16 name(sym.name); 1446 fixupSymbol(&name); 1447 fprintf(fp, "%s @see #%s_%s\n", 1448 indentStr, nclassName.string(), 1449 String8(name).string()); 1450 } 1451 } 1452 fprintf(fp, "%s */\n", getIndentSpace(indent)); 1453 1454 if (deprecated) { 1455 fprintf(fp, "%s@Deprecated\n", indentStr); 1456 } 1457 1458 fprintf(fp, 1459 "%spublic static final int[] %s = {\n" 1460 "%s", 1461 indentStr, nclassName.string(), 1462 getIndentSpace(indent+1)); 1463 1464 for (a=0; a<NA; a++) { 1465 if (a != 0) { 1466 if ((a&3) == 0) { 1467 fprintf(fp, ",\n%s", getIndentSpace(indent+1)); 1468 } else { 1469 fprintf(fp, ", "); 1470 } 1471 } 1472 fprintf(fp, "0x%08x", idents[a]); 1473 } 1474 1475 fprintf(fp, "\n%s};\n", indentStr); 1476 1477 for (a=0; a<NA; a++) { 1478 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 1479 if (pos >= 0) { 1480 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 1481 if (!publicFlags.itemAt(a) && !includePrivate) { 1482 continue; 1483 } 1484 String8 name8(sym.name); 1485 String16 comment(sym.comment); 1486 String16 typeComment; 1487 if (comment.size() <= 0) { 1488 comment = getAttributeComment(assets, name8, &typeComment); 1489 } else { 1490 getAttributeComment(assets, name8, &typeComment); 1491 } 1492 String16 name(name8); 1493 if (fixupSymbol(&name) != NO_ERROR) { 1494 hasErrors = true; 1495 } 1496 1497 uint32_t typeSpecFlags = 0; 1498 String16 name16(sym.name); 1499 assets->getIncludedResources().identifierForName( 1500 name16.string(), name16.size(), 1501 attr16.string(), attr16.size(), 1502 package16.string(), package16.size(), &typeSpecFlags); 1503 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(), 1504 // String8(attr16).string(), String8(name16).string(), typeSpecFlags); 1505 const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 1506 1507 bool deprecated = false; 1508 1509 fprintf(fp, "%s/**\n", indentStr); 1510 if (comment.size() > 0) { 1511 String8 cmt(comment); 1512 fprintf(fp, "%s <p>\n%s @attr description\n", indentStr, indentStr); 1513 fprintf(fp, "%s %s\n", indentStr, cmt.string()); 1514 if (strstr(cmt.string(), "@deprecated") != NULL) { 1515 deprecated = true; 1516 } 1517 } else { 1518 fprintf(fp, 1519 "%s <p>This symbol is the offset where the {@link %s.R.attr#%s}\n" 1520 "%s attribute's value can be found in the {@link #%s} array.\n", 1521 indentStr, 1522 pub ? assets->getPackage().string() 1523 : assets->getSymbolsPrivatePackage().string(), 1524 String8(name).string(), 1525 indentStr, nclassName.string()); 1526 } 1527 if (typeComment.size() > 0) { 1528 String8 cmt(typeComment); 1529 fprintf(fp, "\n\n%s %s\n", indentStr, cmt.string()); 1530 if (strstr(cmt.string(), "@deprecated") != NULL) { 1531 deprecated = true; 1532 } 1533 } 1534 if (comment.size() > 0) { 1535 if (pub) { 1536 fprintf(fp, 1537 "%s <p>This corresponds to the global attribute" 1538 "%s resource symbol {@link %s.R.attr#%s}.\n", 1539 indentStr, indentStr, 1540 assets->getPackage().string(), 1541 String8(name).string()); 1542 } else { 1543 fprintf(fp, 1544 "%s <p>This is a private symbol.\n", indentStr); 1545 } 1546 } 1547 fprintf(fp, "%s @attr name %s:%s\n", indentStr, 1548 "android", String8(name).string()); 1549 fprintf(fp, "%s*/\n", indentStr); 1550 if (deprecated) { 1551 fprintf(fp, "%s@Deprecated\n", indentStr); 1552 } 1553 fprintf(fp, 1554 "%spublic static final int %s_%s = %d;\n", 1555 indentStr, nclassName.string(), 1556 String8(name).string(), (int)pos); 1557 } 1558 } 1559 } 1560 1561 indent--; 1562 fprintf(fp, "%s};\n", getIndentSpace(indent)); 1563 return hasErrors ? UNKNOWN_ERROR : NO_ERROR; 1564} 1565 1566static status_t writeSymbolClass( 1567 FILE* fp, const sp<AaptAssets>& assets, bool includePrivate, 1568 const sp<AaptSymbols>& symbols, const String8& className, int indent) 1569{ 1570 fprintf(fp, "%spublic %sfinal class %s {\n", 1571 getIndentSpace(indent), 1572 indent != 0 ? "static " : "", className.string()); 1573 indent++; 1574 1575 size_t i; 1576 status_t err = NO_ERROR; 1577 1578 size_t N = symbols->getSymbols().size(); 1579 for (i=0; i<N; i++) { 1580 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 1581 if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) { 1582 continue; 1583 } 1584 if (!includePrivate && !sym.isPublic) { 1585 continue; 1586 } 1587 String16 name(sym.name); 1588 String8 realName(name); 1589 if (fixupSymbol(&name) != NO_ERROR) { 1590 return UNKNOWN_ERROR; 1591 } 1592 String16 comment(sym.comment); 1593 bool haveComment = false; 1594 bool deprecated = false; 1595 if (comment.size() > 0) { 1596 haveComment = true; 1597 String8 cmt(comment); 1598 fprintf(fp, 1599 "%s/** %s\n", 1600 getIndentSpace(indent), cmt.string()); 1601 if (strstr(cmt.string(), "@deprecated") != NULL) { 1602 deprecated = true; 1603 } 1604 } else if (sym.isPublic && !includePrivate) { 1605 sym.sourcePos.warning("No comment for public symbol %s:%s/%s", 1606 assets->getPackage().string(), className.string(), 1607 String8(sym.name).string()); 1608 } 1609 String16 typeComment(sym.typeComment); 1610 if (typeComment.size() > 0) { 1611 String8 cmt(typeComment); 1612 if (!haveComment) { 1613 haveComment = true; 1614 fprintf(fp, 1615 "%s/** %s\n", getIndentSpace(indent), cmt.string()); 1616 } else { 1617 fprintf(fp, 1618 "%s %s\n", getIndentSpace(indent), cmt.string()); 1619 } 1620 if (strstr(cmt.string(), "@deprecated") != NULL) { 1621 deprecated = true; 1622 } 1623 } 1624 if (haveComment) { 1625 fprintf(fp,"%s */\n", getIndentSpace(indent)); 1626 } 1627 if (deprecated) { 1628 fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent)); 1629 } 1630 fprintf(fp, "%spublic static final int %s=0x%08x;\n", 1631 getIndentSpace(indent), 1632 String8(name).string(), (int)sym.int32Val); 1633 } 1634 1635 for (i=0; i<N; i++) { 1636 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 1637 if (sym.typeCode != AaptSymbolEntry::TYPE_STRING) { 1638 continue; 1639 } 1640 if (!includePrivate && !sym.isPublic) { 1641 continue; 1642 } 1643 String16 name(sym.name); 1644 if (fixupSymbol(&name) != NO_ERROR) { 1645 return UNKNOWN_ERROR; 1646 } 1647 String16 comment(sym.comment); 1648 bool deprecated = false; 1649 if (comment.size() > 0) { 1650 String8 cmt(comment); 1651 fprintf(fp, 1652 "%s/** %s\n" 1653 "%s */\n", 1654 getIndentSpace(indent), cmt.string(), 1655 getIndentSpace(indent)); 1656 if (strstr(cmt.string(), "@deprecated") != NULL) { 1657 deprecated = true; 1658 } 1659 } else if (sym.isPublic && !includePrivate) { 1660 sym.sourcePos.warning("No comment for public symbol %s:%s/%s", 1661 assets->getPackage().string(), className.string(), 1662 String8(sym.name).string()); 1663 } 1664 if (deprecated) { 1665 fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent)); 1666 } 1667 fprintf(fp, "%spublic static final String %s=\"%s\";\n", 1668 getIndentSpace(indent), 1669 String8(name).string(), sym.stringVal.string()); 1670 } 1671 1672 sp<AaptSymbols> styleableSymbols; 1673 1674 N = symbols->getNestedSymbols().size(); 1675 for (i=0; i<N; i++) { 1676 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 1677 String8 nclassName(symbols->getNestedSymbols().keyAt(i)); 1678 if (nclassName == "styleable") { 1679 styleableSymbols = nsymbols; 1680 } else { 1681 err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent); 1682 } 1683 if (err != NO_ERROR) { 1684 return err; 1685 } 1686 } 1687 1688 if (styleableSymbols != NULL) { 1689 err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate); 1690 if (err != NO_ERROR) { 1691 return err; 1692 } 1693 } 1694 1695 indent--; 1696 fprintf(fp, "%s}\n", getIndentSpace(indent)); 1697 return NO_ERROR; 1698} 1699 1700status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets, 1701 const String8& package, bool includePrivate) 1702{ 1703 if (!bundle->getRClassDir()) { 1704 return NO_ERROR; 1705 } 1706 1707 const size_t N = assets->getSymbols().size(); 1708 for (size_t i=0; i<N; i++) { 1709 sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i); 1710 String8 className(assets->getSymbols().keyAt(i)); 1711 String8 dest(bundle->getRClassDir()); 1712 if (bundle->getMakePackageDirs()) { 1713 String8 pkg(package); 1714 const char* last = pkg.string(); 1715 const char* s = last-1; 1716 do { 1717 s++; 1718 if (s > last && (*s == '.' || *s == 0)) { 1719 String8 part(last, s-last); 1720 dest.appendPath(part); 1721#ifdef HAVE_MS_C_RUNTIME 1722 _mkdir(dest.string()); 1723#else 1724 mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP); 1725#endif 1726 last = s+1; 1727 } 1728 } while (*s); 1729 } 1730 dest.appendPath(className); 1731 dest.append(".java"); 1732 FILE* fp = fopen(dest.string(), "w+"); 1733 if (fp == NULL) { 1734 fprintf(stderr, "ERROR: Unable to open class file %s: %s\n", 1735 dest.string(), strerror(errno)); 1736 return UNKNOWN_ERROR; 1737 } 1738 if (bundle->getVerbose()) { 1739 printf(" Writing symbols for class %s.\n", className.string()); 1740 } 1741 1742 fprintf(fp, 1743 "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n" 1744 " *\n" 1745 " * This class was automatically generated by the\n" 1746 " * aapt tool from the resource data it found. It\n" 1747 " * should not be modified by hand.\n" 1748 " */\n" 1749 "\n" 1750 "package %s;\n\n", package.string()); 1751 1752 status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, className, 0); 1753 if (err != NO_ERROR) { 1754 return err; 1755 } 1756 fclose(fp); 1757 } 1758 1759 return NO_ERROR; 1760} 1761 1762 1763 1764class ProguardKeepSet 1765{ 1766public: 1767 // { rule --> { file locations } } 1768 KeyedVector<String8, SortedVector<String8> > rules; 1769 1770 void add(const String8& rule, const String8& where); 1771}; 1772 1773void ProguardKeepSet::add(const String8& rule, const String8& where) 1774{ 1775 ssize_t index = rules.indexOfKey(rule); 1776 if (index < 0) { 1777 index = rules.add(rule, SortedVector<String8>()); 1778 } 1779 rules.editValueAt(index).add(where); 1780} 1781 1782void 1783addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName, 1784 const char* pkg, const String8& srcName, int line) 1785{ 1786 String8 className(inClassName); 1787 if (pkg != NULL) { 1788 // asdf --> package.asdf 1789 // .asdf .a.b --> package.asdf package.a.b 1790 // asdf.adsf --> asdf.asdf 1791 const char* p = className.string(); 1792 const char* q = strchr(p, '.'); 1793 if (p == q) { 1794 className = pkg; 1795 className.append(inClassName); 1796 } else if (q == NULL) { 1797 className = pkg; 1798 className.append("."); 1799 className.append(inClassName); 1800 } 1801 } 1802 1803 String8 rule("-keep class "); 1804 rule += className; 1805 rule += " { <init>(...); }"; 1806 1807 String8 location("view "); 1808 location += srcName; 1809 char lineno[20]; 1810 sprintf(lineno, ":%d", line); 1811 location += lineno; 1812 1813 keep->add(rule, location); 1814} 1815 1816status_t 1817writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets) 1818{ 1819 status_t err; 1820 ResXMLTree tree; 1821 size_t len; 1822 ResXMLTree::event_code_t code; 1823 int depth = 0; 1824 bool inApplication = false; 1825 String8 error; 1826 sp<AaptGroup> assGroup; 1827 sp<AaptFile> assFile; 1828 String8 pkg; 1829 1830 // First, look for a package file to parse. This is required to 1831 // be able to generate the resource information. 1832 assGroup = assets->getFiles().valueFor(String8("AndroidManifest.xml")); 1833 if (assGroup == NULL) { 1834 fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n"); 1835 return -1; 1836 } 1837 1838 if (assGroup->getFiles().size() != 1) { 1839 fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n", 1840 assGroup->getFiles().valueAt(0)->getPrintableSource().string()); 1841 } 1842 1843 assFile = assGroup->getFiles().valueAt(0); 1844 1845 err = parseXMLResource(assFile, &tree); 1846 if (err != NO_ERROR) { 1847 return err; 1848 } 1849 1850 tree.restart(); 1851 1852 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 1853 if (code == ResXMLTree::END_TAG) { 1854 if (/* name == "Application" && */ depth == 2) { 1855 inApplication = false; 1856 } 1857 depth--; 1858 continue; 1859 } 1860 if (code != ResXMLTree::START_TAG) { 1861 continue; 1862 } 1863 depth++; 1864 String8 tag(tree.getElementName(&len)); 1865 // printf("Depth %d tag %s\n", depth, tag.string()); 1866 bool keepTag = false; 1867 if (depth == 1) { 1868 if (tag != "manifest") { 1869 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 1870 return -1; 1871 } 1872 pkg = getAttribute(tree, NULL, "package", NULL); 1873 } else if (depth == 2) { 1874 if (tag == "application") { 1875 inApplication = true; 1876 keepTag = true; 1877 1878 String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android", 1879 "backupAgent", &error); 1880 if (agent.length() > 0) { 1881 addProguardKeepRule(keep, agent, pkg.string(), 1882 assFile->getPrintableSource(), tree.getLineNumber()); 1883 } 1884 } else if (tag == "instrumentation") { 1885 keepTag = true; 1886 } 1887 } 1888 if (!keepTag && inApplication && depth == 3) { 1889 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") { 1890 keepTag = true; 1891 } 1892 } 1893 if (keepTag) { 1894 String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android", 1895 "name", &error); 1896 if (error != "") { 1897 fprintf(stderr, "ERROR: %s\n", error.string()); 1898 return -1; 1899 } 1900 if (name.length() > 0) { 1901 addProguardKeepRule(keep, name, pkg.string(), 1902 assFile->getPrintableSource(), tree.getLineNumber()); 1903 } 1904 } 1905 } 1906 1907 return NO_ERROR; 1908} 1909 1910status_t 1911writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile, 1912 const char* startTag, const char* altTag) 1913{ 1914 status_t err; 1915 ResXMLTree tree; 1916 size_t len; 1917 ResXMLTree::event_code_t code; 1918 1919 err = parseXMLResource(layoutFile, &tree); 1920 if (err != NO_ERROR) { 1921 return err; 1922 } 1923 1924 tree.restart(); 1925 1926 if (startTag != NULL) { 1927 bool haveStart = false; 1928 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 1929 if (code != ResXMLTree::START_TAG) { 1930 continue; 1931 } 1932 String8 tag(tree.getElementName(&len)); 1933 if (tag == startTag) { 1934 haveStart = true; 1935 } 1936 break; 1937 } 1938 if (!haveStart) { 1939 return NO_ERROR; 1940 } 1941 } 1942 1943 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 1944 if (code != ResXMLTree::START_TAG) { 1945 continue; 1946 } 1947 String8 tag(tree.getElementName(&len)); 1948 1949 // If there is no '.', we'll assume that it's one of the built in names. 1950 if (strchr(tag.string(), '.')) { 1951 addProguardKeepRule(keep, tag, NULL, 1952 layoutFile->getPrintableSource(), tree.getLineNumber()); 1953 } else if (altTag != NULL && tag == altTag) { 1954 ssize_t classIndex = tree.indexOfAttribute(NULL, "class"); 1955 if (classIndex < 0) { 1956 fprintf(stderr, "%s:%d: <view> does not have class attribute.\n", 1957 layoutFile->getPrintableSource().string(), tree.getLineNumber()); 1958 } else { 1959 size_t len; 1960 addProguardKeepRule(keep, 1961 String8(tree.getAttributeStringValue(classIndex, &len)), NULL, 1962 layoutFile->getPrintableSource(), tree.getLineNumber()); 1963 } 1964 } 1965 } 1966 1967 return NO_ERROR; 1968} 1969 1970status_t 1971writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets) 1972{ 1973 status_t err; 1974 const Vector<sp<AaptDir> >& dirs = assets->resDirs(); 1975 const size_t K = dirs.size(); 1976 for (size_t k=0; k<K; k++) { 1977 const sp<AaptDir>& d = dirs.itemAt(k); 1978 const String8& dirName = d->getLeaf(); 1979 const char* startTag = NULL; 1980 const char* altTag = NULL; 1981 if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) { 1982 altTag = "view"; 1983 } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) { 1984 startTag = "PreferenceScreen"; 1985 } else { 1986 continue; 1987 } 1988 1989 const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles(); 1990 const size_t N = groups.size(); 1991 for (size_t i=0; i<N; i++) { 1992 const sp<AaptGroup>& group = groups.valueAt(i); 1993 const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles(); 1994 const size_t M = files.size(); 1995 for (size_t j=0; j<M; j++) { 1996 err = writeProguardForXml(keep, files.valueAt(j), startTag, altTag); 1997 if (err < 0) { 1998 return err; 1999 } 2000 } 2001 } 2002 } 2003 return NO_ERROR; 2004} 2005 2006status_t 2007writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets) 2008{ 2009 status_t err = -1; 2010 2011 if (!bundle->getProguardFile()) { 2012 return NO_ERROR; 2013 } 2014 2015 ProguardKeepSet keep; 2016 2017 err = writeProguardForAndroidManifest(&keep, assets); 2018 if (err < 0) { 2019 return err; 2020 } 2021 2022 err = writeProguardForLayouts(&keep, assets); 2023 if (err < 0) { 2024 return err; 2025 } 2026 2027 FILE* fp = fopen(bundle->getProguardFile(), "w+"); 2028 if (fp == NULL) { 2029 fprintf(stderr, "ERROR: Unable to open class file %s: %s\n", 2030 bundle->getProguardFile(), strerror(errno)); 2031 return UNKNOWN_ERROR; 2032 } 2033 2034 const KeyedVector<String8, SortedVector<String8> >& rules = keep.rules; 2035 const size_t N = rules.size(); 2036 for (size_t i=0; i<N; i++) { 2037 const SortedVector<String8>& locations = rules.valueAt(i); 2038 const size_t M = locations.size(); 2039 for (size_t j=0; j<M; j++) { 2040 fprintf(fp, "# %s\n", locations.itemAt(j).string()); 2041 } 2042 fprintf(fp, "%s\n\n", rules.keyAt(i).string()); 2043 } 2044 fclose(fp); 2045 2046 return err; 2047} 2048