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