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