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 void addTagAttribute(const sp<XMLNode>& node, const char* ns8, 677 const char* attr8, const char* value) 678 { 679 if (value == NULL) { 680 return; 681 } 682 683 const String16 ns(ns8); 684 const String16 attr(attr8); 685 686 if (node->getAttribute(ns, attr) != NULL) { 687 fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s);" 688 " using existing value in manifest.\n", 689 String8(attr).string(), String8(ns).string()); 690 return; 691 } 692 693 node->addAttribute(ns, attr, String16(value)); 694 } 695 696 static void fullyQualifyClassName(const String8& package, sp<XMLNode> node, 697 const String16& attrName) { 698 XMLNode::attribute_entry* attr = node->editAttribute( 699 String16("http://schemas.android.com/apk/res/android"), attrName); 700 if (attr != NULL) { 701 String8 name(attr->string); 702 703 // asdf --> package.asdf 704 // .asdf .a.b --> package.asdf package.a.b 705 // asdf.adsf --> asdf.asdf 706 String8 className; 707 const char* p = name.string(); 708 const char* q = strchr(p, '.'); 709 if (p == q) { 710 className += package; 711 className += name; 712 } else if (q == NULL) { 713 className += package; 714 className += "."; 715 className += name; 716 } else { 717 className += name; 718 } 719 NOISY(printf("Qualifying class '%s' to '%s'", name.string(), className.string())); 720 attr->string.setTo(String16(className)); 721 } 722 } 723 724 status_t massageManifest(Bundle* bundle, sp<XMLNode> root) 725 { 726 root = root->searchElement(String16(), String16("manifest")); 727 if (root == NULL) { 728 fprintf(stderr, "No <manifest> tag.\n"); 729 return UNKNOWN_ERROR; 730 } 731 732 addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode", 733 bundle->getVersionCode()); 734 addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName", 735 bundle->getVersionName()); 736 737 if (bundle->getMinSdkVersion() != NULL 738 || bundle->getTargetSdkVersion() != NULL 739 || bundle->getMaxSdkVersion() != NULL) { 740 sp<XMLNode> vers = root->getChildElement(String16(), String16("uses-sdk")); 741 if (vers == NULL) { 742 vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk")); 743 root->insertChildAt(vers, 0); 744 } 745 746 addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion", 747 bundle->getMinSdkVersion()); 748 addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion", 749 bundle->getTargetSdkVersion()); 750 addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion", 751 bundle->getMaxSdkVersion()); 752 } 753 754 if (bundle->getDebugMode()) { 755 sp<XMLNode> application = root->getChildElement(String16(), String16("application")); 756 if (application != NULL) { 757 addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true"); 758 } 759 } 760 761 // Deal with manifest package name overrides 762 const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride(); 763 if (manifestPackageNameOverride != NULL) { 764 // Update the actual package name 765 XMLNode::attribute_entry* attr = root->editAttribute(String16(), String16("package")); 766 if (attr == NULL) { 767 fprintf(stderr, "package name is required with --rename-manifest-package.\n"); 768 return UNKNOWN_ERROR; 769 } 770 String8 origPackage(attr->string); 771 attr->string.setTo(String16(manifestPackageNameOverride)); 772 NOISY(printf("Overriding package '%s' to be '%s'\n", origPackage.string(), manifestPackageNameOverride)); 773 774 // Make class names fully qualified 775 sp<XMLNode> application = root->getChildElement(String16(), String16("application")); 776 if (application != NULL) { 777 fullyQualifyClassName(origPackage, application, String16("name")); 778 fullyQualifyClassName(origPackage, application, String16("backupAgent")); 779 780 Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(application->getChildren()); 781 for (size_t i = 0; i < children.size(); i++) { 782 sp<XMLNode> child = children.editItemAt(i); 783 String8 tag(child->getElementName()); 784 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") { 785 fullyQualifyClassName(origPackage, child, String16("name")); 786 } else if (tag == "activity-alias") { 787 fullyQualifyClassName(origPackage, child, String16("name")); 788 fullyQualifyClassName(origPackage, child, String16("targetActivity")); 789 } 790 } 791 } 792 } 793 794 // Deal with manifest package name overrides 795 const char* instrumentationPackageNameOverride = bundle->getInstrumentationPackageNameOverride(); 796 if (instrumentationPackageNameOverride != NULL) { 797 // Fix up instrumentation targets. 798 Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(root->getChildren()); 799 for (size_t i = 0; i < children.size(); i++) { 800 sp<XMLNode> child = children.editItemAt(i); 801 String8 tag(child->getElementName()); 802 if (tag == "instrumentation") { 803 XMLNode::attribute_entry* attr = child->editAttribute( 804 String16("http://schemas.android.com/apk/res/android"), String16("targetPackage")); 805 if (attr != NULL) { 806 attr->string.setTo(String16(instrumentationPackageNameOverride)); 807 } 808 } 809 } 810 } 811 812 return NO_ERROR; 813 } 814 815 #define ASSIGN_IT(n) \ 816 do { \ 817 ssize_t index = resources->indexOfKey(String8(#n)); \ 818 if (index >= 0) { \ 819 n ## s = resources->valueAt(index); \ 820 } \ 821 } while (0) 822 823 status_t updatePreProcessedCache(Bundle* bundle) 824 { 825 #if BENCHMARK 826 fprintf(stdout, "BENCHMARK: Starting PNG PreProcessing \n"); 827 long startPNGTime = clock(); 828 #endif /* BENCHMARK */ 829 830 String8 source(bundle->getResourceSourceDirs()[0]); 831 String8 dest(bundle->getCrunchedOutputDir()); 832 833 FileFinder* ff = new SystemFileFinder(); 834 CrunchCache cc(source,dest,ff); 835 836 CacheUpdater* cu = new SystemCacheUpdater(bundle); 837 size_t numFiles = cc.crunch(cu); 838 839 if (bundle->getVerbose()) 840 fprintf(stdout, "Crunched %d PNG files to update cache\n", (int)numFiles); 841 842 delete ff; 843 delete cu; 844 845 #if BENCHMARK 846 fprintf(stdout, "BENCHMARK: End PNG PreProcessing. Time Elapsed: %f ms \n" 847 ,(clock() - startPNGTime)/1000.0); 848 #endif /* BENCHMARK */ 849 return 0; 850 } 851 852 status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) 853 { 854 // First, look for a package file to parse. This is required to 855 // be able to generate the resource information. 856 sp<AaptGroup> androidManifestFile = 857 assets->getFiles().valueFor(String8("AndroidManifest.xml")); 858 if (androidManifestFile == NULL) { 859 fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n"); 860 return UNKNOWN_ERROR; 861 } 862 863 status_t err = parsePackage(bundle, assets, androidManifestFile); 864 if (err != NO_ERROR) { 865 return err; 866 } 867 868 NOISY(printf("Creating resources for package %s\n", 869 assets->getPackage().string())); 870 871 ResourceTable table(bundle, String16(assets->getPackage())); 872 err = table.addIncludedResources(bundle, assets); 873 if (err != NO_ERROR) { 874 return err; 875 } 876 877 NOISY(printf("Found %d included resource packages\n", (int)table.size())); 878 879 // Standard flags for compiled XML and optional UTF-8 encoding 880 int xmlFlags = XML_COMPILE_STANDARD_RESOURCE; 881 882 /* Only enable UTF-8 if the caller of aapt didn't specifically 883 * request UTF-16 encoding and the parameters of this package 884 * allow UTF-8 to be used. 885 */ 886 if (!bundle->getUTF16StringsOption()) { 887 xmlFlags |= XML_COMPILE_UTF8; 888 } 889 890 // -------------------------------------------------------------- 891 // First, gather all resource information. 892 // -------------------------------------------------------------- 893 894 // resType -> leafName -> group 895 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 896 new KeyedVector<String8, sp<ResourceTypeSet> >; 897 collect_files(assets, resources); 898 899 sp<ResourceTypeSet> drawables; 900 sp<ResourceTypeSet> layouts; 901 sp<ResourceTypeSet> anims; 902 sp<ResourceTypeSet> animators; 903 sp<ResourceTypeSet> interpolators; 904 sp<ResourceTypeSet> xmls; 905 sp<ResourceTypeSet> raws; 906 sp<ResourceTypeSet> colors; 907 sp<ResourceTypeSet> menus; 908 sp<ResourceTypeSet> mipmaps; 909 910 ASSIGN_IT(drawable); 911 ASSIGN_IT(layout); 912 ASSIGN_IT(anim); 913 ASSIGN_IT(animator); 914 ASSIGN_IT(interpolator); 915 ASSIGN_IT(xml); 916 ASSIGN_IT(raw); 917 ASSIGN_IT(color); 918 ASSIGN_IT(menu); 919 ASSIGN_IT(mipmap); 920 921 assets->setResources(resources); 922 // now go through any resource overlays and collect their files 923 sp<AaptAssets> current = assets->getOverlay(); 924 while(current.get()) { 925 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 926 new KeyedVector<String8, sp<ResourceTypeSet> >; 927 current->setResources(resources); 928 collect_files(current, resources); 929 current = current->getOverlay(); 930 } 931 // apply the overlay files to the base set 932 if (!applyFileOverlay(bundle, assets, &drawables, "drawable") || 933 !applyFileOverlay(bundle, assets, &layouts, "layout") || 934 !applyFileOverlay(bundle, assets, &anims, "anim") || 935 !applyFileOverlay(bundle, assets, &animators, "animator") || 936 !applyFileOverlay(bundle, assets, &interpolators, "interpolator") || 937 !applyFileOverlay(bundle, assets, &xmls, "xml") || 938 !applyFileOverlay(bundle, assets, &raws, "raw") || 939 !applyFileOverlay(bundle, assets, &colors, "color") || 940 !applyFileOverlay(bundle, assets, &menus, "menu") || 941 !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) { 942 return UNKNOWN_ERROR; 943 } 944 945 bool hasErrors = false; 946 947 if (drawables != NULL) { 948 if (bundle->getOutputAPKFile() != NULL) { 949 err = preProcessImages(bundle, assets, drawables, "drawable"); 950 } 951 if (err == NO_ERROR) { 952 err = makeFileResources(bundle, assets, &table, drawables, "drawable"); 953 if (err != NO_ERROR) { 954 hasErrors = true; 955 } 956 } else { 957 hasErrors = true; 958 } 959 } 960 961 if (mipmaps != NULL) { 962 if (bundle->getOutputAPKFile() != NULL) { 963 err = preProcessImages(bundle, assets, mipmaps, "mipmap"); 964 } 965 if (err == NO_ERROR) { 966 err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap"); 967 if (err != NO_ERROR) { 968 hasErrors = true; 969 } 970 } else { 971 hasErrors = true; 972 } 973 } 974 975 if (layouts != NULL) { 976 err = makeFileResources(bundle, assets, &table, layouts, "layout"); 977 if (err != NO_ERROR) { 978 hasErrors = true; 979 } 980 } 981 982 if (anims != NULL) { 983 err = makeFileResources(bundle, assets, &table, anims, "anim"); 984 if (err != NO_ERROR) { 985 hasErrors = true; 986 } 987 } 988 989 if (animators != NULL) { 990 err = makeFileResources(bundle, assets, &table, animators, "animator"); 991 if (err != NO_ERROR) { 992 hasErrors = true; 993 } 994 } 995 996 if (interpolators != NULL) { 997 err = makeFileResources(bundle, assets, &table, interpolators, "interpolator"); 998 if (err != NO_ERROR) { 999 hasErrors = true; 1000 } 1001 } 1002 1003 if (xmls != NULL) { 1004 err = makeFileResources(bundle, assets, &table, xmls, "xml"); 1005 if (err != NO_ERROR) { 1006 hasErrors = true; 1007 } 1008 } 1009 1010 if (raws != NULL) { 1011 err = makeFileResources(bundle, assets, &table, raws, "raw"); 1012 if (err != NO_ERROR) { 1013 hasErrors = true; 1014 } 1015 } 1016 1017 // compile resources 1018 current = assets; 1019 while(current.get()) { 1020 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 1021 current->getResources(); 1022 1023 ssize_t index = resources->indexOfKey(String8("values")); 1024 if (index >= 0) { 1025 ResourceDirIterator it(resources->valueAt(index), String8("values")); 1026 ssize_t res; 1027 while ((res=it.next()) == NO_ERROR) { 1028 sp<AaptFile> file = it.getFile(); 1029 res = compileResourceFile(bundle, assets, file, it.getParams(), 1030 (current!=assets), &table); 1031 if (res != NO_ERROR) { 1032 hasErrors = true; 1033 } 1034 } 1035 } 1036 current = current->getOverlay(); 1037 } 1038 1039 if (colors != NULL) { 1040 err = makeFileResources(bundle, assets, &table, colors, "color"); 1041 if (err != NO_ERROR) { 1042 hasErrors = true; 1043 } 1044 } 1045 1046 if (menus != NULL) { 1047 err = makeFileResources(bundle, assets, &table, menus, "menu"); 1048 if (err != NO_ERROR) { 1049 hasErrors = true; 1050 } 1051 } 1052 1053 // -------------------------------------------------------------------- 1054 // Assignment of resource IDs and initial generation of resource table. 1055 // -------------------------------------------------------------------- 1056 1057 if (table.hasResources()) { 1058 sp<AaptFile> resFile(getResourceFile(assets)); 1059 if (resFile == NULL) { 1060 fprintf(stderr, "Error: unable to generate entry for resource data\n"); 1061 return UNKNOWN_ERROR; 1062 } 1063 1064 err = table.assignResourceIds(); 1065 if (err < NO_ERROR) { 1066 return err; 1067 } 1068 } 1069 1070 // -------------------------------------------------------------- 1071 // Finally, we can now we can compile XML files, which may reference 1072 // resources. 1073 // -------------------------------------------------------------- 1074 1075 if (layouts != NULL) { 1076 ResourceDirIterator it(layouts, String8("layout")); 1077 while ((err=it.next()) == NO_ERROR) { 1078 String8 src = it.getFile()->getPrintableSource(); 1079 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1080 if (err == NO_ERROR) { 1081 ResXMLTree block; 1082 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); 1083 checkForIds(src, block); 1084 } else { 1085 hasErrors = true; 1086 } 1087 } 1088 1089 if (err < NO_ERROR) { 1090 hasErrors = true; 1091 } 1092 err = NO_ERROR; 1093 } 1094 1095 if (anims != NULL) { 1096 ResourceDirIterator it(anims, String8("anim")); 1097 while ((err=it.next()) == NO_ERROR) { 1098 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1099 if (err != NO_ERROR) { 1100 hasErrors = true; 1101 } 1102 } 1103 1104 if (err < NO_ERROR) { 1105 hasErrors = true; 1106 } 1107 err = NO_ERROR; 1108 } 1109 1110 if (animators != NULL) { 1111 ResourceDirIterator it(animators, String8("animator")); 1112 while ((err=it.next()) == NO_ERROR) { 1113 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1114 if (err != NO_ERROR) { 1115 hasErrors = true; 1116 } 1117 } 1118 1119 if (err < NO_ERROR) { 1120 hasErrors = true; 1121 } 1122 err = NO_ERROR; 1123 } 1124 1125 if (interpolators != NULL) { 1126 ResourceDirIterator it(interpolators, String8("interpolator")); 1127 while ((err=it.next()) == NO_ERROR) { 1128 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1129 if (err != NO_ERROR) { 1130 hasErrors = true; 1131 } 1132 } 1133 1134 if (err < NO_ERROR) { 1135 hasErrors = true; 1136 } 1137 err = NO_ERROR; 1138 } 1139 1140 if (xmls != NULL) { 1141 ResourceDirIterator it(xmls, String8("xml")); 1142 while ((err=it.next()) == NO_ERROR) { 1143 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1144 if (err != NO_ERROR) { 1145 hasErrors = true; 1146 } 1147 } 1148 1149 if (err < NO_ERROR) { 1150 hasErrors = true; 1151 } 1152 err = NO_ERROR; 1153 } 1154 1155 if (drawables != NULL) { 1156 err = postProcessImages(assets, &table, drawables); 1157 if (err != NO_ERROR) { 1158 hasErrors = true; 1159 } 1160 } 1161 1162 if (colors != NULL) { 1163 ResourceDirIterator it(colors, String8("color")); 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 (menus != NULL) { 1178 ResourceDirIterator it(menus, String8("menu")); 1179 while ((err=it.next()) == NO_ERROR) { 1180 String8 src = it.getFile()->getPrintableSource(); 1181 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1182 if (err != NO_ERROR) { 1183 hasErrors = true; 1184 } 1185 ResXMLTree block; 1186 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); 1187 checkForIds(src, block); 1188 } 1189 1190 if (err < NO_ERROR) { 1191 hasErrors = true; 1192 } 1193 err = NO_ERROR; 1194 } 1195 1196 if (table.validateLocalizations()) { 1197 hasErrors = true; 1198 } 1199 1200 if (hasErrors) { 1201 return UNKNOWN_ERROR; 1202 } 1203 1204 const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0)); 1205 String8 manifestPath(manifestFile->getPrintableSource()); 1206 1207 // Generate final compiled manifest file. 1208 manifestFile->clearData(); 1209 sp<XMLNode> manifestTree = XMLNode::parse(manifestFile); 1210 if (manifestTree == NULL) { 1211 return UNKNOWN_ERROR; 1212 } 1213 err = massageManifest(bundle, manifestTree); 1214 if (err < NO_ERROR) { 1215 return err; 1216 } 1217 err = compileXmlFile(assets, manifestTree, manifestFile, &table); 1218 if (err < NO_ERROR) { 1219 return err; 1220 } 1221 1222 //block.restart(); 1223 //printXMLBlock(&block); 1224 1225 // -------------------------------------------------------------- 1226 // Generate the final resource table. 1227 // Re-flatten because we may have added new resource IDs 1228 // -------------------------------------------------------------- 1229 1230 ResTable finalResTable; 1231 sp<AaptFile> resFile; 1232 1233 if (table.hasResources()) { 1234 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R")); 1235 err = table.addSymbols(symbols); 1236 if (err < NO_ERROR) { 1237 return err; 1238 } 1239 1240 resFile = getResourceFile(assets); 1241 if (resFile == NULL) { 1242 fprintf(stderr, "Error: unable to generate entry for resource data\n"); 1243 return UNKNOWN_ERROR; 1244 } 1245 1246 err = table.flatten(bundle, resFile); 1247 if (err < NO_ERROR) { 1248 return err; 1249 } 1250 1251 if (bundle->getPublicOutputFile()) { 1252 FILE* fp = fopen(bundle->getPublicOutputFile(), "w+"); 1253 if (fp == NULL) { 1254 fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n", 1255 (const char*)bundle->getPublicOutputFile(), strerror(errno)); 1256 return UNKNOWN_ERROR; 1257 } 1258 if (bundle->getVerbose()) { 1259 printf(" Writing public definitions to %s.\n", bundle->getPublicOutputFile()); 1260 } 1261 table.writePublicDefinitions(String16(assets->getPackage()), fp); 1262 fclose(fp); 1263 } 1264 1265 // Read resources back in, 1266 finalResTable.add(resFile->getData(), resFile->getSize(), NULL); 1267 1268 #if 0 1269 NOISY( 1270 printf("Generated resources:\n"); 1271 finalResTable.print(); 1272 ) 1273 #endif 1274 } 1275 1276 // Perform a basic validation of the manifest file. This time we 1277 // parse it with the comments intact, so that we can use them to 1278 // generate java docs... so we are not going to write this one 1279 // back out to the final manifest data. 1280 sp<AaptFile> outManifestFile = new AaptFile(manifestFile->getSourceFile(), 1281 manifestFile->getGroupEntry(), 1282 manifestFile->getResourceType()); 1283 err = compileXmlFile(assets, manifestFile, 1284 outManifestFile, &table, 1285 XML_COMPILE_ASSIGN_ATTRIBUTE_IDS 1286 | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES); 1287 if (err < NO_ERROR) { 1288 return err; 1289 } 1290 ResXMLTree block; 1291 block.setTo(outManifestFile->getData(), outManifestFile->getSize(), true); 1292 String16 manifest16("manifest"); 1293 String16 permission16("permission"); 1294 String16 permission_group16("permission-group"); 1295 String16 uses_permission16("uses-permission"); 1296 String16 instrumentation16("instrumentation"); 1297 String16 application16("application"); 1298 String16 provider16("provider"); 1299 String16 service16("service"); 1300 String16 receiver16("receiver"); 1301 String16 activity16("activity"); 1302 String16 action16("action"); 1303 String16 category16("category"); 1304 String16 data16("scheme"); 1305 const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz" 1306 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789"; 1307 const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz" 1308 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-"; 1309 const char* classIdentChars = "abcdefghijklmnopqrstuvwxyz" 1310 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789$"; 1311 const char* processIdentChars = "abcdefghijklmnopqrstuvwxyz" 1312 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:"; 1313 const char* authoritiesIdentChars = "abcdefghijklmnopqrstuvwxyz" 1314 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-:;"; 1315 const char* typeIdentChars = "abcdefghijklmnopqrstuvwxyz" 1316 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:-/*+"; 1317 const char* schemeIdentChars = "abcdefghijklmnopqrstuvwxyz" 1318 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-"; 1319 ResXMLTree::event_code_t code; 1320 sp<AaptSymbols> permissionSymbols; 1321 sp<AaptSymbols> permissionGroupSymbols; 1322 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 1323 && code > ResXMLTree::BAD_DOCUMENT) { 1324 if (code == ResXMLTree::START_TAG) { 1325 size_t len; 1326 if (block.getElementNamespace(&len) != NULL) { 1327 continue; 1328 } 1329 if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) { 1330 if (validateAttr(manifestPath, finalResTable, block, NULL, "package", 1331 packageIdentChars, true) != ATTR_OKAY) { 1332 hasErrors = true; 1333 } 1334 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1335 "sharedUserId", packageIdentChars, false) != ATTR_OKAY) { 1336 hasErrors = true; 1337 } 1338 } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0 1339 || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) { 1340 const bool isGroup = strcmp16(block.getElementName(&len), 1341 permission_group16.string()) == 0; 1342 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1343 "name", isGroup ? packageIdentCharsWithTheStupid 1344 : packageIdentChars, true) != ATTR_OKAY) { 1345 hasErrors = true; 1346 } 1347 SourcePos srcPos(manifestPath, block.getLineNumber()); 1348 sp<AaptSymbols> syms; 1349 if (!isGroup) { 1350 syms = permissionSymbols; 1351 if (syms == NULL) { 1352 sp<AaptSymbols> symbols = 1353 assets->getSymbolsFor(String8("Manifest")); 1354 syms = permissionSymbols = symbols->addNestedSymbol( 1355 String8("permission"), srcPos); 1356 } 1357 } else { 1358 syms = permissionGroupSymbols; 1359 if (syms == NULL) { 1360 sp<AaptSymbols> symbols = 1361 assets->getSymbolsFor(String8("Manifest")); 1362 syms = permissionGroupSymbols = symbols->addNestedSymbol( 1363 String8("permission_group"), srcPos); 1364 } 1365 } 1366 size_t len; 1367 ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name"); 1368 const uint16_t* id = block.getAttributeStringValue(index, &len); 1369 if (id == NULL) { 1370 fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n", 1371 manifestPath.string(), block.getLineNumber(), 1372 String8(block.getElementName(&len)).string()); 1373 hasErrors = true; 1374 break; 1375 } 1376 String8 idStr(id); 1377 char* p = idStr.lockBuffer(idStr.size()); 1378 char* e = p + idStr.size(); 1379 bool begins_with_digit = true; // init to true so an empty string fails 1380 while (e > p) { 1381 e--; 1382 if (*e >= '0' && *e <= '9') { 1383 begins_with_digit = true; 1384 continue; 1385 } 1386 if ((*e >= 'a' && *e <= 'z') || 1387 (*e >= 'A' && *e <= 'Z') || 1388 (*e == '_')) { 1389 begins_with_digit = false; 1390 continue; 1391 } 1392 if (isGroup && (*e == '-')) { 1393 *e = '_'; 1394 begins_with_digit = false; 1395 continue; 1396 } 1397 e++; 1398 break; 1399 } 1400 idStr.unlockBuffer(); 1401 // verify that we stopped because we hit a period or 1402 // the beginning of the string, and that the 1403 // identifier didn't begin with a digit. 1404 if (begins_with_digit || (e != p && *(e-1) != '.')) { 1405 fprintf(stderr, 1406 "%s:%d: Permission name <%s> is not a valid Java symbol\n", 1407 manifestPath.string(), block.getLineNumber(), idStr.string()); 1408 hasErrors = true; 1409 } 1410 syms->addStringSymbol(String8(e), idStr, srcPos); 1411 const uint16_t* cmt = block.getComment(&len); 1412 if (cmt != NULL && *cmt != 0) { 1413 //printf("Comment of %s: %s\n", String8(e).string(), 1414 // String8(cmt).string()); 1415 syms->appendComment(String8(e), String16(cmt), srcPos); 1416 } else { 1417 //printf("No comment for %s\n", String8(e).string()); 1418 } 1419 syms->makeSymbolPublic(String8(e), srcPos); 1420 } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) { 1421 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1422 "name", packageIdentChars, true) != ATTR_OKAY) { 1423 hasErrors = true; 1424 } 1425 } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) { 1426 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1427 "name", classIdentChars, true) != ATTR_OKAY) { 1428 hasErrors = true; 1429 } 1430 if (validateAttr(manifestPath, finalResTable, block, 1431 RESOURCES_ANDROID_NAMESPACE, "targetPackage", 1432 packageIdentChars, true) != ATTR_OKAY) { 1433 hasErrors = true; 1434 } 1435 } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) { 1436 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1437 "name", classIdentChars, false) != ATTR_OKAY) { 1438 hasErrors = true; 1439 } 1440 if (validateAttr(manifestPath, finalResTable, block, 1441 RESOURCES_ANDROID_NAMESPACE, "permission", 1442 packageIdentChars, false) != ATTR_OKAY) { 1443 hasErrors = true; 1444 } 1445 if (validateAttr(manifestPath, finalResTable, block, 1446 RESOURCES_ANDROID_NAMESPACE, "process", 1447 processIdentChars, false) != ATTR_OKAY) { 1448 hasErrors = true; 1449 } 1450 if (validateAttr(manifestPath, finalResTable, block, 1451 RESOURCES_ANDROID_NAMESPACE, "taskAffinity", 1452 processIdentChars, false) != ATTR_OKAY) { 1453 hasErrors = true; 1454 } 1455 } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) { 1456 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1457 "name", classIdentChars, true) != ATTR_OKAY) { 1458 hasErrors = true; 1459 } 1460 if (validateAttr(manifestPath, finalResTable, block, 1461 RESOURCES_ANDROID_NAMESPACE, "authorities", 1462 authoritiesIdentChars, true) != ATTR_OKAY) { 1463 hasErrors = true; 1464 } 1465 if (validateAttr(manifestPath, finalResTable, block, 1466 RESOURCES_ANDROID_NAMESPACE, "permission", 1467 packageIdentChars, false) != ATTR_OKAY) { 1468 hasErrors = true; 1469 } 1470 if (validateAttr(manifestPath, finalResTable, block, 1471 RESOURCES_ANDROID_NAMESPACE, "process", 1472 processIdentChars, false) != ATTR_OKAY) { 1473 hasErrors = true; 1474 } 1475 } else if (strcmp16(block.getElementName(&len), service16.string()) == 0 1476 || strcmp16(block.getElementName(&len), receiver16.string()) == 0 1477 || strcmp16(block.getElementName(&len), activity16.string()) == 0) { 1478 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1479 "name", classIdentChars, true) != ATTR_OKAY) { 1480 hasErrors = true; 1481 } 1482 if (validateAttr(manifestPath, finalResTable, block, 1483 RESOURCES_ANDROID_NAMESPACE, "permission", 1484 packageIdentChars, false) != ATTR_OKAY) { 1485 hasErrors = true; 1486 } 1487 if (validateAttr(manifestPath, finalResTable, block, 1488 RESOURCES_ANDROID_NAMESPACE, "process", 1489 processIdentChars, false) != ATTR_OKAY) { 1490 hasErrors = true; 1491 } 1492 if (validateAttr(manifestPath, finalResTable, block, 1493 RESOURCES_ANDROID_NAMESPACE, "taskAffinity", 1494 processIdentChars, false) != ATTR_OKAY) { 1495 hasErrors = true; 1496 } 1497 } else if (strcmp16(block.getElementName(&len), action16.string()) == 0 1498 || strcmp16(block.getElementName(&len), category16.string()) == 0) { 1499 if (validateAttr(manifestPath, finalResTable, block, 1500 RESOURCES_ANDROID_NAMESPACE, "name", 1501 packageIdentChars, true) != ATTR_OKAY) { 1502 hasErrors = true; 1503 } 1504 } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) { 1505 if (validateAttr(manifestPath, finalResTable, block, 1506 RESOURCES_ANDROID_NAMESPACE, "mimeType", 1507 typeIdentChars, true) != ATTR_OKAY) { 1508 hasErrors = true; 1509 } 1510 if (validateAttr(manifestPath, finalResTable, block, 1511 RESOURCES_ANDROID_NAMESPACE, "scheme", 1512 schemeIdentChars, true) != ATTR_OKAY) { 1513 hasErrors = true; 1514 } 1515 } 1516 } 1517 } 1518 1519 if (resFile != NULL) { 1520 // These resources are now considered to be a part of the included 1521 // resources, for others to reference. 1522 err = assets->addIncludedResources(resFile); 1523 if (err < NO_ERROR) { 1524 fprintf(stderr, "ERROR: Unable to parse generated resources, aborting.\n"); 1525 return err; 1526 } 1527 } 1528 1529 return err; 1530 } 1531 1532 static const char* getIndentSpace(int indent) 1533 { 1534 static const char whitespace[] = 1535 " "; 1536 1537 return whitespace + sizeof(whitespace) - 1 - indent*4; 1538 } 1539 1540 static status_t fixupSymbol(String16* inoutSymbol) 1541 { 1542 inoutSymbol->replaceAll('.', '_'); 1543 inoutSymbol->replaceAll(':', '_'); 1544 return NO_ERROR; 1545 } 1546 1547 static String16 getAttributeComment(const sp<AaptAssets>& assets, 1548 const String8& name, 1549 String16* outTypeComment = NULL) 1550 { 1551 sp<AaptSymbols> asym = assets->getSymbolsFor(String8("R")); 1552 if (asym != NULL) { 1553 //printf("Got R symbols!\n"); 1554 asym = asym->getNestedSymbols().valueFor(String8("attr")); 1555 if (asym != NULL) { 1556 //printf("Got attrs symbols! comment %s=%s\n", 1557 // name.string(), String8(asym->getComment(name)).string()); 1558 if (outTypeComment != NULL) { 1559 *outTypeComment = asym->getTypeComment(name); 1560 } 1561 return asym->getComment(name); 1562 } 1563 } 1564 return String16(); 1565 } 1566 1567 static status_t writeLayoutClasses( 1568 FILE* fp, const sp<AaptAssets>& assets, 1569 const sp<AaptSymbols>& symbols, int indent, bool includePrivate) 1570 { 1571 const char* indentStr = getIndentSpace(indent); 1572 if (!includePrivate) { 1573 fprintf(fp, "%s/** @doconly */\n", indentStr); 1574 } 1575 fprintf(fp, "%spublic static final class styleable {\n", indentStr); 1576 indent++; 1577 1578 String16 attr16("attr"); 1579 String16 package16(assets->getPackage()); 1580 1581 indentStr = getIndentSpace(indent); 1582 bool hasErrors = false; 1583 1584 size_t i; 1585 size_t N = symbols->getNestedSymbols().size(); 1586 for (i=0; i<N; i++) { 1587 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 1588 String16 nclassName16(symbols->getNestedSymbols().keyAt(i)); 1589 String8 realClassName(nclassName16); 1590 if (fixupSymbol(&nclassName16) != NO_ERROR) { 1591 hasErrors = true; 1592 } 1593 String8 nclassName(nclassName16); 1594 1595 SortedVector<uint32_t> idents; 1596 Vector<uint32_t> origOrder; 1597 Vector<bool> publicFlags; 1598 1599 size_t a; 1600 size_t NA = nsymbols->getSymbols().size(); 1601 for (a=0; a<NA; a++) { 1602 const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a)); 1603 int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32 1604 ? sym.int32Val : 0; 1605 bool isPublic = true; 1606 if (code == 0) { 1607 String16 name16(sym.name); 1608 uint32_t typeSpecFlags; 1609 code = assets->getIncludedResources().identifierForName( 1610 name16.string(), name16.size(), 1611 attr16.string(), attr16.size(), 1612 package16.string(), package16.size(), &typeSpecFlags); 1613 if (code == 0) { 1614 fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n", 1615 nclassName.string(), sym.name.string()); 1616 hasErrors = true; 1617 } 1618 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 1619 } 1620 idents.add(code); 1621 origOrder.add(code); 1622 publicFlags.add(isPublic); 1623 } 1624 1625 NA = idents.size(); 1626 1627 bool deprecated = false; 1628 1629 String16 comment = symbols->getComment(realClassName); 1630 fprintf(fp, "%s/** ", indentStr); 1631 if (comment.size() > 0) { 1632 String8 cmt(comment); 1633 fprintf(fp, "%s\n", cmt.string()); 1634 if (strstr(cmt.string(), "@deprecated") != NULL) { 1635 deprecated = true; 1636 } 1637 } else { 1638 fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string()); 1639 } 1640 bool hasTable = false; 1641 for (a=0; a<NA; a++) { 1642 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 1643 if (pos >= 0) { 1644 if (!hasTable) { 1645 hasTable = true; 1646 fprintf(fp, 1647 "%s <p>Includes the following attributes:</p>\n" 1648 "%s <table>\n" 1649 "%s <colgroup align=\"left\" />\n" 1650 "%s <colgroup align=\"left\" />\n" 1651 "%s <tr><th>Attribute</th><th>Description</th></tr>\n", 1652 indentStr, 1653 indentStr, 1654 indentStr, 1655 indentStr, 1656 indentStr); 1657 } 1658 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 1659 if (!publicFlags.itemAt(a) && !includePrivate) { 1660 continue; 1661 } 1662 String8 name8(sym.name); 1663 String16 comment(sym.comment); 1664 if (comment.size() <= 0) { 1665 comment = getAttributeComment(assets, name8); 1666 } 1667 if (comment.size() > 0) { 1668 const char16_t* p = comment.string(); 1669 while (*p != 0 && *p != '.') { 1670 if (*p == '{') { 1671 while (*p != 0 && *p != '}') { 1672 p++; 1673 } 1674 } else { 1675 p++; 1676 } 1677 } 1678 if (*p == '.') { 1679 p++; 1680 } 1681 comment = String16(comment.string(), p-comment.string()); 1682 } 1683 String16 name(name8); 1684 fixupSymbol(&name); 1685 fprintf(fp, "%s <tr><td><code>{@link #%s_%s %s:%s}</code></td><td>%s</td></tr>\n", 1686 indentStr, nclassName.string(), 1687 String8(name).string(), 1688 assets->getPackage().string(), 1689 String8(name).string(), 1690 String8(comment).string()); 1691 } 1692 } 1693 if (hasTable) { 1694 fprintf(fp, "%s </table>\n", indentStr); 1695 } 1696 for (a=0; a<NA; a++) { 1697 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 1698 if (pos >= 0) { 1699 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 1700 if (!publicFlags.itemAt(a) && !includePrivate) { 1701 continue; 1702 } 1703 String16 name(sym.name); 1704 fixupSymbol(&name); 1705 fprintf(fp, "%s @see #%s_%s\n", 1706 indentStr, nclassName.string(), 1707 String8(name).string()); 1708 } 1709 } 1710 fprintf(fp, "%s */\n", getIndentSpace(indent)); 1711 1712 if (deprecated) { 1713 fprintf(fp, "%s@Deprecated\n", indentStr); 1714 } 1715 1716 fprintf(fp, 1717 "%spublic static final int[] %s = {\n" 1718 "%s", 1719 indentStr, nclassName.string(), 1720 getIndentSpace(indent+1)); 1721 1722 for (a=0; a<NA; a++) { 1723 if (a != 0) { 1724 if ((a&3) == 0) { 1725 fprintf(fp, ",\n%s", getIndentSpace(indent+1)); 1726 } else { 1727 fprintf(fp, ", "); 1728 } 1729 } 1730 fprintf(fp, "0x%08x", idents[a]); 1731 } 1732 1733 fprintf(fp, "\n%s};\n", indentStr); 1734 1735 for (a=0; a<NA; a++) { 1736 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 1737 if (pos >= 0) { 1738 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 1739 if (!publicFlags.itemAt(a) && !includePrivate) { 1740 continue; 1741 } 1742 String8 name8(sym.name); 1743 String16 comment(sym.comment); 1744 String16 typeComment; 1745 if (comment.size() <= 0) { 1746 comment = getAttributeComment(assets, name8, &typeComment); 1747 } else { 1748 getAttributeComment(assets, name8, &typeComment); 1749 } 1750 String16 name(name8); 1751 if (fixupSymbol(&name) != NO_ERROR) { 1752 hasErrors = true; 1753 } 1754 1755 uint32_t typeSpecFlags = 0; 1756 String16 name16(sym.name); 1757 assets->getIncludedResources().identifierForName( 1758 name16.string(), name16.size(), 1759 attr16.string(), attr16.size(), 1760 package16.string(), package16.size(), &typeSpecFlags); 1761 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(), 1762 // String8(attr16).string(), String8(name16).string(), typeSpecFlags); 1763 const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 1764 1765 bool deprecated = false; 1766 1767 fprintf(fp, "%s/**\n", indentStr); 1768 if (comment.size() > 0) { 1769 String8 cmt(comment); 1770 fprintf(fp, "%s <p>\n%s @attr description\n", indentStr, indentStr); 1771 fprintf(fp, "%s %s\n", indentStr, cmt.string()); 1772 if (strstr(cmt.string(), "@deprecated") != NULL) { 1773 deprecated = true; 1774 } 1775 } else { 1776 fprintf(fp, 1777 "%s <p>This symbol is the offset where the {@link %s.R.attr#%s}\n" 1778 "%s attribute's value can be found in the {@link #%s} array.\n", 1779 indentStr, 1780 pub ? assets->getPackage().string() 1781 : assets->getSymbolsPrivatePackage().string(), 1782 String8(name).string(), 1783 indentStr, nclassName.string()); 1784 } 1785 if (typeComment.size() > 0) { 1786 String8 cmt(typeComment); 1787 fprintf(fp, "\n\n%s %s\n", indentStr, cmt.string()); 1788 if (strstr(cmt.string(), "@deprecated") != NULL) { 1789 deprecated = true; 1790 } 1791 } 1792 if (comment.size() > 0) { 1793 if (pub) { 1794 fprintf(fp, 1795 "%s <p>This corresponds to the global attribute" 1796 "%s resource symbol {@link %s.R.attr#%s}.\n", 1797 indentStr, indentStr, 1798 assets->getPackage().string(), 1799 String8(name).string()); 1800 } else { 1801 fprintf(fp, 1802 "%s <p>This is a private symbol.\n", indentStr); 1803 } 1804 } 1805 fprintf(fp, "%s @attr name %s:%s\n", indentStr, 1806 "android", String8(name).string()); 1807 fprintf(fp, "%s*/\n", indentStr); 1808 if (deprecated) { 1809 fprintf(fp, "%s@Deprecated\n", indentStr); 1810 } 1811 fprintf(fp, 1812 "%spublic static final int %s_%s = %d;\n", 1813 indentStr, nclassName.string(), 1814 String8(name).string(), (int)pos); 1815 } 1816 } 1817 } 1818 1819 indent--; 1820 fprintf(fp, "%s};\n", getIndentSpace(indent)); 1821 return hasErrors ? UNKNOWN_ERROR : NO_ERROR; 1822 } 1823 1824 static status_t writeSymbolClass( 1825 FILE* fp, const sp<AaptAssets>& assets, bool includePrivate, 1826 const sp<AaptSymbols>& symbols, const String8& className, int indent, 1827 bool nonConstantId) 1828 { 1829 fprintf(fp, "%spublic %sfinal class %s {\n", 1830 getIndentSpace(indent), 1831 indent != 0 ? "static " : "", className.string()); 1832 indent++; 1833 1834 size_t i; 1835 status_t err = NO_ERROR; 1836 1837 const char * id_format = nonConstantId ? 1838 "%spublic static int %s=0x%08x;\n" : 1839 "%spublic static final int %s=0x%08x;\n"; 1840 1841 size_t N = symbols->getSymbols().size(); 1842 for (i=0; i<N; i++) { 1843 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 1844 if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) { 1845 continue; 1846 } 1847 if (!assets->isJavaSymbol(sym, includePrivate)) { 1848 continue; 1849 } 1850 String16 name(sym.name); 1851 String8 realName(name); 1852 if (fixupSymbol(&name) != NO_ERROR) { 1853 return UNKNOWN_ERROR; 1854 } 1855 String16 comment(sym.comment); 1856 bool haveComment = false; 1857 bool deprecated = false; 1858 if (comment.size() > 0) { 1859 haveComment = true; 1860 String8 cmt(comment); 1861 fprintf(fp, 1862 "%s/** %s\n", 1863 getIndentSpace(indent), cmt.string()); 1864 if (strstr(cmt.string(), "@deprecated") != NULL) { 1865 deprecated = true; 1866 } 1867 } else if (sym.isPublic && !includePrivate) { 1868 sym.sourcePos.warning("No comment for public symbol %s:%s/%s", 1869 assets->getPackage().string(), className.string(), 1870 String8(sym.name).string()); 1871 } 1872 String16 typeComment(sym.typeComment); 1873 if (typeComment.size() > 0) { 1874 String8 cmt(typeComment); 1875 if (!haveComment) { 1876 haveComment = true; 1877 fprintf(fp, 1878 "%s/** %s\n", getIndentSpace(indent), cmt.string()); 1879 } else { 1880 fprintf(fp, 1881 "%s %s\n", getIndentSpace(indent), cmt.string()); 1882 } 1883 if (strstr(cmt.string(), "@deprecated") != NULL) { 1884 deprecated = true; 1885 } 1886 } 1887 if (haveComment) { 1888 fprintf(fp,"%s */\n", getIndentSpace(indent)); 1889 } 1890 if (deprecated) { 1891 fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent)); 1892 } 1893 fprintf(fp, id_format, 1894 getIndentSpace(indent), 1895 String8(name).string(), (int)sym.int32Val); 1896 } 1897 1898 for (i=0; i<N; i++) { 1899 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 1900 if (sym.typeCode != AaptSymbolEntry::TYPE_STRING) { 1901 continue; 1902 } 1903 if (!assets->isJavaSymbol(sym, includePrivate)) { 1904 continue; 1905 } 1906 String16 name(sym.name); 1907 if (fixupSymbol(&name) != NO_ERROR) { 1908 return UNKNOWN_ERROR; 1909 } 1910 String16 comment(sym.comment); 1911 bool deprecated = false; 1912 if (comment.size() > 0) { 1913 String8 cmt(comment); 1914 fprintf(fp, 1915 "%s/** %s\n" 1916 "%s */\n", 1917 getIndentSpace(indent), cmt.string(), 1918 getIndentSpace(indent)); 1919 if (strstr(cmt.string(), "@deprecated") != NULL) { 1920 deprecated = true; 1921 } 1922 } else if (sym.isPublic && !includePrivate) { 1923 sym.sourcePos.warning("No comment for public symbol %s:%s/%s", 1924 assets->getPackage().string(), className.string(), 1925 String8(sym.name).string()); 1926 } 1927 if (deprecated) { 1928 fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent)); 1929 } 1930 fprintf(fp, "%spublic static final String %s=\"%s\";\n", 1931 getIndentSpace(indent), 1932 String8(name).string(), sym.stringVal.string()); 1933 } 1934 1935 sp<AaptSymbols> styleableSymbols; 1936 1937 N = symbols->getNestedSymbols().size(); 1938 for (i=0; i<N; i++) { 1939 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 1940 String8 nclassName(symbols->getNestedSymbols().keyAt(i)); 1941 if (nclassName == "styleable") { 1942 styleableSymbols = nsymbols; 1943 } else { 1944 err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent, nonConstantId); 1945 } 1946 if (err != NO_ERROR) { 1947 return err; 1948 } 1949 } 1950 1951 if (styleableSymbols != NULL) { 1952 err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate); 1953 if (err != NO_ERROR) { 1954 return err; 1955 } 1956 } 1957 1958 indent--; 1959 fprintf(fp, "%s}\n", getIndentSpace(indent)); 1960 return NO_ERROR; 1961 } 1962 1963 status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets, 1964 const String8& package, bool includePrivate) 1965 { 1966 if (!bundle->getRClassDir()) { 1967 return NO_ERROR; 1968 } 1969 1970 const size_t N = assets->getSymbols().size(); 1971 for (size_t i=0; i<N; i++) { 1972 sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i); 1973 String8 className(assets->getSymbols().keyAt(i)); 1974 String8 dest(bundle->getRClassDir()); 1975 if (bundle->getMakePackageDirs()) { 1976 String8 pkg(package); 1977 const char* last = pkg.string(); 1978 const char* s = last-1; 1979 do { 1980 s++; 1981 if (s > last && (*s == '.' || *s == 0)) { 1982 String8 part(last, s-last); 1983 dest.appendPath(part); 1984 #ifdef HAVE_MS_C_RUNTIME 1985 _mkdir(dest.string()); 1986 #else 1987 mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP); 1988 #endif 1989 last = s+1; 1990 } 1991 } while (*s); 1992 } 1993 dest.appendPath(className); 1994 dest.append(".java"); 1995 FILE* fp = fopen(dest.string(), "w+"); 1996 if (fp == NULL) { 1997 fprintf(stderr, "ERROR: Unable to open class file %s: %s\n", 1998 dest.string(), strerror(errno)); 1999 return UNKNOWN_ERROR; 2000 } 2001 if (bundle->getVerbose()) { 2002 printf(" Writing symbols for class %s.\n", className.string()); 2003 } 2004 2005 fprintf(fp, 2006 "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n" 2007 " *\n" 2008 " * This class was automatically generated by the\n" 2009 " * aapt tool from the resource data it found. It\n" 2010 " * should not be modified by hand.\n" 2011 " */\n" 2012 "\n" 2013 "package %s;\n\n", package.string()); 2014 2015 status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, 2016 className, 0, bundle->getNonConstantId()); 2017 if (err != NO_ERROR) { 2018 return err; 2019 } 2020 fclose(fp); 2021 2022 // If we were asked to generate a dependency file, we'll go ahead and add this R.java 2023 // as a target in the dependency file right next to it. 2024 if (bundle->getGenDependencies()) { 2025 // Add this R.java to the dependency file 2026 String8 dependencyFile(bundle->getRClassDir()); 2027 dependencyFile.appendPath("R.java.d"); 2028 2029 fp = fopen(dependencyFile.string(), "a"); 2030 fprintf(fp,"%s \\\n", dest.string()); 2031 fclose(fp); 2032 } 2033 } 2034 2035 return NO_ERROR; 2036 } 2037 2038 2039 2040 class ProguardKeepSet 2041 { 2042 public: 2043 // { rule --> { file locations } } 2044 KeyedVector<String8, SortedVector<String8> > rules; 2045 2046 void add(const String8& rule, const String8& where); 2047 }; 2048 2049 void ProguardKeepSet::add(const String8& rule, const String8& where) 2050 { 2051 ssize_t index = rules.indexOfKey(rule); 2052 if (index < 0) { 2053 index = rules.add(rule, SortedVector<String8>()); 2054 } 2055 rules.editValueAt(index).add(where); 2056 } 2057 2058 void 2059 addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName, 2060 const char* pkg, const String8& srcName, int line) 2061 { 2062 String8 className(inClassName); 2063 if (pkg != NULL) { 2064 // asdf --> package.asdf 2065 // .asdf .a.b --> package.asdf package.a.b 2066 // asdf.adsf --> asdf.asdf 2067 const char* p = className.string(); 2068 const char* q = strchr(p, '.'); 2069 if (p == q) { 2070 className = pkg; 2071 className.append(inClassName); 2072 } else if (q == NULL) { 2073 className = pkg; 2074 className.append("."); 2075 className.append(inClassName); 2076 } 2077 } 2078 2079 String8 rule("-keep class "); 2080 rule += className; 2081 rule += " { <init>(...); }"; 2082 2083 String8 location("view "); 2084 location += srcName; 2085 char lineno[20]; 2086 sprintf(lineno, ":%d", line); 2087 location += lineno; 2088 2089 keep->add(rule, location); 2090 } 2091 2092 void 2093 addProguardKeepMethodRule(ProguardKeepSet* keep, const String8& memberName, 2094 const char* pkg, const String8& srcName, int line) 2095 { 2096 String8 rule("-keepclassmembers class * { *** "); 2097 rule += memberName; 2098 rule += "(...); }"; 2099 2100 String8 location("onClick "); 2101 location += srcName; 2102 char lineno[20]; 2103 sprintf(lineno, ":%d", line); 2104 location += lineno; 2105 2106 keep->add(rule, location); 2107 } 2108 2109 status_t 2110 writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets) 2111 { 2112 status_t err; 2113 ResXMLTree tree; 2114 size_t len; 2115 ResXMLTree::event_code_t code; 2116 int depth = 0; 2117 bool inApplication = false; 2118 String8 error; 2119 sp<AaptGroup> assGroup; 2120 sp<AaptFile> assFile; 2121 String8 pkg; 2122 2123 // First, look for a package file to parse. This is required to 2124 // be able to generate the resource information. 2125 assGroup = assets->getFiles().valueFor(String8("AndroidManifest.xml")); 2126 if (assGroup == NULL) { 2127 fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n"); 2128 return -1; 2129 } 2130 2131 if (assGroup->getFiles().size() != 1) { 2132 fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n", 2133 assGroup->getFiles().valueAt(0)->getPrintableSource().string()); 2134 } 2135 2136 assFile = assGroup->getFiles().valueAt(0); 2137 2138 err = parseXMLResource(assFile, &tree); 2139 if (err != NO_ERROR) { 2140 return err; 2141 } 2142 2143 tree.restart(); 2144 2145 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 2146 if (code == ResXMLTree::END_TAG) { 2147 if (/* name == "Application" && */ depth == 2) { 2148 inApplication = false; 2149 } 2150 depth--; 2151 continue; 2152 } 2153 if (code != ResXMLTree::START_TAG) { 2154 continue; 2155 } 2156 depth++; 2157 String8 tag(tree.getElementName(&len)); 2158 // printf("Depth %d tag %s\n", depth, tag.string()); 2159 bool keepTag = false; 2160 if (depth == 1) { 2161 if (tag != "manifest") { 2162 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 2163 return -1; 2164 } 2165 pkg = getAttribute(tree, NULL, "package", NULL); 2166 } else if (depth == 2) { 2167 if (tag == "application") { 2168 inApplication = true; 2169 keepTag = true; 2170 2171 String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android", 2172 "backupAgent", &error); 2173 if (agent.length() > 0) { 2174 addProguardKeepRule(keep, agent, pkg.string(), 2175 assFile->getPrintableSource(), tree.getLineNumber()); 2176 } 2177 } else if (tag == "instrumentation") { 2178 keepTag = true; 2179 } 2180 } 2181 if (!keepTag && inApplication && depth == 3) { 2182 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") { 2183 keepTag = true; 2184 } 2185 } 2186 if (keepTag) { 2187 String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android", 2188 "name", &error); 2189 if (error != "") { 2190 fprintf(stderr, "ERROR: %s\n", error.string()); 2191 return -1; 2192 } 2193 if (name.length() > 0) { 2194 addProguardKeepRule(keep, name, pkg.string(), 2195 assFile->getPrintableSource(), tree.getLineNumber()); 2196 } 2197 } 2198 } 2199 2200 return NO_ERROR; 2201 } 2202 2203 struct NamespaceAttributePair { 2204 const char* ns; 2205 const char* attr; 2206 2207 NamespaceAttributePair(const char* n, const char* a) : ns(n), attr(a) {} 2208 NamespaceAttributePair() : ns(NULL), attr(NULL) {} 2209 }; 2210 2211 status_t 2212 writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile, 2213 const char* startTag, const KeyedVector<String8, NamespaceAttributePair>* tagAttrPairs) 2214 { 2215 status_t err; 2216 ResXMLTree tree; 2217 size_t len; 2218 ResXMLTree::event_code_t code; 2219 2220 err = parseXMLResource(layoutFile, &tree); 2221 if (err != NO_ERROR) { 2222 return err; 2223 } 2224 2225 tree.restart(); 2226 2227 if (startTag != NULL) { 2228 bool haveStart = false; 2229 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 2230 if (code != ResXMLTree::START_TAG) { 2231 continue; 2232 } 2233 String8 tag(tree.getElementName(&len)); 2234 if (tag == startTag) { 2235 haveStart = true; 2236 } 2237 break; 2238 } 2239 if (!haveStart) { 2240 return NO_ERROR; 2241 } 2242 } 2243 2244 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 2245 if (code != ResXMLTree::START_TAG) { 2246 continue; 2247 } 2248 String8 tag(tree.getElementName(&len)); 2249 2250 // If there is no '.', we'll assume that it's one of the built in names. 2251 if (strchr(tag.string(), '.')) { 2252 addProguardKeepRule(keep, tag, NULL, 2253 layoutFile->getPrintableSource(), tree.getLineNumber()); 2254 } else if (tagAttrPairs != NULL) { 2255 ssize_t tagIndex = tagAttrPairs->indexOfKey(tag); 2256 if (tagIndex >= 0) { 2257 const NamespaceAttributePair& nsAttr = tagAttrPairs->valueAt(tagIndex); 2258 ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr); 2259 if (attrIndex < 0) { 2260 // fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n", 2261 // layoutFile->getPrintableSource().string(), tree.getLineNumber(), 2262 // tag.string(), nsAttr.ns, nsAttr.attr); 2263 } else { 2264 size_t len; 2265 addProguardKeepRule(keep, 2266 String8(tree.getAttributeStringValue(attrIndex, &len)), NULL, 2267 layoutFile->getPrintableSource(), tree.getLineNumber()); 2268 } 2269 } 2270 } 2271 ssize_t attrIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "onClick"); 2272 if (attrIndex >= 0) { 2273 size_t len; 2274 addProguardKeepMethodRule(keep, 2275 String8(tree.getAttributeStringValue(attrIndex, &len)), NULL, 2276 layoutFile->getPrintableSource(), tree.getLineNumber()); 2277 } 2278 } 2279 2280 return NO_ERROR; 2281 } 2282 2283 static void addTagAttrPair(KeyedVector<String8, NamespaceAttributePair>* dest, 2284 const char* tag, const char* ns, const char* attr) { 2285 dest->add(String8(tag), NamespaceAttributePair(ns, attr)); 2286 } 2287 2288 status_t 2289 writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets) 2290 { 2291 status_t err; 2292 2293 // tag:attribute pairs that should be checked in layout files. 2294 KeyedVector<String8, NamespaceAttributePair> kLayoutTagAttrPairs; 2295 addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class"); 2296 addTagAttrPair(&kLayoutTagAttrPairs, "fragment", NULL, "class"); 2297 addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name"); 2298 2299 // tag:attribute pairs that should be checked in xml files. 2300 KeyedVector<String8, NamespaceAttributePair> kXmlTagAttrPairs; 2301 addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment"); 2302 addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment"); 2303 2304 const Vector<sp<AaptDir> >& dirs = assets->resDirs(); 2305 const size_t K = dirs.size(); 2306 for (size_t k=0; k<K; k++) { 2307 const sp<AaptDir>& d = dirs.itemAt(k); 2308 const String8& dirName = d->getLeaf(); 2309 const char* startTag = NULL; 2310 const KeyedVector<String8, NamespaceAttributePair>* tagAttrPairs = NULL; 2311 if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) { 2312 tagAttrPairs = &kLayoutTagAttrPairs; 2313 } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) { 2314 startTag = "PreferenceScreen"; 2315 tagAttrPairs = &kXmlTagAttrPairs; 2316 } else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) { 2317 startTag = "menu"; 2318 tagAttrPairs = NULL; 2319 } else { 2320 continue; 2321 } 2322 2323 const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles(); 2324 const size_t N = groups.size(); 2325 for (size_t i=0; i<N; i++) { 2326 const sp<AaptGroup>& group = groups.valueAt(i); 2327 const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles(); 2328 const size_t M = files.size(); 2329 for (size_t j=0; j<M; j++) { 2330 err = writeProguardForXml(keep, files.valueAt(j), startTag, tagAttrPairs); 2331 if (err < 0) { 2332 return err; 2333 } 2334 } 2335 } 2336 } 2337 // Handle the overlays 2338 sp<AaptAssets> overlay = assets->getOverlay(); 2339 if (overlay.get()) { 2340 return writeProguardForLayouts(keep, overlay); 2341 } 2342 return NO_ERROR; 2343 } 2344 2345 status_t 2346 writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets) 2347 { 2348 status_t err = -1; 2349 2350 if (!bundle->getProguardFile()) { 2351 return NO_ERROR; 2352 } 2353 2354 ProguardKeepSet keep; 2355 2356 err = writeProguardForAndroidManifest(&keep, assets); 2357 if (err < 0) { 2358 return err; 2359 } 2360 2361 err = writeProguardForLayouts(&keep, assets); 2362 if (err < 0) { 2363 return err; 2364 } 2365 2366 FILE* fp = fopen(bundle->getProguardFile(), "w+"); 2367 if (fp == NULL) { 2368 fprintf(stderr, "ERROR: Unable to open class file %s: %s\n", 2369 bundle->getProguardFile(), strerror(errno)); 2370 return UNKNOWN_ERROR; 2371 } 2372 2373 const KeyedVector<String8, SortedVector<String8> >& rules = keep.rules; 2374 const size_t N = rules.size(); 2375 for (size_t i=0; i<N; i++) { 2376 const SortedVector<String8>& locations = rules.valueAt(i); 2377 const size_t M = locations.size(); 2378 for (size_t j=0; j<M; j++) { 2379 fprintf(fp, "# %s\n", locations.itemAt(j).string()); 2380 } 2381 fprintf(fp, "%s\n\n", rules.keyAt(i).string()); 2382 } 2383 fclose(fp); 2384 2385 return err; 2386 } 2387 2388 // Loops through the string paths and writes them to the file pointer 2389 // Each file path is written on its own line with a terminating backslash. 2390 status_t writePathsToFile(const sp<FilePathStore>& files, FILE* fp) 2391 { 2392 status_t deps = -1; 2393 for (size_t file_i = 0; file_i < files->size(); ++file_i) { 2394 // Add the full file path to the dependency file 2395 fprintf(fp, "%s \\\n", files->itemAt(file_i).string()); 2396 deps++; 2397 } 2398 return deps; 2399 } 2400 2401 status_t 2402 writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw) 2403 { 2404 status_t deps = -1; 2405 deps += writePathsToFile(assets->getFullResPaths(), fp); 2406 if (includeRaw) { 2407 deps += writePathsToFile(assets->getFullAssetPaths(), fp); 2408 } 2409 return deps; 2410 } 2411