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