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