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