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