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