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