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 if (bundle->getPlatformBuildVersionCode() != "") { 922 if (!addTagAttribute(root, "", "platformBuildVersionCode", 923 bundle->getPlatformBuildVersionCode(), errorOnFailedInsert, true)) { 924 return UNKNOWN_ERROR; 925 } 926 } 927 928 if (bundle->getPlatformBuildVersionName() != "") { 929 if (!addTagAttribute(root, "", "platformBuildVersionName", 930 bundle->getPlatformBuildVersionName(), errorOnFailedInsert, true)) { 931 return UNKNOWN_ERROR; 932 } 933 } 934 935 if (bundle->getDebugMode()) { 936 sp<XMLNode> application = root->getChildElement(String16(), String16("application")); 937 if (application != NULL) { 938 if (!addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true", 939 errorOnFailedInsert)) { 940 return UNKNOWN_ERROR; 941 } 942 } 943 } 944 945 // Deal with manifest package name overrides 946 const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride(); 947 if (manifestPackageNameOverride != NULL) { 948 // Update the actual package name 949 XMLNode::attribute_entry* attr = root->editAttribute(String16(), String16("package")); 950 if (attr == NULL) { 951 fprintf(stderr, "package name is required with --rename-manifest-package.\n"); 952 return UNKNOWN_ERROR; 953 } 954 String8 origPackage(attr->string); 955 attr->string.setTo(String16(manifestPackageNameOverride)); 956 if (kIsDebug) { 957 printf("Overriding package '%s' to be '%s'\n", origPackage.string(), 958 manifestPackageNameOverride); 959 } 960 961 // Make class names fully qualified 962 sp<XMLNode> application = root->getChildElement(String16(), String16("application")); 963 if (application != NULL) { 964 fullyQualifyClassName(origPackage, application, String16("name")); 965 fullyQualifyClassName(origPackage, application, String16("backupAgent")); 966 967 Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(application->getChildren()); 968 for (size_t i = 0; i < children.size(); i++) { 969 sp<XMLNode> child = children.editItemAt(i); 970 String8 tag(child->getElementName()); 971 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") { 972 fullyQualifyClassName(origPackage, child, String16("name")); 973 } else if (tag == "activity-alias") { 974 fullyQualifyClassName(origPackage, child, String16("name")); 975 fullyQualifyClassName(origPackage, child, String16("targetActivity")); 976 } 977 } 978 } 979 } 980 981 // Deal with manifest package name overrides 982 const char* instrumentationPackageNameOverride = bundle->getInstrumentationPackageNameOverride(); 983 if (instrumentationPackageNameOverride != NULL) { 984 // Fix up instrumentation targets. 985 Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(root->getChildren()); 986 for (size_t i = 0; i < children.size(); i++) { 987 sp<XMLNode> child = children.editItemAt(i); 988 String8 tag(child->getElementName()); 989 if (tag == "instrumentation") { 990 XMLNode::attribute_entry* attr = child->editAttribute( 991 String16(RESOURCES_ANDROID_NAMESPACE), String16("targetPackage")); 992 if (attr != NULL) { 993 attr->string.setTo(String16(instrumentationPackageNameOverride)); 994 } 995 } 996 } 997 } 998 999 sp<XMLNode> application = root->getChildElement(String16(), String16("application")); 1000 if (application != NULL) { 1001 XMLNode::attribute_entry* icon_attr = application->editAttribute( 1002 String16(RESOURCES_ANDROID_NAMESPACE), String16("icon")); 1003 if (icon_attr != NULL) { 1004 XMLNode::attribute_entry* round_icon_attr = application->editAttribute( 1005 String16(RESOURCES_ANDROID_NAMESPACE), String16("roundIcon")); 1006 if (round_icon_attr != NULL) { 1007 massageRoundIconSupport(icon_attr->string, round_icon_attr->string, table); 1008 } 1009 } 1010 } 1011 1012 // Generate split name if feature is present. 1013 const XMLNode::attribute_entry* attr = root->getAttribute(String16(), String16("featureName")); 1014 if (attr != NULL) { 1015 String16 splitName("feature_"); 1016 splitName.append(attr->string); 1017 status_t err = root->addAttribute(String16(), String16("split"), splitName); 1018 if (err != NO_ERROR) { 1019 ALOGE("Failed to insert split name into AndroidManifest.xml"); 1020 return err; 1021 } 1022 } 1023 1024 return NO_ERROR; 1025 } 1026 1027 static int32_t getPlatformAssetCookie(const AssetManager& assets) { 1028 // Find the system package (0x01). AAPT always generates attributes 1029 // with the type 0x01, so we're looking for the first attribute 1030 // resource in the system package. 1031 const ResTable& table = assets.getResources(true); 1032 Res_value val; 1033 ssize_t idx = table.getResource(0x01010000, &val, true); 1034 if (idx != NO_ERROR) { 1035 // Try as a bag. 1036 const ResTable::bag_entry* entry; 1037 ssize_t cnt = table.lockBag(0x01010000, &entry); 1038 if (cnt >= 0) { 1039 idx = entry->stringBlock; 1040 } 1041 table.unlockBag(entry); 1042 } 1043 1044 if (idx < 0) { 1045 return 0; 1046 } 1047 return table.getTableCookie(idx); 1048 } 1049 1050 enum { 1051 VERSION_CODE_ATTR = 0x0101021b, 1052 VERSION_NAME_ATTR = 0x0101021c, 1053 }; 1054 1055 static ssize_t extractPlatformBuildVersion(ResXMLTree& tree, Bundle* bundle) { 1056 size_t len; 1057 ResXMLTree::event_code_t code; 1058 while ((code = tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 1059 if (code != ResXMLTree::START_TAG) { 1060 continue; 1061 } 1062 1063 const char16_t* ctag16 = tree.getElementName(&len); 1064 if (ctag16 == NULL) { 1065 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n"); 1066 return UNKNOWN_ERROR; 1067 } 1068 1069 String8 tag(ctag16, len); 1070 if (tag != "manifest") { 1071 continue; 1072 } 1073 1074 String8 error; 1075 int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR, &error); 1076 if (error != "") { 1077 fprintf(stderr, "ERROR: failed to get platform version code\n"); 1078 return UNKNOWN_ERROR; 1079 } 1080 1081 if (versionCode >= 0 && bundle->getPlatformBuildVersionCode() == "") { 1082 bundle->setPlatformBuildVersionCode(String8::format("%d", versionCode)); 1083 } 1084 1085 String8 versionName = AaptXml::getAttribute(tree, VERSION_NAME_ATTR, &error); 1086 if (error != "") { 1087 fprintf(stderr, "ERROR: failed to get platform version name\n"); 1088 return UNKNOWN_ERROR; 1089 } 1090 1091 if (versionName != "" && bundle->getPlatformBuildVersionName() == "") { 1092 bundle->setPlatformBuildVersionName(versionName); 1093 } 1094 return NO_ERROR; 1095 } 1096 1097 fprintf(stderr, "ERROR: no <manifest> tag found in platform AndroidManifest.xml\n"); 1098 return UNKNOWN_ERROR; 1099 } 1100 1101 static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) { 1102 int32_t cookie = getPlatformAssetCookie(assets); 1103 if (cookie == 0) { 1104 // No platform was loaded. 1105 return NO_ERROR; 1106 } 1107 1108 Asset* asset = assets.openNonAsset(cookie, "AndroidManifest.xml", Asset::ACCESS_STREAMING); 1109 if (asset == NULL) { 1110 fprintf(stderr, "ERROR: Platform AndroidManifest.xml not found\n"); 1111 return UNKNOWN_ERROR; 1112 } 1113 1114 ssize_t result = NO_ERROR; 1115 1116 // Create a new scope so that ResXMLTree is destroyed before we delete the memory over 1117 // which it iterates (asset). 1118 { 1119 ResXMLTree tree; 1120 if (tree.setTo(asset->getBuffer(true), asset->getLength()) != NO_ERROR) { 1121 fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n"); 1122 result = UNKNOWN_ERROR; 1123 } else { 1124 result = extractPlatformBuildVersion(tree, bundle); 1125 } 1126 } 1127 1128 delete asset; 1129 return result; 1130 } 1131 1132 #define ASSIGN_IT(n) \ 1133 do { \ 1134 ssize_t index = resources->indexOfKey(String8(#n)); \ 1135 if (index >= 0) { \ 1136 n ## s = resources->valueAt(index); \ 1137 } \ 1138 } while (0) 1139 1140 status_t updatePreProcessedCache(Bundle* bundle) 1141 { 1142 #if BENCHMARK 1143 fprintf(stdout, "BENCHMARK: Starting PNG PreProcessing \n"); 1144 long startPNGTime = clock(); 1145 #endif /* BENCHMARK */ 1146 1147 String8 source(bundle->getResourceSourceDirs()[0]); 1148 String8 dest(bundle->getCrunchedOutputDir()); 1149 1150 FileFinder* ff = new SystemFileFinder(); 1151 CrunchCache cc(source,dest,ff); 1152 1153 CacheUpdater* cu = new SystemCacheUpdater(bundle); 1154 size_t numFiles = cc.crunch(cu); 1155 1156 if (bundle->getVerbose()) 1157 fprintf(stdout, "Crunched %d PNG files to update cache\n", (int)numFiles); 1158 1159 delete ff; 1160 delete cu; 1161 1162 #if BENCHMARK 1163 fprintf(stdout, "BENCHMARK: End PNG PreProcessing. Time Elapsed: %f ms \n" 1164 ,(clock() - startPNGTime)/1000.0); 1165 #endif /* BENCHMARK */ 1166 return 0; 1167 } 1168 1169 status_t generateAndroidManifestForSplit(Bundle* bundle, const sp<AaptAssets>& assets, 1170 const sp<ApkSplit>& split, sp<AaptFile>& outFile, ResourceTable* table) { 1171 const String8 filename("AndroidManifest.xml"); 1172 const String16 androidPrefix("android"); 1173 const String16 androidNSUri("http://schemas.android.com/apk/res/android"); 1174 sp<XMLNode> root = XMLNode::newNamespace(filename, androidPrefix, androidNSUri); 1175 1176 // Build the <manifest> tag 1177 sp<XMLNode> manifest = XMLNode::newElement(filename, String16(), String16("manifest")); 1178 1179 // Add the 'package' attribute which is set to the package name. 1180 const char* packageName = assets->getPackage(); 1181 const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride(); 1182 if (manifestPackageNameOverride != NULL) { 1183 packageName = manifestPackageNameOverride; 1184 } 1185 manifest->addAttribute(String16(), String16("package"), String16(packageName)); 1186 1187 // Add the 'versionCode' attribute which is set to the original version code. 1188 if (!addTagAttribute(manifest, RESOURCES_ANDROID_NAMESPACE, "versionCode", 1189 bundle->getVersionCode(), true, true)) { 1190 return UNKNOWN_ERROR; 1191 } 1192 1193 // Add the 'revisionCode' attribute, which is set to the original revisionCode. 1194 if (bundle->getRevisionCode().size() > 0) { 1195 if (!addTagAttribute(manifest, RESOURCES_ANDROID_NAMESPACE, "revisionCode", 1196 bundle->getRevisionCode().string(), true, true)) { 1197 return UNKNOWN_ERROR; 1198 } 1199 } 1200 1201 // Add the 'split' attribute which describes the configurations included. 1202 String8 splitName("config."); 1203 splitName.append(split->getPackageSafeName()); 1204 manifest->addAttribute(String16(), String16("split"), String16(splitName)); 1205 1206 // Build an empty <application> tag (required). 1207 sp<XMLNode> app = XMLNode::newElement(filename, String16(), String16("application")); 1208 1209 // Add the 'hasCode' attribute which is never true for resource splits. 1210 if (!addTagAttribute(app, RESOURCES_ANDROID_NAMESPACE, "hasCode", 1211 "false", true, true)) { 1212 return UNKNOWN_ERROR; 1213 } 1214 1215 manifest->addChild(app); 1216 root->addChild(manifest); 1217 1218 int err = compileXmlFile(bundle, assets, String16(), root, outFile, table); 1219 if (err < NO_ERROR) { 1220 return err; 1221 } 1222 outFile->setCompressionMethod(ZipEntry::kCompressDeflated); 1223 return NO_ERROR; 1224 } 1225 1226 status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder) 1227 { 1228 // First, look for a package file to parse. This is required to 1229 // be able to generate the resource information. 1230 sp<AaptGroup> androidManifestFile = 1231 assets->getFiles().valueFor(String8("AndroidManifest.xml")); 1232 if (androidManifestFile == NULL) { 1233 fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n"); 1234 return UNKNOWN_ERROR; 1235 } 1236 1237 status_t err = parsePackage(bundle, assets, androidManifestFile); 1238 if (err != NO_ERROR) { 1239 return err; 1240 } 1241 1242 if (kIsDebug) { 1243 printf("Creating resources for package %s\n", assets->getPackage().string()); 1244 } 1245 1246 // Set the private symbols package if it was declared. 1247 // This can also be declared in XML as <private-symbols name="package" /> 1248 if (bundle->getPrivateSymbolsPackage().size() != 0) { 1249 assets->setSymbolsPrivatePackage(bundle->getPrivateSymbolsPackage()); 1250 } 1251 1252 ResourceTable::PackageType packageType = ResourceTable::App; 1253 if (bundle->getBuildSharedLibrary()) { 1254 packageType = ResourceTable::SharedLibrary; 1255 } else if (bundle->getExtending()) { 1256 packageType = ResourceTable::System; 1257 } else if (!bundle->getFeatureOfPackage().isEmpty()) { 1258 packageType = ResourceTable::AppFeature; 1259 } 1260 1261 ResourceTable table(bundle, String16(assets->getPackage()), packageType); 1262 err = table.addIncludedResources(bundle, assets); 1263 if (err != NO_ERROR) { 1264 return err; 1265 } 1266 1267 if (kIsDebug) { 1268 printf("Found %d included resource packages\n", (int)table.size()); 1269 } 1270 1271 // Standard flags for compiled XML and optional UTF-8 encoding 1272 int xmlFlags = XML_COMPILE_STANDARD_RESOURCE; 1273 1274 /* Only enable UTF-8 if the caller of aapt didn't specifically 1275 * request UTF-16 encoding and the parameters of this package 1276 * allow UTF-8 to be used. 1277 */ 1278 if (!bundle->getUTF16StringsOption()) { 1279 xmlFlags |= XML_COMPILE_UTF8; 1280 } 1281 1282 // -------------------------------------------------------------- 1283 // First, gather all resource information. 1284 // -------------------------------------------------------------- 1285 1286 // resType -> leafName -> group 1287 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 1288 new KeyedVector<String8, sp<ResourceTypeSet> >; 1289 collect_files(assets, resources); 1290 1291 sp<ResourceTypeSet> drawables; 1292 sp<ResourceTypeSet> layouts; 1293 sp<ResourceTypeSet> anims; 1294 sp<ResourceTypeSet> animators; 1295 sp<ResourceTypeSet> interpolators; 1296 sp<ResourceTypeSet> transitions; 1297 sp<ResourceTypeSet> xmls; 1298 sp<ResourceTypeSet> raws; 1299 sp<ResourceTypeSet> colors; 1300 sp<ResourceTypeSet> menus; 1301 sp<ResourceTypeSet> mipmaps; 1302 sp<ResourceTypeSet> fonts; 1303 1304 ASSIGN_IT(drawable); 1305 ASSIGN_IT(layout); 1306 ASSIGN_IT(anim); 1307 ASSIGN_IT(animator); 1308 ASSIGN_IT(interpolator); 1309 ASSIGN_IT(transition); 1310 ASSIGN_IT(xml); 1311 ASSIGN_IT(raw); 1312 ASSIGN_IT(color); 1313 ASSIGN_IT(menu); 1314 ASSIGN_IT(mipmap); 1315 ASSIGN_IT(font); 1316 1317 assets->setResources(resources); 1318 // now go through any resource overlays and collect their files 1319 sp<AaptAssets> current = assets->getOverlay(); 1320 while(current.get()) { 1321 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 1322 new KeyedVector<String8, sp<ResourceTypeSet> >; 1323 current->setResources(resources); 1324 collect_files(current, resources); 1325 current = current->getOverlay(); 1326 } 1327 // apply the overlay files to the base set 1328 if (!applyFileOverlay(bundle, assets, &drawables, "drawable") || 1329 !applyFileOverlay(bundle, assets, &layouts, "layout") || 1330 !applyFileOverlay(bundle, assets, &anims, "anim") || 1331 !applyFileOverlay(bundle, assets, &animators, "animator") || 1332 !applyFileOverlay(bundle, assets, &interpolators, "interpolator") || 1333 !applyFileOverlay(bundle, assets, &transitions, "transition") || 1334 !applyFileOverlay(bundle, assets, &xmls, "xml") || 1335 !applyFileOverlay(bundle, assets, &raws, "raw") || 1336 !applyFileOverlay(bundle, assets, &colors, "color") || 1337 !applyFileOverlay(bundle, assets, &menus, "menu") || 1338 !applyFileOverlay(bundle, assets, &fonts, "font") || 1339 !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) { 1340 return UNKNOWN_ERROR; 1341 } 1342 1343 bool hasErrors = false; 1344 1345 if (drawables != NULL) { 1346 if (bundle->getOutputAPKFile() != NULL) { 1347 err = preProcessImages(bundle, assets, drawables, "drawable"); 1348 } 1349 if (err == NO_ERROR) { 1350 err = makeFileResources(bundle, assets, &table, drawables, "drawable"); 1351 if (err != NO_ERROR) { 1352 hasErrors = true; 1353 } 1354 } else { 1355 hasErrors = true; 1356 } 1357 } 1358 1359 if (mipmaps != NULL) { 1360 if (bundle->getOutputAPKFile() != NULL) { 1361 err = preProcessImages(bundle, assets, mipmaps, "mipmap"); 1362 } 1363 if (err == NO_ERROR) { 1364 err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap"); 1365 if (err != NO_ERROR) { 1366 hasErrors = true; 1367 } 1368 } else { 1369 hasErrors = true; 1370 } 1371 } 1372 1373 if (fonts != NULL) { 1374 err = makeFileResources(bundle, assets, &table, fonts, "font"); 1375 if (err != NO_ERROR) { 1376 hasErrors = true; 1377 } 1378 } 1379 1380 if (layouts != NULL) { 1381 err = makeFileResources(bundle, assets, &table, layouts, "layout"); 1382 if (err != NO_ERROR) { 1383 hasErrors = true; 1384 } 1385 } 1386 1387 if (anims != NULL) { 1388 err = makeFileResources(bundle, assets, &table, anims, "anim"); 1389 if (err != NO_ERROR) { 1390 hasErrors = true; 1391 } 1392 } 1393 1394 if (animators != NULL) { 1395 err = makeFileResources(bundle, assets, &table, animators, "animator"); 1396 if (err != NO_ERROR) { 1397 hasErrors = true; 1398 } 1399 } 1400 1401 if (transitions != NULL) { 1402 err = makeFileResources(bundle, assets, &table, transitions, "transition"); 1403 if (err != NO_ERROR) { 1404 hasErrors = true; 1405 } 1406 } 1407 1408 if (interpolators != NULL) { 1409 err = makeFileResources(bundle, assets, &table, interpolators, "interpolator"); 1410 if (err != NO_ERROR) { 1411 hasErrors = true; 1412 } 1413 } 1414 1415 if (xmls != NULL) { 1416 err = makeFileResources(bundle, assets, &table, xmls, "xml"); 1417 if (err != NO_ERROR) { 1418 hasErrors = true; 1419 } 1420 } 1421 1422 if (raws != NULL) { 1423 err = makeFileResources(bundle, assets, &table, raws, "raw"); 1424 if (err != NO_ERROR) { 1425 hasErrors = true; 1426 } 1427 } 1428 1429 // compile resources 1430 current = assets; 1431 while(current.get()) { 1432 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 1433 current->getResources(); 1434 1435 ssize_t index = resources->indexOfKey(String8("values")); 1436 if (index >= 0) { 1437 ResourceDirIterator it(resources->valueAt(index), String8("values")); 1438 ssize_t res; 1439 while ((res=it.next()) == NO_ERROR) { 1440 const sp<AaptFile>& file = it.getFile(); 1441 res = compileResourceFile(bundle, assets, file, it.getParams(), 1442 (current!=assets), &table); 1443 if (res != NO_ERROR) { 1444 hasErrors = true; 1445 } 1446 } 1447 } 1448 current = current->getOverlay(); 1449 } 1450 1451 if (colors != NULL) { 1452 err = makeFileResources(bundle, assets, &table, colors, "color"); 1453 if (err != NO_ERROR) { 1454 hasErrors = true; 1455 } 1456 } 1457 1458 if (menus != NULL) { 1459 err = makeFileResources(bundle, assets, &table, menus, "menu"); 1460 if (err != NO_ERROR) { 1461 hasErrors = true; 1462 } 1463 } 1464 1465 if (hasErrors) { 1466 return UNKNOWN_ERROR; 1467 } 1468 1469 // -------------------------------------------------------------------- 1470 // Assignment of resource IDs and initial generation of resource table. 1471 // -------------------------------------------------------------------- 1472 1473 if (table.hasResources()) { 1474 err = table.assignResourceIds(); 1475 if (err < NO_ERROR) { 1476 return err; 1477 } 1478 } 1479 1480 // -------------------------------------------------------------- 1481 // Finally, we can now we can compile XML files, which may reference 1482 // resources. 1483 // -------------------------------------------------------------- 1484 1485 if (layouts != NULL) { 1486 ResourceDirIterator it(layouts, String8("layout")); 1487 while ((err=it.next()) == NO_ERROR) { 1488 String8 src = it.getFile()->getPrintableSource(); 1489 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1490 it.getFile(), &table, xmlFlags); 1491 // Only verify IDs if there was no error and the file is non-empty. 1492 if (err == NO_ERROR && it.getFile()->hasData()) { 1493 ResXMLTree block; 1494 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); 1495 checkForIds(src, block); 1496 } else { 1497 hasErrors = true; 1498 } 1499 } 1500 1501 if (err < NO_ERROR) { 1502 hasErrors = true; 1503 } 1504 err = NO_ERROR; 1505 } 1506 1507 if (anims != NULL) { 1508 ResourceDirIterator it(anims, String8("anim")); 1509 while ((err=it.next()) == NO_ERROR) { 1510 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1511 it.getFile(), &table, xmlFlags); 1512 if (err != NO_ERROR) { 1513 hasErrors = true; 1514 } 1515 } 1516 1517 if (err < NO_ERROR) { 1518 hasErrors = true; 1519 } 1520 err = NO_ERROR; 1521 } 1522 1523 if (animators != NULL) { 1524 ResourceDirIterator it(animators, String8("animator")); 1525 while ((err=it.next()) == NO_ERROR) { 1526 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1527 it.getFile(), &table, xmlFlags); 1528 if (err != NO_ERROR) { 1529 hasErrors = true; 1530 } 1531 } 1532 1533 if (err < NO_ERROR) { 1534 hasErrors = true; 1535 } 1536 err = NO_ERROR; 1537 } 1538 1539 if (interpolators != NULL) { 1540 ResourceDirIterator it(interpolators, String8("interpolator")); 1541 while ((err=it.next()) == NO_ERROR) { 1542 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1543 it.getFile(), &table, xmlFlags); 1544 if (err != NO_ERROR) { 1545 hasErrors = true; 1546 } 1547 } 1548 1549 if (err < NO_ERROR) { 1550 hasErrors = true; 1551 } 1552 err = NO_ERROR; 1553 } 1554 1555 if (transitions != NULL) { 1556 ResourceDirIterator it(transitions, String8("transition")); 1557 while ((err=it.next()) == NO_ERROR) { 1558 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1559 it.getFile(), &table, xmlFlags); 1560 if (err != NO_ERROR) { 1561 hasErrors = true; 1562 } 1563 } 1564 1565 if (err < NO_ERROR) { 1566 hasErrors = true; 1567 } 1568 err = NO_ERROR; 1569 } 1570 1571 if (xmls != NULL) { 1572 ResourceDirIterator it(xmls, String8("xml")); 1573 while ((err=it.next()) == NO_ERROR) { 1574 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1575 it.getFile(), &table, xmlFlags); 1576 if (err != NO_ERROR) { 1577 hasErrors = true; 1578 } 1579 } 1580 1581 if (err < NO_ERROR) { 1582 hasErrors = true; 1583 } 1584 err = NO_ERROR; 1585 } 1586 1587 if (drawables != NULL) { 1588 ResourceDirIterator it(drawables, String8("drawable")); 1589 while ((err=it.next()) == NO_ERROR) { 1590 err = postProcessImage(bundle, assets, &table, it.getFile()); 1591 if (err != NO_ERROR) { 1592 hasErrors = true; 1593 } 1594 } 1595 1596 if (err < NO_ERROR) { 1597 hasErrors = true; 1598 } 1599 err = NO_ERROR; 1600 } 1601 1602 if (mipmaps != NULL) { 1603 ResourceDirIterator it(mipmaps, String8("mipmap")); 1604 while ((err=it.next()) == NO_ERROR) { 1605 err = postProcessImage(bundle, assets, &table, it.getFile()); 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 (colors != NULL) { 1618 ResourceDirIterator it(colors, String8("color")); 1619 while ((err=it.next()) == NO_ERROR) { 1620 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1621 it.getFile(), &table, xmlFlags); 1622 if (err != NO_ERROR) { 1623 hasErrors = true; 1624 } 1625 } 1626 1627 if (err < NO_ERROR) { 1628 hasErrors = true; 1629 } 1630 err = NO_ERROR; 1631 } 1632 1633 if (menus != NULL) { 1634 ResourceDirIterator it(menus, String8("menu")); 1635 while ((err=it.next()) == NO_ERROR) { 1636 String8 src = it.getFile()->getPrintableSource(); 1637 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1638 it.getFile(), &table, xmlFlags); 1639 if (err == NO_ERROR && it.getFile()->hasData()) { 1640 ResXMLTree block; 1641 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); 1642 checkForIds(src, block); 1643 } else { 1644 hasErrors = true; 1645 } 1646 } 1647 1648 if (err < NO_ERROR) { 1649 hasErrors = true; 1650 } 1651 err = NO_ERROR; 1652 } 1653 1654 if (fonts != NULL) { 1655 ResourceDirIterator it(fonts, String8("font")); 1656 while ((err=it.next()) == NO_ERROR) { 1657 // fonts can be resources other than xml. 1658 if (it.getFile()->getPath().getPathExtension() == ".xml") { 1659 String8 src = it.getFile()->getPrintableSource(); 1660 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), 1661 it.getFile(), &table, xmlFlags); 1662 if (err != NO_ERROR) { 1663 hasErrors = true; 1664 } 1665 } 1666 } 1667 1668 if (err < NO_ERROR) { 1669 hasErrors = true; 1670 } 1671 err = NO_ERROR; 1672 } 1673 1674 // Now compile any generated resources. 1675 std::queue<CompileResourceWorkItem>& workQueue = table.getWorkQueue(); 1676 while (!workQueue.empty()) { 1677 CompileResourceWorkItem& workItem = workQueue.front(); 1678 int xmlCompilationFlags = xmlFlags | XML_COMPILE_PARSE_VALUES 1679 | XML_COMPILE_ASSIGN_ATTRIBUTE_IDS; 1680 if (!workItem.needsCompiling) { 1681 xmlCompilationFlags &= ~XML_COMPILE_ASSIGN_ATTRIBUTE_IDS; 1682 xmlCompilationFlags &= ~XML_COMPILE_PARSE_VALUES; 1683 } 1684 err = compileXmlFile(bundle, assets, workItem.resourceName, workItem.xmlRoot, 1685 workItem.file, &table, xmlCompilationFlags); 1686 1687 if (err == NO_ERROR && workItem.file->hasData()) { 1688 assets->addResource(workItem.resPath.getPathLeaf(), 1689 workItem.resPath, 1690 workItem.file, 1691 workItem.file->getResourceType()); 1692 } else { 1693 hasErrors = true; 1694 } 1695 workQueue.pop(); 1696 } 1697 1698 if (table.validateLocalizations()) { 1699 hasErrors = true; 1700 } 1701 1702 if (hasErrors) { 1703 return UNKNOWN_ERROR; 1704 } 1705 1706 // If we're not overriding the platform build versions, 1707 // extract them from the platform APK. 1708 if (packageType != ResourceTable::System && 1709 (bundle->getPlatformBuildVersionCode() == "" || 1710 bundle->getPlatformBuildVersionName() == "")) { 1711 err = extractPlatformBuildVersion(assets->getAssetManager(), bundle); 1712 if (err != NO_ERROR) { 1713 return UNKNOWN_ERROR; 1714 } 1715 } 1716 1717 const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0)); 1718 String8 manifestPath(manifestFile->getPrintableSource()); 1719 1720 // Generate final compiled manifest file. 1721 manifestFile->clearData(); 1722 sp<XMLNode> manifestTree = XMLNode::parse(manifestFile); 1723 if (manifestTree == NULL) { 1724 return UNKNOWN_ERROR; 1725 } 1726 err = massageManifest(bundle, &table, manifestTree); 1727 if (err < NO_ERROR) { 1728 return err; 1729 } 1730 err = compileXmlFile(bundle, assets, String16(), manifestTree, manifestFile, &table); 1731 if (err < NO_ERROR) { 1732 return err; 1733 } 1734 1735 if (table.modifyForCompat(bundle) != NO_ERROR) { 1736 return UNKNOWN_ERROR; 1737 } 1738 1739 //block.restart(); 1740 //printXMLBlock(&block); 1741 1742 // -------------------------------------------------------------- 1743 // Generate the final resource table. 1744 // Re-flatten because we may have added new resource IDs 1745 // -------------------------------------------------------------- 1746 1747 1748 ResTable finalResTable; 1749 sp<AaptFile> resFile; 1750 1751 if (table.hasResources()) { 1752 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R")); 1753 err = table.addSymbols(symbols, bundle->getSkipSymbolsWithoutDefaultLocalization()); 1754 if (err < NO_ERROR) { 1755 return err; 1756 } 1757 1758 KeyedVector<Symbol, Vector<SymbolDefinition> > densityVaryingResources; 1759 if (builder->getSplits().size() > 1) { 1760 // Only look for density varying resources if we're generating 1761 // splits. 1762 table.getDensityVaryingResources(densityVaryingResources); 1763 } 1764 1765 Vector<sp<ApkSplit> >& splits = builder->getSplits(); 1766 const size_t numSplits = splits.size(); 1767 for (size_t i = 0; i < numSplits; i++) { 1768 sp<ApkSplit>& split = splits.editItemAt(i); 1769 sp<AaptFile> flattenedTable = new AaptFile(String8("resources.arsc"), 1770 AaptGroupEntry(), String8()); 1771 err = table.flatten(bundle, split->getResourceFilter(), 1772 flattenedTable, split->isBase()); 1773 if (err != NO_ERROR) { 1774 fprintf(stderr, "Failed to generate resource table for split '%s'\n", 1775 split->getPrintableName().string()); 1776 return err; 1777 } 1778 split->addEntry(String8("resources.arsc"), flattenedTable); 1779 1780 if (split->isBase()) { 1781 resFile = flattenedTable; 1782 err = finalResTable.add(flattenedTable->getData(), flattenedTable->getSize()); 1783 if (err != NO_ERROR) { 1784 fprintf(stderr, "Generated resource table is corrupt.\n"); 1785 return err; 1786 } 1787 } else { 1788 ResTable resTable; 1789 err = resTable.add(flattenedTable->getData(), flattenedTable->getSize()); 1790 if (err != NO_ERROR) { 1791 fprintf(stderr, "Generated resource table for split '%s' is corrupt.\n", 1792 split->getPrintableName().string()); 1793 return err; 1794 } 1795 1796 bool hasError = false; 1797 const std::set<ConfigDescription>& splitConfigs = split->getConfigs(); 1798 for (std::set<ConfigDescription>::const_iterator iter = splitConfigs.begin(); 1799 iter != splitConfigs.end(); 1800 ++iter) { 1801 const ConfigDescription& config = *iter; 1802 if (AaptConfig::isDensityOnly(config)) { 1803 // Each density only split must contain all 1804 // density only resources. 1805 Res_value val; 1806 resTable.setParameters(&config); 1807 const size_t densityVaryingResourceCount = densityVaryingResources.size(); 1808 for (size_t k = 0; k < densityVaryingResourceCount; k++) { 1809 const Symbol& symbol = densityVaryingResources.keyAt(k); 1810 ssize_t block = resTable.getResource(symbol.id, &val, true); 1811 if (block < 0) { 1812 // Maybe it's in the base? 1813 finalResTable.setParameters(&config); 1814 block = finalResTable.getResource(symbol.id, &val, true); 1815 } 1816 1817 if (block < 0) { 1818 hasError = true; 1819 SourcePos().error("%s has no definition for density split '%s'", 1820 symbol.toString().string(), config.toString().string()); 1821 1822 if (bundle->getVerbose()) { 1823 const Vector<SymbolDefinition>& defs = densityVaryingResources[k]; 1824 const size_t defCount = std::min(size_t(5), defs.size()); 1825 for (size_t d = 0; d < defCount; d++) { 1826 const SymbolDefinition& def = defs[d]; 1827 def.source.error("%s has definition for %s", 1828 symbol.toString().string(), def.config.toString().string()); 1829 } 1830 1831 if (defCount < defs.size()) { 1832 SourcePos().error("and %d more ...", (int) (defs.size() - defCount)); 1833 } 1834 } 1835 } 1836 } 1837 } 1838 } 1839 1840 if (hasError) { 1841 return UNKNOWN_ERROR; 1842 } 1843 1844 // Generate the AndroidManifest for this split. 1845 sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"), 1846 AaptGroupEntry(), String8()); 1847 err = generateAndroidManifestForSplit(bundle, assets, split, 1848 generatedManifest, &table); 1849 if (err != NO_ERROR) { 1850 fprintf(stderr, "Failed to generate AndroidManifest.xml for split '%s'\n", 1851 split->getPrintableName().string()); 1852 return err; 1853 } 1854 split->addEntry(String8("AndroidManifest.xml"), generatedManifest); 1855 } 1856 } 1857 1858 if (bundle->getPublicOutputFile()) { 1859 FILE* fp = fopen(bundle->getPublicOutputFile(), "w+"); 1860 if (fp == NULL) { 1861 fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n", 1862 (const char*)bundle->getPublicOutputFile(), strerror(errno)); 1863 return UNKNOWN_ERROR; 1864 } 1865 if (bundle->getVerbose()) { 1866 printf(" Writing public definitions to %s.\n", bundle->getPublicOutputFile()); 1867 } 1868 table.writePublicDefinitions(String16(assets->getPackage()), fp); 1869 fclose(fp); 1870 } 1871 1872 if (finalResTable.getTableCount() == 0 || resFile == NULL) { 1873 fprintf(stderr, "No resource table was generated.\n"); 1874 return UNKNOWN_ERROR; 1875 } 1876 } 1877 1878 // Perform a basic validation of the manifest file. This time we 1879 // parse it with the comments intact, so that we can use them to 1880 // generate java docs... so we are not going to write this one 1881 // back out to the final manifest data. 1882 sp<AaptFile> outManifestFile = new AaptFile(manifestFile->getSourceFile(), 1883 manifestFile->getGroupEntry(), 1884 manifestFile->getResourceType()); 1885 err = compileXmlFile(bundle, assets, String16(), manifestFile, 1886 outManifestFile, &table, XML_COMPILE_STANDARD_RESOURCE & ~XML_COMPILE_STRIP_COMMENTS); 1887 if (err < NO_ERROR) { 1888 return err; 1889 } 1890 ResXMLTree block; 1891 block.setTo(outManifestFile->getData(), outManifestFile->getSize(), true); 1892 String16 manifest16("manifest"); 1893 String16 permission16("permission"); 1894 String16 permission_group16("permission-group"); 1895 String16 uses_permission16("uses-permission"); 1896 String16 instrumentation16("instrumentation"); 1897 String16 application16("application"); 1898 String16 provider16("provider"); 1899 String16 service16("service"); 1900 String16 receiver16("receiver"); 1901 String16 activity16("activity"); 1902 String16 action16("action"); 1903 String16 category16("category"); 1904 String16 data16("scheme"); 1905 String16 feature_group16("feature-group"); 1906 String16 uses_feature16("uses-feature"); 1907 const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz" 1908 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789"; 1909 const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz" 1910 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-"; 1911 const char* classIdentChars = "abcdefghijklmnopqrstuvwxyz" 1912 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789$"; 1913 const char* processIdentChars = "abcdefghijklmnopqrstuvwxyz" 1914 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:"; 1915 const char* authoritiesIdentChars = "abcdefghijklmnopqrstuvwxyz" 1916 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-:;"; 1917 const char* typeIdentChars = "abcdefghijklmnopqrstuvwxyz" 1918 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:-/*+"; 1919 const char* schemeIdentChars = "abcdefghijklmnopqrstuvwxyz" 1920 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-"; 1921 ResXMLTree::event_code_t code; 1922 sp<AaptSymbols> permissionSymbols; 1923 sp<AaptSymbols> permissionGroupSymbols; 1924 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 1925 && code > ResXMLTree::BAD_DOCUMENT) { 1926 if (code == ResXMLTree::START_TAG) { 1927 size_t len; 1928 if (block.getElementNamespace(&len) != NULL) { 1929 continue; 1930 } 1931 if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) { 1932 if (validateAttr(manifestPath, finalResTable, block, NULL, "package", 1933 packageIdentChars, true) != ATTR_OKAY) { 1934 hasErrors = true; 1935 } 1936 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1937 "sharedUserId", packageIdentChars, false) != ATTR_OKAY) { 1938 hasErrors = true; 1939 } 1940 } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0 1941 || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) { 1942 const bool isGroup = strcmp16(block.getElementName(&len), 1943 permission_group16.string()) == 0; 1944 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1945 "name", isGroup ? packageIdentCharsWithTheStupid 1946 : packageIdentChars, true) != ATTR_OKAY) { 1947 hasErrors = true; 1948 } 1949 SourcePos srcPos(manifestPath, block.getLineNumber()); 1950 sp<AaptSymbols> syms; 1951 if (!isGroup) { 1952 syms = permissionSymbols; 1953 if (syms == NULL) { 1954 sp<AaptSymbols> symbols = 1955 assets->getSymbolsFor(String8("Manifest")); 1956 syms = permissionSymbols = symbols->addNestedSymbol( 1957 String8("permission"), srcPos); 1958 } 1959 } else { 1960 syms = permissionGroupSymbols; 1961 if (syms == NULL) { 1962 sp<AaptSymbols> symbols = 1963 assets->getSymbolsFor(String8("Manifest")); 1964 syms = permissionGroupSymbols = symbols->addNestedSymbol( 1965 String8("permission_group"), srcPos); 1966 } 1967 } 1968 size_t len; 1969 ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name"); 1970 const char16_t* id = block.getAttributeStringValue(index, &len); 1971 if (id == NULL) { 1972 fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n", 1973 manifestPath.string(), block.getLineNumber(), 1974 String8(block.getElementName(&len)).string()); 1975 hasErrors = true; 1976 break; 1977 } 1978 String8 idStr(id); 1979 char* p = idStr.lockBuffer(idStr.size()); 1980 char* e = p + idStr.size(); 1981 bool begins_with_digit = true; // init to true so an empty string fails 1982 while (e > p) { 1983 e--; 1984 if (*e >= '0' && *e <= '9') { 1985 begins_with_digit = true; 1986 continue; 1987 } 1988 if ((*e >= 'a' && *e <= 'z') || 1989 (*e >= 'A' && *e <= 'Z') || 1990 (*e == '_')) { 1991 begins_with_digit = false; 1992 continue; 1993 } 1994 if (isGroup && (*e == '-')) { 1995 *e = '_'; 1996 begins_with_digit = false; 1997 continue; 1998 } 1999 e++; 2000 break; 2001 } 2002 idStr.unlockBuffer(); 2003 // verify that we stopped because we hit a period or 2004 // the beginning of the string, and that the 2005 // identifier didn't begin with a digit. 2006 if (begins_with_digit || (e != p && *(e-1) != '.')) { 2007 fprintf(stderr, 2008 "%s:%d: Permission name <%s> is not a valid Java symbol\n", 2009 manifestPath.string(), block.getLineNumber(), idStr.string()); 2010 hasErrors = true; 2011 } 2012 syms->addStringSymbol(String8(e), idStr, srcPos); 2013 const char16_t* cmt = block.getComment(&len); 2014 if (cmt != NULL && *cmt != 0) { 2015 //printf("Comment of %s: %s\n", String8(e).string(), 2016 // String8(cmt).string()); 2017 syms->appendComment(String8(e), String16(cmt), srcPos); 2018 } 2019 syms->makeSymbolPublic(String8(e), srcPos); 2020 } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) { 2021 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 2022 "name", packageIdentChars, true) != ATTR_OKAY) { 2023 hasErrors = true; 2024 } 2025 } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) { 2026 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 2027 "name", classIdentChars, true) != ATTR_OKAY) { 2028 hasErrors = true; 2029 } 2030 if (validateAttr(manifestPath, finalResTable, block, 2031 RESOURCES_ANDROID_NAMESPACE, "targetPackage", 2032 packageIdentChars, true) != ATTR_OKAY) { 2033 hasErrors = true; 2034 } 2035 } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) { 2036 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 2037 "name", classIdentChars, false) != ATTR_OKAY) { 2038 hasErrors = true; 2039 } 2040 if (validateAttr(manifestPath, finalResTable, block, 2041 RESOURCES_ANDROID_NAMESPACE, "permission", 2042 packageIdentChars, false) != ATTR_OKAY) { 2043 hasErrors = true; 2044 } 2045 if (validateAttr(manifestPath, finalResTable, block, 2046 RESOURCES_ANDROID_NAMESPACE, "process", 2047 processIdentChars, false) != ATTR_OKAY) { 2048 hasErrors = true; 2049 } 2050 if (validateAttr(manifestPath, finalResTable, block, 2051 RESOURCES_ANDROID_NAMESPACE, "taskAffinity", 2052 processIdentChars, false) != ATTR_OKAY) { 2053 hasErrors = true; 2054 } 2055 } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) { 2056 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 2057 "name", classIdentChars, true) != ATTR_OKAY) { 2058 hasErrors = true; 2059 } 2060 if (validateAttr(manifestPath, finalResTable, block, 2061 RESOURCES_ANDROID_NAMESPACE, "authorities", 2062 authoritiesIdentChars, true) != ATTR_OKAY) { 2063 hasErrors = true; 2064 } 2065 if (validateAttr(manifestPath, finalResTable, block, 2066 RESOURCES_ANDROID_NAMESPACE, "permission", 2067 packageIdentChars, false) != ATTR_OKAY) { 2068 hasErrors = true; 2069 } 2070 if (validateAttr(manifestPath, finalResTable, block, 2071 RESOURCES_ANDROID_NAMESPACE, "process", 2072 processIdentChars, false) != ATTR_OKAY) { 2073 hasErrors = true; 2074 } 2075 } else if (strcmp16(block.getElementName(&len), service16.string()) == 0 2076 || strcmp16(block.getElementName(&len), receiver16.string()) == 0 2077 || strcmp16(block.getElementName(&len), activity16.string()) == 0) { 2078 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 2079 "name", classIdentChars, true) != ATTR_OKAY) { 2080 hasErrors = true; 2081 } 2082 if (validateAttr(manifestPath, finalResTable, block, 2083 RESOURCES_ANDROID_NAMESPACE, "permission", 2084 packageIdentChars, false) != ATTR_OKAY) { 2085 hasErrors = true; 2086 } 2087 if (validateAttr(manifestPath, finalResTable, block, 2088 RESOURCES_ANDROID_NAMESPACE, "process", 2089 processIdentChars, false) != ATTR_OKAY) { 2090 hasErrors = true; 2091 } 2092 if (validateAttr(manifestPath, finalResTable, block, 2093 RESOURCES_ANDROID_NAMESPACE, "taskAffinity", 2094 processIdentChars, false) != ATTR_OKAY) { 2095 hasErrors = true; 2096 } 2097 } else if (strcmp16(block.getElementName(&len), action16.string()) == 0 2098 || strcmp16(block.getElementName(&len), category16.string()) == 0) { 2099 if (validateAttr(manifestPath, finalResTable, block, 2100 RESOURCES_ANDROID_NAMESPACE, "name", 2101 packageIdentChars, true) != ATTR_OKAY) { 2102 hasErrors = true; 2103 } 2104 } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) { 2105 if (validateAttr(manifestPath, finalResTable, block, 2106 RESOURCES_ANDROID_NAMESPACE, "mimeType", 2107 typeIdentChars, true) != ATTR_OKAY) { 2108 hasErrors = true; 2109 } 2110 if (validateAttr(manifestPath, finalResTable, block, 2111 RESOURCES_ANDROID_NAMESPACE, "scheme", 2112 schemeIdentChars, true) != ATTR_OKAY) { 2113 hasErrors = true; 2114 } 2115 } else if (strcmp16(block.getElementName(&len), feature_group16.string()) == 0) { 2116 int depth = 1; 2117 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 2118 && code > ResXMLTree::BAD_DOCUMENT) { 2119 if (code == ResXMLTree::START_TAG) { 2120 depth++; 2121 if (strcmp16(block.getElementName(&len), uses_feature16.string()) == 0) { 2122 ssize_t idx = block.indexOfAttribute( 2123 RESOURCES_ANDROID_NAMESPACE, "required"); 2124 if (idx < 0) { 2125 continue; 2126 } 2127 2128 int32_t data = block.getAttributeData(idx); 2129 if (data == 0) { 2130 fprintf(stderr, "%s:%d: Tag <uses-feature> can not have " 2131 "android:required=\"false\" when inside a " 2132 "<feature-group> tag.\n", 2133 manifestPath.string(), block.getLineNumber()); 2134 hasErrors = true; 2135 } 2136 } 2137 } else if (code == ResXMLTree::END_TAG) { 2138 depth--; 2139 if (depth == 0) { 2140 break; 2141 } 2142 } 2143 } 2144 } 2145 } 2146 } 2147 2148 if (hasErrors) { 2149 return UNKNOWN_ERROR; 2150 } 2151 2152 if (resFile != NULL) { 2153 // These resources are now considered to be a part of the included 2154 // resources, for others to reference. 2155 err = assets->addIncludedResources(resFile); 2156 if (err < NO_ERROR) { 2157 fprintf(stderr, "ERROR: Unable to parse generated resources, aborting.\n"); 2158 return err; 2159 } 2160 } 2161 2162 return err; 2163 } 2164 2165 static const char* getIndentSpace(int indent) 2166 { 2167 static const char whitespace[] = 2168 " "; 2169 2170 return whitespace + sizeof(whitespace) - 1 - indent*4; 2171 } 2172 2173 static String8 flattenSymbol(const String8& symbol) { 2174 String8 result(symbol); 2175 ssize_t first; 2176 if ((first = symbol.find(":", 0)) >= 0 2177 || (first = symbol.find(".", 0)) >= 0) { 2178 size_t size = symbol.size(); 2179 char* buf = result.lockBuffer(size); 2180 for (size_t i = first; i < size; i++) { 2181 if (buf[i] == ':' || buf[i] == '.') { 2182 buf[i] = '_'; 2183 } 2184 } 2185 result.unlockBuffer(size); 2186 } 2187 return result; 2188 } 2189 2190 static String8 getSymbolPackage(const String8& symbol, const sp<AaptAssets>& assets, bool pub) { 2191 ssize_t colon = symbol.find(":", 0); 2192 if (colon >= 0) { 2193 return String8(symbol.string(), colon); 2194 } 2195 return pub ? assets->getPackage() : assets->getSymbolsPrivatePackage(); 2196 } 2197 2198 static String8 getSymbolName(const String8& symbol) { 2199 ssize_t colon = symbol.find(":", 0); 2200 if (colon >= 0) { 2201 return String8(symbol.string() + colon + 1); 2202 } 2203 return symbol; 2204 } 2205 2206 static String16 getAttributeComment(const sp<AaptAssets>& assets, 2207 const String8& name, 2208 String16* outTypeComment = NULL) 2209 { 2210 sp<AaptSymbols> asym = assets->getSymbolsFor(String8("R")); 2211 if (asym != NULL) { 2212 //printf("Got R symbols!\n"); 2213 asym = asym->getNestedSymbols().valueFor(String8("attr")); 2214 if (asym != NULL) { 2215 //printf("Got attrs symbols! comment %s=%s\n", 2216 // name.string(), String8(asym->getComment(name)).string()); 2217 if (outTypeComment != NULL) { 2218 *outTypeComment = asym->getTypeComment(name); 2219 } 2220 return asym->getComment(name); 2221 } 2222 } 2223 return String16(); 2224 } 2225 2226 static status_t writeResourceLoadedCallbackForLayoutClasses( 2227 FILE* fp, const sp<AaptAssets>& assets, 2228 const sp<AaptSymbols>& symbols, int indent, bool /* includePrivate */) 2229 { 2230 String16 attr16("attr"); 2231 String16 package16(assets->getPackage()); 2232 2233 const char* indentStr = getIndentSpace(indent); 2234 bool hasErrors = false; 2235 2236 size_t i; 2237 size_t N = symbols->getNestedSymbols().size(); 2238 for (i=0; i<N; i++) { 2239 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2240 String8 realClassName(symbols->getNestedSymbols().keyAt(i)); 2241 String8 nclassName(flattenSymbol(realClassName)); 2242 2243 fprintf(fp, 2244 "%sfor(int i = 0; i < styleable.%s.length; ++i) {\n" 2245 "%sstyleable.%s[i] = (styleable.%s[i] & 0x00ffffff) | (packageId << 24);\n" 2246 "%s}\n", 2247 indentStr, nclassName.string(), 2248 getIndentSpace(indent+1), nclassName.string(), nclassName.string(), 2249 indentStr); 2250 } 2251 2252 return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR; 2253 } 2254 2255 static status_t writeResourceLoadedCallback( 2256 FILE* fp, const sp<AaptAssets>& assets, bool includePrivate, 2257 const sp<AaptSymbols>& symbols, const String8& className, int indent) 2258 { 2259 size_t i; 2260 status_t err = NO_ERROR; 2261 2262 size_t N = symbols->getSymbols().size(); 2263 for (i=0; i<N; i++) { 2264 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 2265 if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) { 2266 continue; 2267 } 2268 if (!assets->isJavaSymbol(sym, includePrivate)) { 2269 continue; 2270 } 2271 String8 flat_name(flattenSymbol(sym.name)); 2272 fprintf(fp, 2273 "%s%s.%s = (%s.%s & 0x00ffffff) | (packageId << 24);\n", 2274 getIndentSpace(indent), className.string(), flat_name.string(), 2275 className.string(), flat_name.string()); 2276 } 2277 2278 N = symbols->getNestedSymbols().size(); 2279 for (i=0; i<N; i++) { 2280 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2281 String8 nclassName(symbols->getNestedSymbols().keyAt(i)); 2282 if (nclassName == "styleable") { 2283 err = writeResourceLoadedCallbackForLayoutClasses( 2284 fp, assets, nsymbols, indent, includePrivate); 2285 } else { 2286 err = writeResourceLoadedCallback(fp, assets, includePrivate, nsymbols, 2287 nclassName, indent); 2288 } 2289 if (err != NO_ERROR) { 2290 return err; 2291 } 2292 } 2293 2294 return NO_ERROR; 2295 } 2296 2297 static status_t writeLayoutClasses( 2298 FILE* fp, const sp<AaptAssets>& assets, 2299 const sp<AaptSymbols>& symbols, int indent, bool includePrivate, bool nonConstantId) 2300 { 2301 const char* indentStr = getIndentSpace(indent); 2302 if (!includePrivate) { 2303 fprintf(fp, "%s/** @doconly */\n", indentStr); 2304 } 2305 fprintf(fp, "%spublic static final class styleable {\n", indentStr); 2306 indent++; 2307 2308 String16 attr16("attr"); 2309 String16 package16(assets->getPackage()); 2310 2311 indentStr = getIndentSpace(indent); 2312 bool hasErrors = false; 2313 2314 size_t i; 2315 size_t N = symbols->getNestedSymbols().size(); 2316 for (i=0; i<N; i++) { 2317 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2318 String8 realClassName(symbols->getNestedSymbols().keyAt(i)); 2319 String8 nclassName(flattenSymbol(realClassName)); 2320 2321 SortedVector<uint32_t> idents; 2322 Vector<uint32_t> origOrder; 2323 Vector<bool> publicFlags; 2324 2325 size_t a; 2326 size_t NA = nsymbols->getSymbols().size(); 2327 for (a=0; a<NA; a++) { 2328 const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a)); 2329 int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32 2330 ? sym.int32Val : 0; 2331 bool isPublic = true; 2332 if (code == 0) { 2333 String16 name16(sym.name); 2334 uint32_t typeSpecFlags; 2335 code = assets->getIncludedResources().identifierForName( 2336 name16.string(), name16.size(), 2337 attr16.string(), attr16.size(), 2338 package16.string(), package16.size(), &typeSpecFlags); 2339 if (code == 0) { 2340 fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n", 2341 nclassName.string(), sym.name.string()); 2342 hasErrors = true; 2343 } 2344 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 2345 } 2346 idents.add(code); 2347 origOrder.add(code); 2348 publicFlags.add(isPublic); 2349 } 2350 2351 NA = idents.size(); 2352 2353 String16 comment = symbols->getComment(realClassName); 2354 AnnotationProcessor ann; 2355 fprintf(fp, "%s/** ", indentStr); 2356 if (comment.size() > 0) { 2357 String8 cmt(comment); 2358 ann.preprocessComment(cmt); 2359 fprintf(fp, "%s\n", cmt.string()); 2360 } else { 2361 fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string()); 2362 } 2363 bool hasTable = false; 2364 for (a=0; a<NA; a++) { 2365 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 2366 if (pos >= 0) { 2367 if (!hasTable) { 2368 hasTable = true; 2369 fprintf(fp, 2370 "%s <p>Includes the following attributes:</p>\n" 2371 "%s <table>\n" 2372 "%s <colgroup align=\"left\" />\n" 2373 "%s <colgroup align=\"left\" />\n" 2374 "%s <tr><th>Attribute</th><th>Description</th></tr>\n", 2375 indentStr, 2376 indentStr, 2377 indentStr, 2378 indentStr, 2379 indentStr); 2380 } 2381 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 2382 if (!publicFlags.itemAt(a) && !includePrivate) { 2383 continue; 2384 } 2385 String8 name8(sym.name); 2386 String16 comment(sym.comment); 2387 if (comment.size() <= 0) { 2388 comment = getAttributeComment(assets, name8); 2389 } 2390 if (comment.contains(u"@removed")) { 2391 continue; 2392 } 2393 if (comment.size() > 0) { 2394 const char16_t* p = comment.string(); 2395 while (*p != 0 && *p != '.') { 2396 if (*p == '{') { 2397 while (*p != 0 && *p != '}') { 2398 p++; 2399 } 2400 } else { 2401 p++; 2402 } 2403 } 2404 if (*p == '.') { 2405 p++; 2406 } 2407 comment = String16(comment.string(), p-comment.string()); 2408 } 2409 fprintf(fp, "%s <tr><td><code>{@link #%s_%s %s:%s}</code></td><td>%s</td></tr>\n", 2410 indentStr, nclassName.string(), 2411 flattenSymbol(name8).string(), 2412 getSymbolPackage(name8, assets, true).string(), 2413 getSymbolName(name8).string(), 2414 String8(comment).string()); 2415 } 2416 } 2417 if (hasTable) { 2418 fprintf(fp, "%s </table>\n", indentStr); 2419 } 2420 for (a=0; a<NA; a++) { 2421 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 2422 if (pos >= 0) { 2423 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 2424 if (!publicFlags.itemAt(a) && !includePrivate) { 2425 continue; 2426 } 2427 fprintf(fp, "%s @see #%s_%s\n", 2428 indentStr, nclassName.string(), 2429 flattenSymbol(sym.name).string()); 2430 } 2431 } 2432 fprintf(fp, "%s */\n", getIndentSpace(indent)); 2433 2434 ann.printAnnotations(fp, indentStr); 2435 2436 fprintf(fp, 2437 "%spublic static final int[] %s = {\n" 2438 "%s", 2439 indentStr, nclassName.string(), 2440 getIndentSpace(indent+1)); 2441 2442 for (a=0; a<NA; a++) { 2443 if (a != 0) { 2444 if ((a&3) == 0) { 2445 fprintf(fp, ",\n%s", getIndentSpace(indent+1)); 2446 } else { 2447 fprintf(fp, ", "); 2448 } 2449 } 2450 fprintf(fp, "0x%08x", idents[a]); 2451 } 2452 2453 fprintf(fp, "\n%s};\n", indentStr); 2454 2455 for (a=0; a<NA; a++) { 2456 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 2457 if (pos >= 0) { 2458 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 2459 if (!publicFlags.itemAt(a) && !includePrivate) { 2460 continue; 2461 } 2462 String8 name8(sym.name); 2463 String16 comment(sym.comment); 2464 String16 typeComment; 2465 if (comment.size() <= 0) { 2466 comment = getAttributeComment(assets, name8, &typeComment); 2467 } else { 2468 getAttributeComment(assets, name8, &typeComment); 2469 } 2470 2471 uint32_t typeSpecFlags = 0; 2472 String16 name16(sym.name); 2473 assets->getIncludedResources().identifierForName( 2474 name16.string(), name16.size(), 2475 attr16.string(), attr16.size(), 2476 package16.string(), package16.size(), &typeSpecFlags); 2477 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(), 2478 // String8(attr16).string(), String8(name16).string(), typeSpecFlags); 2479 const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 2480 2481 AnnotationProcessor ann; 2482 fprintf(fp, "%s/**\n", indentStr); 2483 if (comment.size() > 0) { 2484 String8 cmt(comment); 2485 ann.preprocessComment(cmt); 2486 fprintf(fp, "%s <p>\n%s @attr description\n", indentStr, indentStr); 2487 fprintf(fp, "%s %s\n", indentStr, cmt.string()); 2488 } else { 2489 fprintf(fp, 2490 "%s <p>This symbol is the offset where the {@link %s.R.attr#%s}\n" 2491 "%s attribute's value can be found in the {@link #%s} array.\n", 2492 indentStr, 2493 getSymbolPackage(name8, assets, pub).string(), 2494 getSymbolName(name8).string(), 2495 indentStr, nclassName.string()); 2496 } 2497 if (typeComment.size() > 0) { 2498 String8 cmt(typeComment); 2499 ann.preprocessComment(cmt); 2500 fprintf(fp, "\n\n%s %s\n", indentStr, cmt.string()); 2501 } 2502 if (comment.size() > 0) { 2503 if (pub) { 2504 fprintf(fp, 2505 "%s <p>This corresponds to the global attribute\n" 2506 "%s resource symbol {@link %s.R.attr#%s}.\n", 2507 indentStr, indentStr, 2508 getSymbolPackage(name8, assets, true).string(), 2509 getSymbolName(name8).string()); 2510 } else { 2511 fprintf(fp, 2512 "%s <p>This is a private symbol.\n", indentStr); 2513 } 2514 } 2515 fprintf(fp, "%s @attr name %s:%s\n", indentStr, 2516 getSymbolPackage(name8, assets, pub).string(), 2517 getSymbolName(name8).string()); 2518 fprintf(fp, "%s*/\n", indentStr); 2519 ann.printAnnotations(fp, indentStr); 2520 2521 const char * id_format = nonConstantId ? 2522 "%spublic static int %s_%s = %d;\n" : 2523 "%spublic static final int %s_%s = %d;\n"; 2524 2525 fprintf(fp, 2526 id_format, 2527 indentStr, nclassName.string(), 2528 flattenSymbol(name8).string(), (int)pos); 2529 } 2530 } 2531 } 2532 2533 indent--; 2534 fprintf(fp, "%s};\n", getIndentSpace(indent)); 2535 return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR; 2536 } 2537 2538 static status_t writeTextLayoutClasses( 2539 FILE* fp, const sp<AaptAssets>& assets, 2540 const sp<AaptSymbols>& symbols, bool includePrivate) 2541 { 2542 String16 attr16("attr"); 2543 String16 package16(assets->getPackage()); 2544 2545 bool hasErrors = false; 2546 2547 size_t i; 2548 size_t N = symbols->getNestedSymbols().size(); 2549 for (i=0; i<N; i++) { 2550 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2551 String8 realClassName(symbols->getNestedSymbols().keyAt(i)); 2552 String8 nclassName(flattenSymbol(realClassName)); 2553 2554 SortedVector<uint32_t> idents; 2555 Vector<uint32_t> origOrder; 2556 Vector<bool> publicFlags; 2557 2558 size_t a; 2559 size_t NA = nsymbols->getSymbols().size(); 2560 for (a=0; a<NA; a++) { 2561 const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a)); 2562 int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32 2563 ? sym.int32Val : 0; 2564 bool isPublic = true; 2565 if (code == 0) { 2566 String16 name16(sym.name); 2567 uint32_t typeSpecFlags; 2568 code = assets->getIncludedResources().identifierForName( 2569 name16.string(), name16.size(), 2570 attr16.string(), attr16.size(), 2571 package16.string(), package16.size(), &typeSpecFlags); 2572 if (code == 0) { 2573 fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n", 2574 nclassName.string(), sym.name.string()); 2575 hasErrors = true; 2576 } 2577 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 2578 } 2579 idents.add(code); 2580 origOrder.add(code); 2581 publicFlags.add(isPublic); 2582 } 2583 2584 NA = idents.size(); 2585 2586 fprintf(fp, "int[] styleable %s {", nclassName.string()); 2587 2588 for (a=0; a<NA; a++) { 2589 if (a != 0) { 2590 fprintf(fp, ","); 2591 } 2592 fprintf(fp, " 0x%08x", idents[a]); 2593 } 2594 2595 fprintf(fp, " }\n"); 2596 2597 for (a=0; a<NA; a++) { 2598 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 2599 if (pos >= 0) { 2600 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 2601 if (!publicFlags.itemAt(a) && !includePrivate) { 2602 continue; 2603 } 2604 String8 name8(sym.name); 2605 String16 comment(sym.comment); 2606 String16 typeComment; 2607 if (comment.size() <= 0) { 2608 comment = getAttributeComment(assets, name8, &typeComment); 2609 } else { 2610 getAttributeComment(assets, name8, &typeComment); 2611 } 2612 2613 uint32_t typeSpecFlags = 0; 2614 String16 name16(sym.name); 2615 assets->getIncludedResources().identifierForName( 2616 name16.string(), name16.size(), 2617 attr16.string(), attr16.size(), 2618 package16.string(), package16.size(), &typeSpecFlags); 2619 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(), 2620 // String8(attr16).string(), String8(name16).string(), typeSpecFlags); 2621 //const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 2622 2623 fprintf(fp, 2624 "int styleable %s_%s %d\n", 2625 nclassName.string(), 2626 flattenSymbol(name8).string(), (int)pos); 2627 } 2628 } 2629 } 2630 2631 return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR; 2632 } 2633 2634 static status_t writeSymbolClass( 2635 FILE* fp, const sp<AaptAssets>& assets, bool includePrivate, 2636 const sp<AaptSymbols>& symbols, const String8& className, int indent, 2637 bool nonConstantId, bool emitCallback) 2638 { 2639 fprintf(fp, "%spublic %sfinal class %s {\n", 2640 getIndentSpace(indent), 2641 indent != 0 ? "static " : "", className.string()); 2642 indent++; 2643 2644 size_t i; 2645 status_t err = NO_ERROR; 2646 2647 const char * id_format = nonConstantId ? 2648 "%spublic static int %s=0x%08x;\n" : 2649 "%spublic static final int %s=0x%08x;\n"; 2650 2651 size_t N = symbols->getSymbols().size(); 2652 for (i=0; i<N; i++) { 2653 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 2654 if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) { 2655 continue; 2656 } 2657 if (!assets->isJavaSymbol(sym, includePrivate)) { 2658 continue; 2659 } 2660 String8 name8(sym.name); 2661 String16 comment(sym.comment); 2662 bool haveComment = false; 2663 AnnotationProcessor ann; 2664 if (comment.size() > 0) { 2665 haveComment = true; 2666 String8 cmt(comment); 2667 ann.preprocessComment(cmt); 2668 fprintf(fp, 2669 "%s/** %s\n", 2670 getIndentSpace(indent), cmt.string()); 2671 } 2672 String16 typeComment(sym.typeComment); 2673 if (typeComment.size() > 0) { 2674 String8 cmt(typeComment); 2675 ann.preprocessComment(cmt); 2676 if (!haveComment) { 2677 haveComment = true; 2678 fprintf(fp, 2679 "%s/** %s\n", getIndentSpace(indent), cmt.string()); 2680 } else { 2681 fprintf(fp, 2682 "%s %s\n", getIndentSpace(indent), cmt.string()); 2683 } 2684 } 2685 if (haveComment) { 2686 fprintf(fp,"%s */\n", getIndentSpace(indent)); 2687 } 2688 ann.printAnnotations(fp, getIndentSpace(indent)); 2689 fprintf(fp, id_format, 2690 getIndentSpace(indent), 2691 flattenSymbol(name8).string(), (int)sym.int32Val); 2692 } 2693 2694 for (i=0; i<N; i++) { 2695 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 2696 if (sym.typeCode != AaptSymbolEntry::TYPE_STRING) { 2697 continue; 2698 } 2699 if (!assets->isJavaSymbol(sym, includePrivate)) { 2700 continue; 2701 } 2702 String8 name8(sym.name); 2703 String16 comment(sym.comment); 2704 AnnotationProcessor ann; 2705 if (comment.size() > 0) { 2706 String8 cmt(comment); 2707 ann.preprocessComment(cmt); 2708 fprintf(fp, 2709 "%s/** %s\n" 2710 "%s */\n", 2711 getIndentSpace(indent), cmt.string(), 2712 getIndentSpace(indent)); 2713 } 2714 ann.printAnnotations(fp, getIndentSpace(indent)); 2715 fprintf(fp, "%spublic static final String %s=\"%s\";\n", 2716 getIndentSpace(indent), 2717 flattenSymbol(name8).string(), sym.stringVal.string()); 2718 } 2719 2720 sp<AaptSymbols> styleableSymbols; 2721 2722 N = symbols->getNestedSymbols().size(); 2723 for (i=0; i<N; i++) { 2724 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2725 String8 nclassName(symbols->getNestedSymbols().keyAt(i)); 2726 if (nclassName == "styleable") { 2727 styleableSymbols = nsymbols; 2728 } else { 2729 err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, 2730 indent, nonConstantId, false); 2731 } 2732 if (err != NO_ERROR) { 2733 return err; 2734 } 2735 } 2736 2737 if (styleableSymbols != NULL) { 2738 err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate, nonConstantId); 2739 if (err != NO_ERROR) { 2740 return err; 2741 } 2742 } 2743 2744 if (emitCallback) { 2745 fprintf(fp, "%spublic static void onResourcesLoaded(int packageId) {\n", 2746 getIndentSpace(indent)); 2747 writeResourceLoadedCallback(fp, assets, includePrivate, symbols, className, indent + 1); 2748 fprintf(fp, "%s}\n", getIndentSpace(indent)); 2749 } 2750 2751 indent--; 2752 fprintf(fp, "%s}\n", getIndentSpace(indent)); 2753 return NO_ERROR; 2754 } 2755 2756 static status_t writeTextSymbolClass( 2757 FILE* fp, const sp<AaptAssets>& assets, bool includePrivate, 2758 const sp<AaptSymbols>& symbols, const String8& className) 2759 { 2760 size_t i; 2761 status_t err = NO_ERROR; 2762 2763 size_t N = symbols->getSymbols().size(); 2764 for (i=0; i<N; i++) { 2765 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 2766 if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) { 2767 continue; 2768 } 2769 2770 if (!assets->isJavaSymbol(sym, includePrivate)) { 2771 continue; 2772 } 2773 2774 String8 name8(sym.name); 2775 fprintf(fp, "int %s %s 0x%08x\n", 2776 className.string(), 2777 flattenSymbol(name8).string(), (int)sym.int32Val); 2778 } 2779 2780 N = symbols->getNestedSymbols().size(); 2781 for (i=0; i<N; i++) { 2782 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2783 String8 nclassName(symbols->getNestedSymbols().keyAt(i)); 2784 if (nclassName == "styleable") { 2785 err = writeTextLayoutClasses(fp, assets, nsymbols, includePrivate); 2786 } else { 2787 err = writeTextSymbolClass(fp, assets, includePrivate, nsymbols, nclassName); 2788 } 2789 if (err != NO_ERROR) { 2790 return err; 2791 } 2792 } 2793 2794 return NO_ERROR; 2795 } 2796 2797 status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets, 2798 const String8& package, bool includePrivate, bool emitCallback) 2799 { 2800 if (!bundle->getRClassDir()) { 2801 return NO_ERROR; 2802 } 2803 2804 const char* textSymbolsDest = bundle->getOutputTextSymbols(); 2805 2806 String8 R("R"); 2807 const size_t N = assets->getSymbols().size(); 2808 for (size_t i=0; i<N; i++) { 2809 sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i); 2810 String8 className(assets->getSymbols().keyAt(i)); 2811 String8 dest(bundle->getRClassDir()); 2812 2813 if (bundle->getMakePackageDirs()) { 2814 const String8& pkg(package); 2815 const char* last = pkg.string(); 2816 const char* s = last-1; 2817 do { 2818 s++; 2819 if (s > last && (*s == '.' || *s == 0)) { 2820 String8 part(last, s-last); 2821 dest.appendPath(part); 2822 #ifdef _WIN32 2823 _mkdir(dest.string()); 2824 #else 2825 mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP); 2826 #endif 2827 last = s+1; 2828 } 2829 } while (*s); 2830 } 2831 dest.appendPath(className); 2832 dest.append(".java"); 2833 FILE* fp = fopen(dest.string(), "w+"); 2834 if (fp == NULL) { 2835 fprintf(stderr, "ERROR: Unable to open class file %s: %s\n", 2836 dest.string(), strerror(errno)); 2837 return UNKNOWN_ERROR; 2838 } 2839 if (bundle->getVerbose()) { 2840 printf(" Writing symbols for class %s.\n", className.string()); 2841 } 2842 2843 fprintf(fp, 2844 "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n" 2845 " *\n" 2846 " * This class was automatically generated by the\n" 2847 " * aapt tool from the resource data it found. It\n" 2848 " * should not be modified by hand.\n" 2849 " */\n" 2850 "\n" 2851 "package %s;\n\n", package.string()); 2852 2853 status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, 2854 className, 0, bundle->getNonConstantId(), emitCallback); 2855 fclose(fp); 2856 if (err != NO_ERROR) { 2857 return err; 2858 } 2859 2860 if (textSymbolsDest != NULL && R == className) { 2861 String8 textDest(textSymbolsDest); 2862 textDest.appendPath(className); 2863 textDest.append(".txt"); 2864 2865 FILE* fp = fopen(textDest.string(), "w+"); 2866 if (fp == NULL) { 2867 fprintf(stderr, "ERROR: Unable to open text symbol file %s: %s\n", 2868 textDest.string(), strerror(errno)); 2869 return UNKNOWN_ERROR; 2870 } 2871 if (bundle->getVerbose()) { 2872 printf(" Writing text symbols for class %s.\n", className.string()); 2873 } 2874 2875 status_t err = writeTextSymbolClass(fp, assets, includePrivate, symbols, 2876 className); 2877 fclose(fp); 2878 if (err != NO_ERROR) { 2879 return err; 2880 } 2881 } 2882 2883 // If we were asked to generate a dependency file, we'll go ahead and add this R.java 2884 // as a target in the dependency file right next to it. 2885 if (bundle->getGenDependencies() && R == className) { 2886 // Add this R.java to the dependency file 2887 String8 dependencyFile(bundle->getRClassDir()); 2888 dependencyFile.appendPath("R.java.d"); 2889 2890 FILE *fp = fopen(dependencyFile.string(), "a"); 2891 fprintf(fp,"%s \\\n", dest.string()); 2892 fclose(fp); 2893 } 2894 } 2895 2896 return NO_ERROR; 2897 } 2898 2899 2900 class ProguardKeepSet 2901 { 2902 public: 2903 // { rule --> { file locations } } 2904 KeyedVector<String8, SortedVector<String8> > rules; 2905 2906 void add(const String8& rule, const String8& where); 2907 }; 2908 2909 void ProguardKeepSet::add(const String8& rule, const String8& where) 2910 { 2911 ssize_t index = rules.indexOfKey(rule); 2912 if (index < 0) { 2913 index = rules.add(rule, SortedVector<String8>()); 2914 } 2915 rules.editValueAt(index).add(where); 2916 } 2917 2918 void 2919 addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName, 2920 const char* pkg, const String8& srcName, int line) 2921 { 2922 String8 className(inClassName); 2923 if (pkg != NULL) { 2924 // asdf --> package.asdf 2925 // .asdf .a.b --> package.asdf package.a.b 2926 // asdf.adsf --> asdf.asdf 2927 const char* p = className.string(); 2928 const char* q = strchr(p, '.'); 2929 if (p == q) { 2930 className = pkg; 2931 className.append(inClassName); 2932 } else if (q == NULL) { 2933 className = pkg; 2934 className.append("."); 2935 className.append(inClassName); 2936 } 2937 } 2938 2939 String8 rule("-keep class "); 2940 rule += className; 2941 rule += " { <init>(...); }"; 2942 2943 String8 location("view "); 2944 location += srcName; 2945 char lineno[20]; 2946 sprintf(lineno, ":%d", line); 2947 location += lineno; 2948 2949 keep->add(rule, location); 2950 } 2951 2952 void 2953 addProguardKeepMethodRule(ProguardKeepSet* keep, const String8& memberName, 2954 const char* /* pkg */, const String8& srcName, int line) 2955 { 2956 String8 rule("-keepclassmembers class * { *** "); 2957 rule += memberName; 2958 rule += "(...); }"; 2959 2960 String8 location("onClick "); 2961 location += srcName; 2962 char lineno[20]; 2963 sprintf(lineno, ":%d", line); 2964 location += lineno; 2965 2966 keep->add(rule, location); 2967 } 2968 2969 status_t 2970 writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets, bool mainDex) 2971 { 2972 status_t err; 2973 ResXMLTree tree; 2974 size_t len; 2975 ResXMLTree::event_code_t code; 2976 int depth = 0; 2977 bool inApplication = false; 2978 String8 error; 2979 sp<AaptGroup> assGroup; 2980 sp<AaptFile> assFile; 2981 String8 pkg; 2982 String8 defaultProcess; 2983 2984 // First, look for a package file to parse. This is required to 2985 // be able to generate the resource information. 2986 assGroup = assets->getFiles().valueFor(String8("AndroidManifest.xml")); 2987 if (assGroup == NULL) { 2988 fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n"); 2989 return -1; 2990 } 2991 2992 if (assGroup->getFiles().size() != 1) { 2993 fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n", 2994 assGroup->getFiles().valueAt(0)->getPrintableSource().string()); 2995 } 2996 2997 assFile = assGroup->getFiles().valueAt(0); 2998 2999 err = parseXMLResource(assFile, &tree); 3000 if (err != NO_ERROR) { 3001 return err; 3002 } 3003 3004 tree.restart(); 3005 3006 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 3007 if (code == ResXMLTree::END_TAG) { 3008 if (/* name == "Application" && */ depth == 2) { 3009 inApplication = false; 3010 } 3011 depth--; 3012 continue; 3013 } 3014 if (code != ResXMLTree::START_TAG) { 3015 continue; 3016 } 3017 depth++; 3018 String8 tag(tree.getElementName(&len)); 3019 // printf("Depth %d tag %s\n", depth, tag.string()); 3020 bool keepTag = false; 3021 if (depth == 1) { 3022 if (tag != "manifest") { 3023 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 3024 return -1; 3025 } 3026 pkg = AaptXml::getAttribute(tree, NULL, "package"); 3027 } else if (depth == 2) { 3028 if (tag == "application") { 3029 inApplication = true; 3030 keepTag = true; 3031 3032 String8 agent = AaptXml::getAttribute(tree, 3033 "http://schemas.android.com/apk/res/android", 3034 "backupAgent", &error); 3035 if (agent.length() > 0) { 3036 addProguardKeepRule(keep, agent, pkg.string(), 3037 assFile->getPrintableSource(), tree.getLineNumber()); 3038 } 3039 3040 if (mainDex) { 3041 defaultProcess = AaptXml::getAttribute(tree, 3042 "http://schemas.android.com/apk/res/android", "process", &error); 3043 if (error != "") { 3044 fprintf(stderr, "ERROR: %s\n", error.string()); 3045 return -1; 3046 } 3047 } 3048 } else if (tag == "instrumentation") { 3049 keepTag = true; 3050 } 3051 } 3052 if (!keepTag && inApplication && depth == 3) { 3053 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") { 3054 keepTag = true; 3055 3056 if (mainDex) { 3057 String8 componentProcess = AaptXml::getAttribute(tree, 3058 "http://schemas.android.com/apk/res/android", "process", &error); 3059 if (error != "") { 3060 fprintf(stderr, "ERROR: %s\n", error.string()); 3061 return -1; 3062 } 3063 3064 const String8& process = 3065 componentProcess.length() > 0 ? componentProcess : defaultProcess; 3066 keepTag = process.length() > 0 && process.find(":") != 0; 3067 } 3068 } 3069 } 3070 if (keepTag) { 3071 String8 name = AaptXml::getAttribute(tree, 3072 "http://schemas.android.com/apk/res/android", "name", &error); 3073 if (error != "") { 3074 fprintf(stderr, "ERROR: %s\n", error.string()); 3075 return -1; 3076 } 3077 3078 keepTag = name.length() > 0; 3079 3080 if (keepTag) { 3081 addProguardKeepRule(keep, name, pkg.string(), 3082 assFile->getPrintableSource(), tree.getLineNumber()); 3083 } 3084 } 3085 } 3086 3087 return NO_ERROR; 3088 } 3089 3090 struct NamespaceAttributePair { 3091 const char* ns; 3092 const char* attr; 3093 3094 NamespaceAttributePair(const char* n, const char* a) : ns(n), attr(a) {} 3095 NamespaceAttributePair() : ns(NULL), attr(NULL) {} 3096 }; 3097 3098 status_t 3099 writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile, 3100 const Vector<String8>& startTags, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs) 3101 { 3102 status_t err; 3103 ResXMLTree tree; 3104 size_t len; 3105 ResXMLTree::event_code_t code; 3106 3107 err = parseXMLResource(layoutFile, &tree); 3108 if (err != NO_ERROR) { 3109 return err; 3110 } 3111 3112 tree.restart(); 3113 3114 if (!startTags.isEmpty()) { 3115 bool haveStart = false; 3116 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 3117 if (code != ResXMLTree::START_TAG) { 3118 continue; 3119 } 3120 String8 tag(tree.getElementName(&len)); 3121 const size_t numStartTags = startTags.size(); 3122 for (size_t i = 0; i < numStartTags; i++) { 3123 if (tag == startTags[i]) { 3124 haveStart = true; 3125 } 3126 } 3127 break; 3128 } 3129 if (!haveStart) { 3130 return NO_ERROR; 3131 } 3132 } 3133 3134 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 3135 if (code != ResXMLTree::START_TAG) { 3136 continue; 3137 } 3138 String8 tag(tree.getElementName(&len)); 3139 3140 // If there is no '.', we'll assume that it's one of the built in names. 3141 if (strchr(tag.string(), '.')) { 3142 addProguardKeepRule(keep, tag, NULL, 3143 layoutFile->getPrintableSource(), tree.getLineNumber()); 3144 } else if (tagAttrPairs != NULL) { 3145 ssize_t tagIndex = tagAttrPairs->indexOfKey(tag); 3146 if (tagIndex >= 0) { 3147 const Vector<NamespaceAttributePair>& nsAttrVector = tagAttrPairs->valueAt(tagIndex); 3148 for (size_t i = 0; i < nsAttrVector.size(); i++) { 3149 const NamespaceAttributePair& nsAttr = nsAttrVector[i]; 3150 3151 ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr); 3152 if (attrIndex < 0) { 3153 // fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n", 3154 // layoutFile->getPrintableSource().string(), tree.getLineNumber(), 3155 // tag.string(), nsAttr.ns, nsAttr.attr); 3156 } else { 3157 size_t len; 3158 addProguardKeepRule(keep, 3159 String8(tree.getAttributeStringValue(attrIndex, &len)), NULL, 3160 layoutFile->getPrintableSource(), tree.getLineNumber()); 3161 } 3162 } 3163 } 3164 } 3165 ssize_t attrIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "onClick"); 3166 if (attrIndex >= 0) { 3167 size_t len; 3168 addProguardKeepMethodRule(keep, 3169 String8(tree.getAttributeStringValue(attrIndex, &len)), NULL, 3170 layoutFile->getPrintableSource(), tree.getLineNumber()); 3171 } 3172 } 3173 3174 return NO_ERROR; 3175 } 3176 3177 static void addTagAttrPair(KeyedVector<String8, Vector<NamespaceAttributePair> >* dest, 3178 const char* tag, const char* ns, const char* attr) { 3179 String8 tagStr(tag); 3180 ssize_t index = dest->indexOfKey(tagStr); 3181 3182 if (index < 0) { 3183 Vector<NamespaceAttributePair> vector; 3184 vector.add(NamespaceAttributePair(ns, attr)); 3185 dest->add(tagStr, vector); 3186 } else { 3187 dest->editValueAt(index).add(NamespaceAttributePair(ns, attr)); 3188 } 3189 } 3190 3191 status_t 3192 writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets) 3193 { 3194 status_t err; 3195 const char* kClass = "class"; 3196 const char* kFragment = "fragment"; 3197 const String8 kTransition("transition"); 3198 const String8 kTransitionPrefix("transition-"); 3199 3200 // tag:attribute pairs that should be checked in layout files. 3201 KeyedVector<String8, Vector<NamespaceAttributePair> > kLayoutTagAttrPairs; 3202 addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, kClass); 3203 addTagAttrPair(&kLayoutTagAttrPairs, kFragment, NULL, kClass); 3204 addTagAttrPair(&kLayoutTagAttrPairs, kFragment, RESOURCES_ANDROID_NAMESPACE, "name"); 3205 3206 // tag:attribute pairs that should be checked in xml files. 3207 KeyedVector<String8, Vector<NamespaceAttributePair> > kXmlTagAttrPairs; 3208 addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, kFragment); 3209 addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, kFragment); 3210 3211 // tag:attribute pairs that should be checked in transition files. 3212 KeyedVector<String8, Vector<NamespaceAttributePair> > kTransitionTagAttrPairs; 3213 addTagAttrPair(&kTransitionTagAttrPairs, kTransition.string(), NULL, kClass); 3214 addTagAttrPair(&kTransitionTagAttrPairs, "pathMotion", NULL, kClass); 3215 3216 const Vector<sp<AaptDir> >& dirs = assets->resDirs(); 3217 const size_t K = dirs.size(); 3218 for (size_t k=0; k<K; k++) { 3219 const sp<AaptDir>& d = dirs.itemAt(k); 3220 const String8& dirName = d->getLeaf(); 3221 Vector<String8> startTags; 3222 const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs = NULL; 3223 if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) { 3224 tagAttrPairs = &kLayoutTagAttrPairs; 3225 } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) { 3226 startTags.add(String8("PreferenceScreen")); 3227 startTags.add(String8("preference-headers")); 3228 tagAttrPairs = &kXmlTagAttrPairs; 3229 } else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) { 3230 startTags.add(String8("menu")); 3231 tagAttrPairs = NULL; 3232 } else if (dirName == kTransition || (strncmp(dirName.string(), kTransitionPrefix.string(), 3233 kTransitionPrefix.size()) == 0)) { 3234 tagAttrPairs = &kTransitionTagAttrPairs; 3235 } else { 3236 continue; 3237 } 3238 3239 const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles(); 3240 const size_t N = groups.size(); 3241 for (size_t i=0; i<N; i++) { 3242 const sp<AaptGroup>& group = groups.valueAt(i); 3243 const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles(); 3244 const size_t M = files.size(); 3245 for (size_t j=0; j<M; j++) { 3246 err = writeProguardForXml(keep, files.valueAt(j), startTags, tagAttrPairs); 3247 if (err < 0) { 3248 return err; 3249 } 3250 } 3251 } 3252 } 3253 // Handle the overlays 3254 sp<AaptAssets> overlay = assets->getOverlay(); 3255 if (overlay.get()) { 3256 return writeProguardForLayouts(keep, overlay); 3257 } 3258 3259 return NO_ERROR; 3260 } 3261 3262 status_t 3263 writeProguardSpec(const char* filename, const ProguardKeepSet& keep, status_t err) 3264 { 3265 FILE* fp = fopen(filename, "w+"); 3266 if (fp == NULL) { 3267 fprintf(stderr, "ERROR: Unable to open class file %s: %s\n", 3268 filename, strerror(errno)); 3269 return UNKNOWN_ERROR; 3270 } 3271 3272 const KeyedVector<String8, SortedVector<String8> >& rules = keep.rules; 3273 const size_t N = rules.size(); 3274 for (size_t i=0; i<N; i++) { 3275 const SortedVector<String8>& locations = rules.valueAt(i); 3276 const size_t M = locations.size(); 3277 for (size_t j=0; j<M; j++) { 3278 fprintf(fp, "# %s\n", locations.itemAt(j).string()); 3279 } 3280 fprintf(fp, "%s\n\n", rules.keyAt(i).string()); 3281 } 3282 fclose(fp); 3283 3284 return err; 3285 } 3286 3287 status_t 3288 writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets) 3289 { 3290 status_t err = -1; 3291 3292 if (!bundle->getProguardFile()) { 3293 return NO_ERROR; 3294 } 3295 3296 ProguardKeepSet keep; 3297 3298 err = writeProguardForAndroidManifest(&keep, assets, false); 3299 if (err < 0) { 3300 return err; 3301 } 3302 3303 err = writeProguardForLayouts(&keep, assets); 3304 if (err < 0) { 3305 return err; 3306 } 3307 3308 return writeProguardSpec(bundle->getProguardFile(), keep, err); 3309 } 3310 3311 status_t 3312 writeMainDexProguardFile(Bundle* bundle, const sp<AaptAssets>& assets) 3313 { 3314 status_t err = -1; 3315 3316 if (!bundle->getMainDexProguardFile()) { 3317 return NO_ERROR; 3318 } 3319 3320 ProguardKeepSet keep; 3321 3322 err = writeProguardForAndroidManifest(&keep, assets, true); 3323 if (err < 0) { 3324 return err; 3325 } 3326 3327 return writeProguardSpec(bundle->getMainDexProguardFile(), keep, err); 3328 } 3329 3330 // Loops through the string paths and writes them to the file pointer 3331 // Each file path is written on its own line with a terminating backslash. 3332 status_t writePathsToFile(const sp<FilePathStore>& files, FILE* fp) 3333 { 3334 status_t deps = -1; 3335 for (size_t file_i = 0; file_i < files->size(); ++file_i) { 3336 // Add the full file path to the dependency file 3337 fprintf(fp, "%s \\\n", files->itemAt(file_i).string()); 3338 deps++; 3339 } 3340 return deps; 3341 } 3342 3343 status_t 3344 writeDependencyPreReqs(Bundle* /* bundle */, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw) 3345 { 3346 status_t deps = -1; 3347 deps += writePathsToFile(assets->getFullResPaths(), fp); 3348 if (includeRaw) { 3349 deps += writePathsToFile(assets->getFullAssetPaths(), fp); 3350 } 3351 return deps; 3352 } 3353