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