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