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