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