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