Command.cpp revision 2675f769673f69b0661ddee346292f25cb30a296
1// 2// Copyright 2006 The Android Open Source Project 3// 4// Android Asset Packaging Tool main entry point. 5// 6#include "Main.h" 7#include "Bundle.h" 8#include "ResourceFilter.h" 9#include "ResourceTable.h" 10#include "Images.h" 11#include "XMLNode.h" 12 13#include <utils/Log.h> 14#include <utils/threads.h> 15#include <utils/List.h> 16#include <utils/Errors.h> 17 18#include <fcntl.h> 19#include <errno.h> 20 21using namespace android; 22 23/* 24 * Show version info. All the cool kids do it. 25 */ 26int doVersion(Bundle* bundle) 27{ 28 if (bundle->getFileSpecCount() != 0) { 29 printf("(ignoring extra arguments)\n"); 30 } 31 printf("Android Asset Packaging Tool, v0.2\n"); 32 33 return 0; 34} 35 36 37/* 38 * Open the file read only. The call fails if the file doesn't exist. 39 * 40 * Returns NULL on failure. 41 */ 42ZipFile* openReadOnly(const char* fileName) 43{ 44 ZipFile* zip; 45 status_t result; 46 47 zip = new ZipFile; 48 result = zip->open(fileName, ZipFile::kOpenReadOnly); 49 if (result != NO_ERROR) { 50 if (result == NAME_NOT_FOUND) { 51 fprintf(stderr, "ERROR: '%s' not found\n", fileName); 52 } else if (result == PERMISSION_DENIED) { 53 fprintf(stderr, "ERROR: '%s' access denied\n", fileName); 54 } else { 55 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n", 56 fileName); 57 } 58 delete zip; 59 return NULL; 60 } 61 62 return zip; 63} 64 65/* 66 * Open the file read-write. The file will be created if it doesn't 67 * already exist and "okayToCreate" is set. 68 * 69 * Returns NULL on failure. 70 */ 71ZipFile* openReadWrite(const char* fileName, bool okayToCreate) 72{ 73 ZipFile* zip = NULL; 74 status_t result; 75 int flags; 76 77 flags = ZipFile::kOpenReadWrite; 78 if (okayToCreate) { 79 flags |= ZipFile::kOpenCreate; 80 } 81 82 zip = new ZipFile; 83 result = zip->open(fileName, flags); 84 if (result != NO_ERROR) { 85 delete zip; 86 zip = NULL; 87 goto bail; 88 } 89 90bail: 91 return zip; 92} 93 94 95/* 96 * Return a short string describing the compression method. 97 */ 98const char* compressionName(int method) 99{ 100 if (method == ZipEntry::kCompressStored) { 101 return "Stored"; 102 } else if (method == ZipEntry::kCompressDeflated) { 103 return "Deflated"; 104 } else { 105 return "Unknown"; 106 } 107} 108 109/* 110 * Return the percent reduction in size (0% == no compression). 111 */ 112int calcPercent(long uncompressedLen, long compressedLen) 113{ 114 if (!uncompressedLen) { 115 return 0; 116 } else { 117 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5); 118 } 119} 120 121/* 122 * Handle the "list" command, which can be a simple file dump or 123 * a verbose listing. 124 * 125 * The verbose listing closely matches the output of the Info-ZIP "unzip" 126 * command. 127 */ 128int doList(Bundle* bundle) 129{ 130 int result = 1; 131 ZipFile* zip = NULL; 132 const ZipEntry* entry; 133 long totalUncLen, totalCompLen; 134 const char* zipFileName; 135 136 if (bundle->getFileSpecCount() != 1) { 137 fprintf(stderr, "ERROR: specify zip file name (only)\n"); 138 goto bail; 139 } 140 zipFileName = bundle->getFileSpecEntry(0); 141 142 zip = openReadOnly(zipFileName); 143 if (zip == NULL) { 144 goto bail; 145 } 146 147 int count, i; 148 149 if (bundle->getVerbose()) { 150 printf("Archive: %s\n", zipFileName); 151 printf( 152 " Length Method Size Ratio Offset Date Time CRC-32 Name\n"); 153 printf( 154 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n"); 155 } 156 157 totalUncLen = totalCompLen = 0; 158 159 count = zip->getNumEntries(); 160 for (i = 0; i < count; i++) { 161 entry = zip->getEntryByIndex(i); 162 if (bundle->getVerbose()) { 163 char dateBuf[32]; 164 time_t when; 165 166 when = entry->getModWhen(); 167 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M", 168 localtime(&when)); 169 170 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n", 171 (long) entry->getUncompressedLen(), 172 compressionName(entry->getCompressionMethod()), 173 (long) entry->getCompressedLen(), 174 calcPercent(entry->getUncompressedLen(), 175 entry->getCompressedLen()), 176 (size_t) entry->getLFHOffset(), 177 dateBuf, 178 entry->getCRC32(), 179 entry->getFileName()); 180 } else { 181 printf("%s\n", entry->getFileName()); 182 } 183 184 totalUncLen += entry->getUncompressedLen(); 185 totalCompLen += entry->getCompressedLen(); 186 } 187 188 if (bundle->getVerbose()) { 189 printf( 190 "-------- ------- --- -------\n"); 191 printf("%8ld %7ld %2d%% %d files\n", 192 totalUncLen, 193 totalCompLen, 194 calcPercent(totalUncLen, totalCompLen), 195 zip->getNumEntries()); 196 } 197 198 if (bundle->getAndroidList()) { 199 AssetManager assets; 200 if (!assets.addAssetPath(String8(zipFileName), NULL)) { 201 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n"); 202 goto bail; 203 } 204 205 const ResTable& res = assets.getResources(false); 206 if (&res == NULL) { 207 printf("\nNo resource table found.\n"); 208 } else { 209#ifndef HAVE_ANDROID_OS 210 printf("\nResource table:\n"); 211 res.print(false); 212#endif 213 } 214 215 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml", 216 Asset::ACCESS_BUFFER); 217 if (manifestAsset == NULL) { 218 printf("\nNo AndroidManifest.xml found.\n"); 219 } else { 220 printf("\nAndroid manifest:\n"); 221 ResXMLTree tree; 222 tree.setTo(manifestAsset->getBuffer(true), 223 manifestAsset->getLength()); 224 printXMLBlock(&tree); 225 } 226 delete manifestAsset; 227 } 228 229 result = 0; 230 231bail: 232 delete zip; 233 return result; 234} 235 236static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes) 237{ 238 size_t N = tree.getAttributeCount(); 239 for (size_t i=0; i<N; i++) { 240 if (tree.getAttributeNameResID(i) == attrRes) { 241 return (ssize_t)i; 242 } 243 } 244 return -1; 245} 246 247String8 getAttribute(const ResXMLTree& tree, const char* ns, 248 const char* attr, String8* outError) 249{ 250 ssize_t idx = tree.indexOfAttribute(ns, attr); 251 if (idx < 0) { 252 return String8(); 253 } 254 Res_value value; 255 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 256 if (value.dataType != Res_value::TYPE_STRING) { 257 if (outError != NULL) { 258 *outError = "attribute is not a string value"; 259 } 260 return String8(); 261 } 262 } 263 size_t len; 264 const uint16_t* str = tree.getAttributeStringValue(idx, &len); 265 return str ? String8(str, len) : String8(); 266} 267 268static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError) 269{ 270 ssize_t idx = indexOfAttribute(tree, attrRes); 271 if (idx < 0) { 272 return String8(); 273 } 274 Res_value value; 275 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 276 if (value.dataType != Res_value::TYPE_STRING) { 277 if (outError != NULL) { 278 *outError = "attribute is not a string value"; 279 } 280 return String8(); 281 } 282 } 283 size_t len; 284 const uint16_t* str = tree.getAttributeStringValue(idx, &len); 285 return str ? String8(str, len) : String8(); 286} 287 288static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes, 289 String8* outError, int32_t defValue = -1) 290{ 291 ssize_t idx = indexOfAttribute(tree, attrRes); 292 if (idx < 0) { 293 return defValue; 294 } 295 Res_value value; 296 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 297 if (value.dataType < Res_value::TYPE_FIRST_INT 298 || value.dataType > Res_value::TYPE_LAST_INT) { 299 if (outError != NULL) { 300 *outError = "attribute is not an integer value"; 301 } 302 return defValue; 303 } 304 } 305 return value.data; 306} 307 308static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree, 309 uint32_t attrRes, String8* outError, int32_t defValue = -1) 310{ 311 ssize_t idx = indexOfAttribute(tree, attrRes); 312 if (idx < 0) { 313 return defValue; 314 } 315 Res_value value; 316 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 317 if (value.dataType == Res_value::TYPE_REFERENCE) { 318 resTable->resolveReference(&value, 0); 319 } 320 if (value.dataType < Res_value::TYPE_FIRST_INT 321 || value.dataType > Res_value::TYPE_LAST_INT) { 322 if (outError != NULL) { 323 *outError = "attribute is not an integer value"; 324 } 325 return defValue; 326 } 327 } 328 return value.data; 329} 330 331static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree, 332 uint32_t attrRes, String8* outError) 333{ 334 ssize_t idx = indexOfAttribute(tree, attrRes); 335 if (idx < 0) { 336 return String8(); 337 } 338 Res_value value; 339 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 340 if (value.dataType == Res_value::TYPE_STRING) { 341 size_t len; 342 const uint16_t* str = tree.getAttributeStringValue(idx, &len); 343 return str ? String8(str, len) : String8(); 344 } 345 resTable->resolveReference(&value, 0); 346 if (value.dataType != Res_value::TYPE_STRING) { 347 if (outError != NULL) { 348 *outError = "attribute is not a string value"; 349 } 350 return String8(); 351 } 352 } 353 size_t len; 354 const Res_value* value2 = &value; 355 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len); 356 return str ? String8(str, len) : String8(); 357} 358 359static void getResolvedResourceAttribute(Res_value* value, const ResTable* resTable, 360 const ResXMLTree& tree, uint32_t attrRes, String8* outError) 361{ 362 ssize_t idx = indexOfAttribute(tree, attrRes); 363 if (idx < 0) { 364 if (outError != NULL) { 365 *outError = "attribute could not be found"; 366 } 367 return; 368 } 369 if (tree.getAttributeValue(idx, value) != NO_ERROR) { 370 if (value->dataType == Res_value::TYPE_REFERENCE) { 371 resTable->resolveReference(value, 0); 372 } 373 // The attribute was found and was resolved if need be. 374 return; 375 } 376 if (outError != NULL) { 377 *outError = "error getting resolved resource attribute"; 378 } 379} 380 381static void printResolvedResourceAttribute(const ResTable* resTable, const ResXMLTree& tree, 382 uint32_t attrRes, String8 attrLabel, String8* outError) 383{ 384 Res_value value; 385 getResolvedResourceAttribute(&value, resTable, tree, attrRes, outError); 386 if (*outError != "") { 387 *outError = "error print resolved resource attribute"; 388 return; 389 } 390 if (value.dataType == Res_value::TYPE_STRING) { 391 String8 result = getResolvedAttribute(resTable, tree, attrRes, outError); 392 printf("%s='%s'", attrLabel.string(), 393 ResTable::normalizeForOutput(result.string()).string()); 394 } else if (Res_value::TYPE_FIRST_INT <= value.dataType && 395 value.dataType <= Res_value::TYPE_LAST_INT) { 396 printf("%s='%d'", attrLabel.string(), value.data); 397 } else { 398 printf("%s='0x%x'", attrLabel.string(), (int)value.data); 399 } 400} 401 402// These are attribute resource constants for the platform, as found 403// in android.R.attr 404enum { 405 LABEL_ATTR = 0x01010001, 406 ICON_ATTR = 0x01010002, 407 NAME_ATTR = 0x01010003, 408 PERMISSION_ATTR = 0x01010006, 409 RESOURCE_ATTR = 0x01010025, 410 DEBUGGABLE_ATTR = 0x0101000f, 411 VALUE_ATTR = 0x01010024, 412 VERSION_CODE_ATTR = 0x0101021b, 413 VERSION_NAME_ATTR = 0x0101021c, 414 SCREEN_ORIENTATION_ATTR = 0x0101001e, 415 MIN_SDK_VERSION_ATTR = 0x0101020c, 416 MAX_SDK_VERSION_ATTR = 0x01010271, 417 REQ_TOUCH_SCREEN_ATTR = 0x01010227, 418 REQ_KEYBOARD_TYPE_ATTR = 0x01010228, 419 REQ_HARD_KEYBOARD_ATTR = 0x01010229, 420 REQ_NAVIGATION_ATTR = 0x0101022a, 421 REQ_FIVE_WAY_NAV_ATTR = 0x01010232, 422 TARGET_SDK_VERSION_ATTR = 0x01010270, 423 TEST_ONLY_ATTR = 0x01010272, 424 ANY_DENSITY_ATTR = 0x0101026c, 425 GL_ES_VERSION_ATTR = 0x01010281, 426 SMALL_SCREEN_ATTR = 0x01010284, 427 NORMAL_SCREEN_ATTR = 0x01010285, 428 LARGE_SCREEN_ATTR = 0x01010286, 429 XLARGE_SCREEN_ATTR = 0x010102bf, 430 REQUIRED_ATTR = 0x0101028e, 431 SCREEN_SIZE_ATTR = 0x010102ca, 432 SCREEN_DENSITY_ATTR = 0x010102cb, 433 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364, 434 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365, 435 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366, 436 PUBLIC_KEY_ATTR = 0x010103a6, 437 CATEGORY_ATTR = 0x010103e8, 438}; 439 440String8 getComponentName(String8 &pkgName, String8 &componentName) { 441 ssize_t idx = componentName.find("."); 442 String8 retStr(pkgName); 443 if (idx == 0) { 444 retStr += componentName; 445 } else if (idx < 0) { 446 retStr += "."; 447 retStr += componentName; 448 } else { 449 return componentName; 450 } 451 return retStr; 452} 453 454static void printCompatibleScreens(ResXMLTree& tree) { 455 size_t len; 456 ResXMLTree::event_code_t code; 457 int depth = 0; 458 bool first = true; 459 printf("compatible-screens:"); 460 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 461 if (code == ResXMLTree::END_TAG) { 462 depth--; 463 if (depth < 0) { 464 break; 465 } 466 continue; 467 } 468 if (code != ResXMLTree::START_TAG) { 469 continue; 470 } 471 depth++; 472 String8 tag(tree.getElementName(&len)); 473 if (tag == "screen") { 474 int32_t screenSize = getIntegerAttribute(tree, 475 SCREEN_SIZE_ATTR, NULL, -1); 476 int32_t screenDensity = getIntegerAttribute(tree, 477 SCREEN_DENSITY_ATTR, NULL, -1); 478 if (screenSize > 0 && screenDensity > 0) { 479 if (!first) { 480 printf(","); 481 } 482 first = false; 483 printf("'%d/%d'", screenSize, screenDensity); 484 } 485 } 486 } 487 printf("\n"); 488} 489 490Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost, 491 String8 *outError = NULL) 492{ 493 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER); 494 if (aidAsset == NULL) { 495 if (outError != NULL) *outError = "xml resource does not exist"; 496 return Vector<String8>(); 497 } 498 499 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service"); 500 501 bool withinApduService = false; 502 Vector<String8> categories; 503 504 String8 error; 505 ResXMLTree tree; 506 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength()); 507 508 size_t len; 509 int depth = 0; 510 ResXMLTree::event_code_t code; 511 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 512 if (code == ResXMLTree::END_TAG) { 513 depth--; 514 String8 tag(tree.getElementName(&len)); 515 516 if (depth == 0 && tag == serviceTagName) { 517 withinApduService = false; 518 } 519 520 } else if (code == ResXMLTree::START_TAG) { 521 depth++; 522 String8 tag(tree.getElementName(&len)); 523 524 if (depth == 1) { 525 if (tag == serviceTagName) { 526 withinApduService = true; 527 } 528 } else if (depth == 2 && withinApduService) { 529 if (tag == "aid-group") { 530 String8 category = getAttribute(tree, CATEGORY_ATTR, &error); 531 if (error != "") { 532 if (outError != NULL) *outError = error; 533 return Vector<String8>(); 534 } 535 536 categories.add(category); 537 } 538 } 539 } 540 } 541 aidAsset->close(); 542 return categories; 543} 544 545/* 546 * Handle the "dump" command, to extract select data from an archive. 547 */ 548extern char CONSOLE_DATA[2925]; // see EOF 549int doDump(Bundle* bundle) 550{ 551 status_t result = UNKNOWN_ERROR; 552 Asset* asset = NULL; 553 554 if (bundle->getFileSpecCount() < 1) { 555 fprintf(stderr, "ERROR: no dump option specified\n"); 556 return 1; 557 } 558 559 if (bundle->getFileSpecCount() < 2) { 560 fprintf(stderr, "ERROR: no dump file specified\n"); 561 return 1; 562 } 563 564 const char* option = bundle->getFileSpecEntry(0); 565 const char* filename = bundle->getFileSpecEntry(1); 566 567 AssetManager assets; 568 void* assetsCookie; 569 if (!assets.addAssetPath(String8(filename), &assetsCookie)) { 570 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n"); 571 return 1; 572 } 573 574 // Make a dummy config for retrieving resources... we need to supply 575 // non-default values for some configs so that we can retrieve resources 576 // in the app that don't have a default. The most important of these is 577 // the API version because key resources like icons will have an implicit 578 // version if they are using newer config types like density. 579 ResTable_config config; 580 config.language[0] = 'e'; 581 config.language[1] = 'n'; 582 config.country[0] = 'U'; 583 config.country[1] = 'S'; 584 config.orientation = ResTable_config::ORIENTATION_PORT; 585 config.density = ResTable_config::DENSITY_MEDIUM; 586 config.sdkVersion = 10000; // Very high. 587 config.screenWidthDp = 320; 588 config.screenHeightDp = 480; 589 config.smallestScreenWidthDp = 320; 590 assets.setConfiguration(config); 591 592 const ResTable& res = assets.getResources(false); 593 if (&res == NULL) { 594 fprintf(stderr, "ERROR: dump failed because no resource table was found\n"); 595 goto bail; 596 } 597 598 if (strcmp("resources", option) == 0) { 599#ifndef HAVE_ANDROID_OS 600 res.print(bundle->getValues()); 601#endif 602 603 } else if (strcmp("strings", option) == 0) { 604 const ResStringPool* pool = res.getTableStringBlock(0); 605 printStringPool(pool); 606 607 } else if (strcmp("xmltree", option) == 0) { 608 if (bundle->getFileSpecCount() < 3) { 609 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n"); 610 goto bail; 611 } 612 613 for (int i=2; i<bundle->getFileSpecCount(); i++) { 614 const char* resname = bundle->getFileSpecEntry(i); 615 ResXMLTree tree; 616 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER); 617 if (asset == NULL) { 618 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname); 619 goto bail; 620 } 621 622 if (tree.setTo(asset->getBuffer(true), 623 asset->getLength()) != NO_ERROR) { 624 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname); 625 goto bail; 626 } 627 tree.restart(); 628 printXMLBlock(&tree); 629 tree.uninit(); 630 delete asset; 631 asset = NULL; 632 } 633 634 } else if (strcmp("xmlstrings", option) == 0) { 635 if (bundle->getFileSpecCount() < 3) { 636 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n"); 637 goto bail; 638 } 639 640 for (int i=2; i<bundle->getFileSpecCount(); i++) { 641 const char* resname = bundle->getFileSpecEntry(i); 642 ResXMLTree tree; 643 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER); 644 if (asset == NULL) { 645 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname); 646 goto bail; 647 } 648 649 if (tree.setTo(asset->getBuffer(true), 650 asset->getLength()) != NO_ERROR) { 651 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname); 652 goto bail; 653 } 654 printStringPool(&tree.getStrings()); 655 delete asset; 656 asset = NULL; 657 } 658 659 } else { 660 ResXMLTree tree; 661 asset = assets.openNonAsset("AndroidManifest.xml", 662 Asset::ACCESS_BUFFER); 663 if (asset == NULL) { 664 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n"); 665 goto bail; 666 } 667 668 if (tree.setTo(asset->getBuffer(true), 669 asset->getLength()) != NO_ERROR) { 670 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n"); 671 goto bail; 672 } 673 tree.restart(); 674 675 if (strcmp("permissions", option) == 0) { 676 size_t len; 677 ResXMLTree::event_code_t code; 678 int depth = 0; 679 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 680 if (code == ResXMLTree::END_TAG) { 681 depth--; 682 continue; 683 } 684 if (code != ResXMLTree::START_TAG) { 685 continue; 686 } 687 depth++; 688 String8 tag(tree.getElementName(&len)); 689 //printf("Depth %d tag %s\n", depth, tag.string()); 690 if (depth == 1) { 691 if (tag != "manifest") { 692 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 693 goto bail; 694 } 695 String8 pkg = getAttribute(tree, NULL, "package", NULL); 696 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string()); 697 } else if (depth == 2 && tag == "permission") { 698 String8 error; 699 String8 name = getAttribute(tree, NAME_ATTR, &error); 700 if (error != "") { 701 fprintf(stderr, "ERROR: %s\n", error.string()); 702 goto bail; 703 } 704 printf("permission: %s\n", 705 ResTable::normalizeForOutput(name.string()).string()); 706 } else if (depth == 2 && tag == "uses-permission") { 707 String8 error; 708 String8 name = getAttribute(tree, NAME_ATTR, &error); 709 if (error != "") { 710 fprintf(stderr, "ERROR: %s\n", error.string()); 711 goto bail; 712 } 713 printf("uses-permission: %s\n", 714 ResTable::normalizeForOutput(name.string()).string()); 715 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1); 716 if (!req) { 717 printf("optional-permission: %s\n", 718 ResTable::normalizeForOutput(name.string()).string()); 719 } 720 } 721 } 722 } else if (strcmp("badging", option) == 0) { 723 Vector<String8> locales; 724 res.getLocales(&locales); 725 726 Vector<ResTable_config> configs; 727 res.getConfigurations(&configs); 728 SortedVector<int> densities; 729 const size_t NC = configs.size(); 730 for (size_t i=0; i<NC; i++) { 731 int dens = configs[i].density; 732 if (dens == 0) { 733 dens = 160; 734 } 735 densities.add(dens); 736 } 737 738 size_t len; 739 ResXMLTree::event_code_t code; 740 int depth = 0; 741 String8 error; 742 bool withinActivity = false; 743 bool isMainActivity = false; 744 bool isLauncherActivity = false; 745 bool isSearchable = false; 746 bool withinApplication = false; 747 bool withinSupportsInput = false; 748 bool withinReceiver = false; 749 bool withinService = false; 750 bool withinIntentFilter = false; 751 bool hasMainActivity = false; 752 bool hasOtherActivities = false; 753 bool hasOtherReceivers = false; 754 bool hasOtherServices = false; 755 bool hasWallpaperService = false; 756 bool hasImeService = false; 757 bool hasAccessibilityService = false; 758 bool hasPrintService = false; 759 bool hasWidgetReceivers = false; 760 bool hasDeviceAdminReceiver = false; 761 bool hasIntentFilter = false; 762 bool hasPaymentService = false; 763 bool actMainActivity = false; 764 bool actWidgetReceivers = false; 765 bool actDeviceAdminEnabled = false; 766 bool actImeService = false; 767 bool actWallpaperService = false; 768 bool actAccessibilityService = false; 769 bool actPrintService = false; 770 bool actHostApduService = false; 771 bool actOffHostApduService = false; 772 bool hasMetaHostPaymentCategory = false; 773 bool hasMetaOffHostPaymentCategory = false; 774 775 // These permissions are required by services implementing services 776 // the system binds to (IME, Accessibility, PrintServices, etc.) 777 bool hasBindDeviceAdminPermission = false; 778 bool hasBindInputMethodPermission = false; 779 bool hasBindAccessibilityServicePermission = false; 780 bool hasBindPrintServicePermission = false; 781 bool hasBindNfcServicePermission = false; 782 783 // These two implement the implicit permissions that are granted 784 // to pre-1.6 applications. 785 bool hasWriteExternalStoragePermission = false; 786 bool hasReadPhoneStatePermission = false; 787 788 // If an app requests write storage, they will also get read storage. 789 bool hasReadExternalStoragePermission = false; 790 791 // Implement transition to read and write call log. 792 bool hasReadContactsPermission = false; 793 bool hasWriteContactsPermission = false; 794 bool hasReadCallLogPermission = false; 795 bool hasWriteCallLogPermission = false; 796 797 // This next group of variables is used to implement a group of 798 // backward-compatibility heuristics necessitated by the addition of 799 // some new uses-feature constants in 2.1 and 2.2. In most cases, the 800 // heuristic is "if an app requests a permission but doesn't explicitly 801 // request the corresponding <uses-feature>, presume it's there anyway". 802 bool specCameraFeature = false; // camera-related 803 bool specCameraAutofocusFeature = false; 804 bool reqCameraAutofocusFeature = false; 805 bool reqCameraFlashFeature = false; 806 bool hasCameraPermission = false; 807 bool specLocationFeature = false; // location-related 808 bool specNetworkLocFeature = false; 809 bool reqNetworkLocFeature = false; 810 bool specGpsFeature = false; 811 bool reqGpsFeature = false; 812 bool hasMockLocPermission = false; 813 bool hasCoarseLocPermission = false; 814 bool hasGpsPermission = false; 815 bool hasGeneralLocPermission = false; 816 bool specBluetoothFeature = false; // Bluetooth API-related 817 bool hasBluetoothPermission = false; 818 bool specMicrophoneFeature = false; // microphone-related 819 bool hasRecordAudioPermission = false; 820 bool specWiFiFeature = false; 821 bool hasWiFiPermission = false; 822 bool specTelephonyFeature = false; // telephony-related 823 bool reqTelephonySubFeature = false; 824 bool hasTelephonyPermission = false; 825 bool specTouchscreenFeature = false; // touchscreen-related 826 bool specMultitouchFeature = false; 827 bool reqDistinctMultitouchFeature = false; 828 bool specScreenPortraitFeature = false; 829 bool specScreenLandscapeFeature = false; 830 bool reqScreenPortraitFeature = false; 831 bool reqScreenLandscapeFeature = false; 832 // 2.2 also added some other features that apps can request, but that 833 // have no corresponding permission, so we cannot implement any 834 // back-compatibility heuristic for them. The below are thus unnecessary 835 // (but are retained here for documentary purposes.) 836 //bool specCompassFeature = false; 837 //bool specAccelerometerFeature = false; 838 //bool specProximityFeature = false; 839 //bool specAmbientLightFeature = false; 840 //bool specLiveWallpaperFeature = false; 841 842 int targetSdk = 0; 843 int smallScreen = 1; 844 int normalScreen = 1; 845 int largeScreen = 1; 846 int xlargeScreen = 1; 847 int anyDensity = 1; 848 int requiresSmallestWidthDp = 0; 849 int compatibleWidthLimitDp = 0; 850 int largestWidthLimitDp = 0; 851 String8 pkg; 852 String8 activityName; 853 String8 activityLabel; 854 String8 activityIcon; 855 String8 receiverName; 856 String8 serviceName; 857 Vector<String8> supportedInput; 858 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 859 if (code == ResXMLTree::END_TAG) { 860 depth--; 861 if (depth < 2) { 862 if (withinSupportsInput && !supportedInput.isEmpty()) { 863 printf("supports-input: '"); 864 const size_t N = supportedInput.size(); 865 for (size_t i=0; i<N; i++) { 866 printf("%s", ResTable::normalizeForOutput( 867 supportedInput[i].string()).string()); 868 if (i != N - 1) { 869 printf("' '"); 870 } else { 871 printf("'\n"); 872 } 873 } 874 supportedInput.clear(); 875 } 876 withinApplication = false; 877 withinSupportsInput = false; 878 } else if (depth < 3) { 879 if (withinActivity && isMainActivity && isLauncherActivity) { 880 String8 aName(getComponentName(pkg, activityName)); 881 printf("launchable-activity:"); 882 if (aName.length() > 0) { 883 printf(" name='%s' ", 884 ResTable::normalizeForOutput(aName.string()).string()); 885 } 886 printf(" label='%s' icon='%s'\n", 887 ResTable::normalizeForOutput(activityLabel.string()).string(), 888 ResTable::normalizeForOutput(activityIcon.string()).string()); 889 } 890 if (!hasIntentFilter) { 891 hasOtherActivities |= withinActivity; 892 hasOtherReceivers |= withinReceiver; 893 hasOtherServices |= withinService; 894 } else { 895 if (withinService) { 896 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory && 897 hasBindNfcServicePermission); 898 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory && 899 hasBindNfcServicePermission); 900 } 901 } 902 withinActivity = false; 903 withinService = false; 904 withinReceiver = false; 905 hasIntentFilter = false; 906 isMainActivity = isLauncherActivity = false; 907 } else if (depth < 4) { 908 if (withinIntentFilter) { 909 if (withinActivity) { 910 hasMainActivity |= actMainActivity; 911 hasOtherActivities |= !actMainActivity; 912 } else if (withinReceiver) { 913 hasWidgetReceivers |= actWidgetReceivers; 914 hasDeviceAdminReceiver |= (actDeviceAdminEnabled && 915 hasBindDeviceAdminPermission); 916 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled); 917 } else if (withinService) { 918 hasImeService |= actImeService; 919 hasWallpaperService |= actWallpaperService; 920 hasAccessibilityService |= (actAccessibilityService && 921 hasBindAccessibilityServicePermission); 922 hasPrintService |= (actPrintService && hasBindPrintServicePermission); 923 hasOtherServices |= (!actImeService && !actWallpaperService && 924 !actAccessibilityService && !actPrintService && 925 !actHostApduService && !actOffHostApduService); 926 } 927 } 928 withinIntentFilter = false; 929 } 930 continue; 931 } 932 if (code != ResXMLTree::START_TAG) { 933 continue; 934 } 935 depth++; 936 String8 tag(tree.getElementName(&len)); 937 //printf("Depth %d, %s\n", depth, tag.string()); 938 if (depth == 1) { 939 if (tag != "manifest") { 940 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 941 goto bail; 942 } 943 pkg = getAttribute(tree, NULL, "package", NULL); 944 printf("package: name='%s' ", 945 ResTable::normalizeForOutput(pkg.string()).string()); 946 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error); 947 if (error != "") { 948 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string()); 949 goto bail; 950 } 951 if (versionCode > 0) { 952 printf("versionCode='%d' ", versionCode); 953 } else { 954 printf("versionCode='' "); 955 } 956 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error); 957 if (error != "") { 958 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string()); 959 goto bail; 960 } 961 printf("versionName='%s'\n", 962 ResTable::normalizeForOutput(versionName.string()).string()); 963 } else if (depth == 2) { 964 withinApplication = false; 965 if (tag == "application") { 966 withinApplication = true; 967 968 String8 label; 969 const size_t NL = locales.size(); 970 for (size_t i=0; i<NL; i++) { 971 const char* localeStr = locales[i].string(); 972 assets.setLocale(localeStr != NULL ? localeStr : ""); 973 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); 974 if (llabel != "") { 975 if (localeStr == NULL || strlen(localeStr) == 0) { 976 label = llabel; 977 printf("application-label:'%s'\n", 978 ResTable::normalizeForOutput(llabel.string()).string()); 979 } else { 980 if (label == "") { 981 label = llabel; 982 } 983 printf("application-label-%s:'%s'\n", localeStr, 984 ResTable::normalizeForOutput(llabel.string()).string()); 985 } 986 } 987 } 988 989 ResTable_config tmpConfig = config; 990 const size_t ND = densities.size(); 991 for (size_t i=0; i<ND; i++) { 992 tmpConfig.density = densities[i]; 993 assets.setConfiguration(tmpConfig); 994 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 995 if (icon != "") { 996 printf("application-icon-%d:'%s'\n", densities[i], 997 ResTable::normalizeForOutput(icon.string()).string()); 998 } 999 } 1000 assets.setConfiguration(config); 1001 1002 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 1003 if (error != "") { 1004 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string()); 1005 goto bail; 1006 } 1007 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0); 1008 if (error != "") { 1009 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string()); 1010 goto bail; 1011 } 1012 printf("application: label='%s' ", 1013 ResTable::normalizeForOutput(label.string()).string()); 1014 printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string()); 1015 if (testOnly != 0) { 1016 printf("testOnly='%d'\n", testOnly); 1017 } 1018 1019 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0); 1020 if (error != "") { 1021 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string()); 1022 goto bail; 1023 } 1024 if (debuggable != 0) { 1025 printf("application-debuggable\n"); 1026 } 1027 } else if (tag == "uses-sdk") { 1028 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error); 1029 if (error != "") { 1030 error = ""; 1031 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error); 1032 if (error != "") { 1033 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n", 1034 error.string()); 1035 goto bail; 1036 } 1037 if (name == "Donut") targetSdk = 4; 1038 printf("sdkVersion:'%s'\n", 1039 ResTable::normalizeForOutput(name.string()).string()); 1040 } else if (code != -1) { 1041 targetSdk = code; 1042 printf("sdkVersion:'%d'\n", code); 1043 } 1044 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1); 1045 if (code != -1) { 1046 printf("maxSdkVersion:'%d'\n", code); 1047 } 1048 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error); 1049 if (error != "") { 1050 error = ""; 1051 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error); 1052 if (error != "") { 1053 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n", 1054 error.string()); 1055 goto bail; 1056 } 1057 if (name == "Donut" && targetSdk < 4) targetSdk = 4; 1058 printf("targetSdkVersion:'%s'\n", 1059 ResTable::normalizeForOutput(name.string()).string()); 1060 } else if (code != -1) { 1061 if (targetSdk < code) { 1062 targetSdk = code; 1063 } 1064 printf("targetSdkVersion:'%d'\n", code); 1065 } 1066 } else if (tag == "uses-configuration") { 1067 int32_t reqTouchScreen = getIntegerAttribute(tree, 1068 REQ_TOUCH_SCREEN_ATTR, NULL, 0); 1069 int32_t reqKeyboardType = getIntegerAttribute(tree, 1070 REQ_KEYBOARD_TYPE_ATTR, NULL, 0); 1071 int32_t reqHardKeyboard = getIntegerAttribute(tree, 1072 REQ_HARD_KEYBOARD_ATTR, NULL, 0); 1073 int32_t reqNavigation = getIntegerAttribute(tree, 1074 REQ_NAVIGATION_ATTR, NULL, 0); 1075 int32_t reqFiveWayNav = getIntegerAttribute(tree, 1076 REQ_FIVE_WAY_NAV_ATTR, NULL, 0); 1077 printf("uses-configuration:"); 1078 if (reqTouchScreen != 0) { 1079 printf(" reqTouchScreen='%d'", reqTouchScreen); 1080 } 1081 if (reqKeyboardType != 0) { 1082 printf(" reqKeyboardType='%d'", reqKeyboardType); 1083 } 1084 if (reqHardKeyboard != 0) { 1085 printf(" reqHardKeyboard='%d'", reqHardKeyboard); 1086 } 1087 if (reqNavigation != 0) { 1088 printf(" reqNavigation='%d'", reqNavigation); 1089 } 1090 if (reqFiveWayNav != 0) { 1091 printf(" reqFiveWayNav='%d'", reqFiveWayNav); 1092 } 1093 printf("\n"); 1094 } else if (tag == "supports-input") { 1095 withinSupportsInput = true; 1096 } else if (tag == "supports-screens") { 1097 smallScreen = getIntegerAttribute(tree, 1098 SMALL_SCREEN_ATTR, NULL, 1); 1099 normalScreen = getIntegerAttribute(tree, 1100 NORMAL_SCREEN_ATTR, NULL, 1); 1101 largeScreen = getIntegerAttribute(tree, 1102 LARGE_SCREEN_ATTR, NULL, 1); 1103 xlargeScreen = getIntegerAttribute(tree, 1104 XLARGE_SCREEN_ATTR, NULL, 1); 1105 anyDensity = getIntegerAttribute(tree, 1106 ANY_DENSITY_ATTR, NULL, 1); 1107 requiresSmallestWidthDp = getIntegerAttribute(tree, 1108 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0); 1109 compatibleWidthLimitDp = getIntegerAttribute(tree, 1110 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0); 1111 largestWidthLimitDp = getIntegerAttribute(tree, 1112 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0); 1113 } else if (tag == "uses-feature") { 1114 String8 name = getAttribute(tree, NAME_ATTR, &error); 1115 1116 if (name != "" && error == "") { 1117 int req = getIntegerAttribute(tree, 1118 REQUIRED_ATTR, NULL, 1); 1119 1120 if (name == "android.hardware.camera") { 1121 specCameraFeature = true; 1122 } else if (name == "android.hardware.camera.autofocus") { 1123 // these have no corresponding permission to check for, 1124 // but should imply the foundational camera permission 1125 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req; 1126 specCameraAutofocusFeature = true; 1127 } else if (req && (name == "android.hardware.camera.flash")) { 1128 // these have no corresponding permission to check for, 1129 // but should imply the foundational camera permission 1130 reqCameraFlashFeature = true; 1131 } else if (name == "android.hardware.location") { 1132 specLocationFeature = true; 1133 } else if (name == "android.hardware.location.network") { 1134 specNetworkLocFeature = true; 1135 reqNetworkLocFeature = reqNetworkLocFeature || req; 1136 } else if (name == "android.hardware.location.gps") { 1137 specGpsFeature = true; 1138 reqGpsFeature = reqGpsFeature || req; 1139 } else if (name == "android.hardware.bluetooth") { 1140 specBluetoothFeature = true; 1141 } else if (name == "android.hardware.touchscreen") { 1142 specTouchscreenFeature = true; 1143 } else if (name == "android.hardware.touchscreen.multitouch") { 1144 specMultitouchFeature = true; 1145 } else if (name == "android.hardware.touchscreen.multitouch.distinct") { 1146 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req; 1147 } else if (name == "android.hardware.microphone") { 1148 specMicrophoneFeature = true; 1149 } else if (name == "android.hardware.wifi") { 1150 specWiFiFeature = true; 1151 } else if (name == "android.hardware.telephony") { 1152 specTelephonyFeature = true; 1153 } else if (req && (name == "android.hardware.telephony.gsm" || 1154 name == "android.hardware.telephony.cdma")) { 1155 // these have no corresponding permission to check for, 1156 // but should imply the foundational telephony permission 1157 reqTelephonySubFeature = true; 1158 } else if (name == "android.hardware.screen.portrait") { 1159 specScreenPortraitFeature = true; 1160 } else if (name == "android.hardware.screen.landscape") { 1161 specScreenLandscapeFeature = true; 1162 } 1163 printf("uses-feature%s:'%s'\n", 1164 req ? "" : "-not-required", 1165 ResTable::normalizeForOutput(name.string()).string()); 1166 } else { 1167 int vers = getIntegerAttribute(tree, 1168 GL_ES_VERSION_ATTR, &error); 1169 if (error == "") { 1170 printf("uses-gl-es:'0x%x'\n", vers); 1171 } 1172 } 1173 } else if (tag == "uses-permission") { 1174 String8 name = getAttribute(tree, NAME_ATTR, &error); 1175 if (name != "" && error == "") { 1176 if (name == "android.permission.CAMERA") { 1177 hasCameraPermission = true; 1178 } else if (name == "android.permission.ACCESS_FINE_LOCATION") { 1179 hasGpsPermission = true; 1180 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") { 1181 hasMockLocPermission = true; 1182 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") { 1183 hasCoarseLocPermission = true; 1184 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" || 1185 name == "android.permission.INSTALL_LOCATION_PROVIDER") { 1186 hasGeneralLocPermission = true; 1187 } else if (name == "android.permission.BLUETOOTH" || 1188 name == "android.permission.BLUETOOTH_ADMIN") { 1189 hasBluetoothPermission = true; 1190 } else if (name == "android.permission.RECORD_AUDIO") { 1191 hasRecordAudioPermission = true; 1192 } else if (name == "android.permission.ACCESS_WIFI_STATE" || 1193 name == "android.permission.CHANGE_WIFI_STATE" || 1194 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") { 1195 hasWiFiPermission = true; 1196 } else if (name == "android.permission.CALL_PHONE" || 1197 name == "android.permission.CALL_PRIVILEGED" || 1198 name == "android.permission.MODIFY_PHONE_STATE" || 1199 name == "android.permission.PROCESS_OUTGOING_CALLS" || 1200 name == "android.permission.READ_SMS" || 1201 name == "android.permission.RECEIVE_SMS" || 1202 name == "android.permission.RECEIVE_MMS" || 1203 name == "android.permission.RECEIVE_WAP_PUSH" || 1204 name == "android.permission.SEND_SMS" || 1205 name == "android.permission.WRITE_APN_SETTINGS" || 1206 name == "android.permission.WRITE_SMS") { 1207 hasTelephonyPermission = true; 1208 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") { 1209 hasWriteExternalStoragePermission = true; 1210 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") { 1211 hasReadExternalStoragePermission = true; 1212 } else if (name == "android.permission.READ_PHONE_STATE") { 1213 hasReadPhoneStatePermission = true; 1214 } else if (name == "android.permission.READ_CONTACTS") { 1215 hasReadContactsPermission = true; 1216 } else if (name == "android.permission.WRITE_CONTACTS") { 1217 hasWriteContactsPermission = true; 1218 } else if (name == "android.permission.READ_CALL_LOG") { 1219 hasReadCallLogPermission = true; 1220 } else if (name == "android.permission.WRITE_CALL_LOG") { 1221 hasWriteCallLogPermission = true; 1222 } 1223 printf("uses-permission:'%s'\n", 1224 ResTable::normalizeForOutput(name.string()).string()); 1225 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1); 1226 if (!req) { 1227 printf("optional-permission:'%s'\n", 1228 ResTable::normalizeForOutput(name.string()).string()); 1229 } 1230 } else { 1231 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1232 error.string()); 1233 goto bail; 1234 } 1235 } else if (tag == "uses-package") { 1236 String8 name = getAttribute(tree, NAME_ATTR, &error); 1237 if (name != "" && error == "") { 1238 printf("uses-package:'%s'\n", 1239 ResTable::normalizeForOutput(name.string()).string()); 1240 } else { 1241 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1242 error.string()); 1243 goto bail; 1244 } 1245 } else if (tag == "original-package") { 1246 String8 name = getAttribute(tree, NAME_ATTR, &error); 1247 if (name != "" && error == "") { 1248 printf("original-package:'%s'\n", 1249 ResTable::normalizeForOutput(name.string()).string()); 1250 } else { 1251 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1252 error.string()); 1253 goto bail; 1254 } 1255 } else if (tag == "supports-gl-texture") { 1256 String8 name = getAttribute(tree, NAME_ATTR, &error); 1257 if (name != "" && error == "") { 1258 printf("supports-gl-texture:'%s'\n", 1259 ResTable::normalizeForOutput(name.string()).string()); 1260 } else { 1261 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1262 error.string()); 1263 goto bail; 1264 } 1265 } else if (tag == "compatible-screens") { 1266 printCompatibleScreens(tree); 1267 depth--; 1268 } else if (tag == "package-verifier") { 1269 String8 name = getAttribute(tree, NAME_ATTR, &error); 1270 if (name != "" && error == "") { 1271 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error); 1272 if (publicKey != "" && error == "") { 1273 printf("package-verifier: name='%s' publicKey='%s'\n", 1274 ResTable::normalizeForOutput(name.string()).string(), 1275 ResTable::normalizeForOutput(publicKey.string()).string()); 1276 } 1277 } 1278 } 1279 } else if (depth == 3) { 1280 withinActivity = false; 1281 withinReceiver = false; 1282 withinService = false; 1283 hasIntentFilter = false; 1284 hasMetaHostPaymentCategory = false; 1285 hasMetaOffHostPaymentCategory = false; 1286 hasBindDeviceAdminPermission = false; 1287 hasBindInputMethodPermission = false; 1288 hasBindAccessibilityServicePermission = false; 1289 hasBindPrintServicePermission = false; 1290 hasBindNfcServicePermission = false; 1291 if (withinApplication) { 1292 if(tag == "activity") { 1293 withinActivity = true; 1294 activityName = getAttribute(tree, NAME_ATTR, &error); 1295 if (error != "") { 1296 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1297 error.string()); 1298 goto bail; 1299 } 1300 1301 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); 1302 if (error != "") { 1303 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", 1304 error.string()); 1305 goto bail; 1306 } 1307 1308 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 1309 if (error != "") { 1310 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", 1311 error.string()); 1312 goto bail; 1313 } 1314 1315 int32_t orien = getResolvedIntegerAttribute(&res, tree, 1316 SCREEN_ORIENTATION_ATTR, &error); 1317 if (error == "") { 1318 if (orien == 0 || orien == 6 || orien == 8) { 1319 // Requests landscape, sensorLandscape, or reverseLandscape. 1320 reqScreenLandscapeFeature = true; 1321 } else if (orien == 1 || orien == 7 || orien == 9) { 1322 // Requests portrait, sensorPortrait, or reversePortrait. 1323 reqScreenPortraitFeature = true; 1324 } 1325 } 1326 } else if (tag == "uses-library") { 1327 String8 libraryName = getAttribute(tree, NAME_ATTR, &error); 1328 if (error != "") { 1329 fprintf(stderr, 1330 "ERROR getting 'android:name' attribute for uses-library" 1331 " %s\n", error.string()); 1332 goto bail; 1333 } 1334 int req = getIntegerAttribute(tree, 1335 REQUIRED_ATTR, NULL, 1); 1336 printf("uses-library%s:'%s'\n", 1337 req ? "" : "-not-required", ResTable::normalizeForOutput( 1338 libraryName.string()).string()); 1339 } else if (tag == "receiver") { 1340 withinReceiver = true; 1341 receiverName = getAttribute(tree, NAME_ATTR, &error); 1342 1343 if (error != "") { 1344 fprintf(stderr, 1345 "ERROR getting 'android:name' attribute for receiver:" 1346 " %s\n", error.string()); 1347 goto bail; 1348 } 1349 1350 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error); 1351 if (error == "") { 1352 if (permission == "android.permission.BIND_DEVICE_ADMIN") { 1353 hasBindDeviceAdminPermission = true; 1354 } 1355 } else { 1356 fprintf(stderr, "ERROR getting 'android:permission' attribute for" 1357 " receiver '%s': %s\n", receiverName.string(), error.string()); 1358 } 1359 } else if (tag == "service") { 1360 withinService = true; 1361 serviceName = getAttribute(tree, NAME_ATTR, &error); 1362 1363 if (error != "") { 1364 fprintf(stderr, "ERROR getting 'android:name' attribute for " 1365 "service:%s\n", error.string()); 1366 goto bail; 1367 } 1368 1369 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error); 1370 if (error == "") { 1371 if (permission == "android.permission.BIND_INPUT_METHOD") { 1372 hasBindInputMethodPermission = true; 1373 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") { 1374 hasBindAccessibilityServicePermission = true; 1375 } else if (permission == "android.permission.BIND_PRINT_SERVICE") { 1376 hasBindPrintServicePermission = true; 1377 } else if (permission == "android.permission.BIND_NFC_SERVICE") { 1378 hasBindNfcServicePermission = true; 1379 } 1380 } else { 1381 fprintf(stderr, "ERROR getting 'android:permission' attribute for" 1382 " service '%s': %s\n", serviceName.string(), error.string()); 1383 } 1384 } else if (bundle->getIncludeMetaData() && tag == "meta-data") { 1385 String8 metaDataName = getAttribute(tree, NAME_ATTR, &error); 1386 if (error != "") { 1387 fprintf(stderr, "ERROR getting 'android:name' attribute for " 1388 "meta-data:%s\n", error.string()); 1389 goto bail; 1390 } 1391 printf("meta-data: name='%s' ", 1392 ResTable::normalizeForOutput(metaDataName.string()).string()); 1393 printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"), 1394 &error); 1395 if (error != "") { 1396 // Try looking for a RESOURCE_ATTR 1397 error = ""; 1398 printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR, 1399 String8("resource"), &error); 1400 if (error != "") { 1401 fprintf(stderr, "ERROR getting 'android:value' or " 1402 "'android:resource' attribute for " 1403 "meta-data:%s\n", error.string()); 1404 goto bail; 1405 } 1406 } 1407 printf("\n"); 1408 } else if (withinSupportsInput && tag == "input-type") { 1409 String8 name = getAttribute(tree, NAME_ATTR, &error); 1410 if (name != "" && error == "") { 1411 supportedInput.add(name); 1412 } else { 1413 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1414 error.string()); 1415 goto bail; 1416 } 1417 } 1418 } 1419 } else if (depth == 4) { 1420 if (tag == "intent-filter") { 1421 hasIntentFilter = true; 1422 withinIntentFilter = true; 1423 actMainActivity = false; 1424 actWidgetReceivers = false; 1425 actImeService = false; 1426 actWallpaperService = false; 1427 actAccessibilityService = false; 1428 actPrintService = false; 1429 actDeviceAdminEnabled = false; 1430 actHostApduService = false; 1431 actOffHostApduService = false; 1432 } else if (withinService && tag == "meta-data") { 1433 String8 name = getAttribute(tree, NAME_ATTR, &error); 1434 if (error != "") { 1435 fprintf(stderr, "ERROR getting 'android:name' attribute for" 1436 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string()); 1437 goto bail; 1438 } 1439 1440 if (name == "android.nfc.cardemulation.host_apdu_service" || 1441 name == "android.nfc.cardemulation.off_host_apdu_service") { 1442 bool offHost = true; 1443 if (name == "android.nfc.cardemulation.host_apdu_service") { 1444 offHost = false; 1445 } 1446 1447 String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error); 1448 if (error != "") { 1449 fprintf(stderr, "ERROR getting 'android:resource' attribute for" 1450 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string()); 1451 goto bail; 1452 } 1453 1454 Vector<String8> categories = getNfcAidCategories(assets, xmlPath, 1455 offHost, &error); 1456 if (error != "") { 1457 fprintf(stderr, "ERROR getting AID category for service '%s'\n", 1458 serviceName.string()); 1459 goto bail; 1460 } 1461 1462 const size_t catLen = categories.size(); 1463 for (size_t i = 0; i < catLen; i++) { 1464 bool paymentCategory = (categories[i] == "payment"); 1465 if (offHost) { 1466 hasMetaOffHostPaymentCategory |= paymentCategory; 1467 } else { 1468 hasMetaHostPaymentCategory |= paymentCategory; 1469 } 1470 } 1471 } 1472 } 1473 } else if ((depth == 5) && withinIntentFilter) { 1474 String8 action; 1475 if (tag == "action") { 1476 action = getAttribute(tree, NAME_ATTR, &error); 1477 if (error != "") { 1478 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1479 error.string()); 1480 goto bail; 1481 } 1482 1483 if (withinActivity) { 1484 if (action == "android.intent.action.MAIN") { 1485 isMainActivity = true; 1486 actMainActivity = true; 1487 } 1488 } else if (withinReceiver) { 1489 if (action == "android.appwidget.action.APPWIDGET_UPDATE") { 1490 actWidgetReceivers = true; 1491 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") { 1492 actDeviceAdminEnabled = true; 1493 } 1494 } else if (withinService) { 1495 if (action == "android.view.InputMethod") { 1496 actImeService = true; 1497 } else if (action == "android.service.wallpaper.WallpaperService") { 1498 actWallpaperService = true; 1499 } else if (action == "android.accessibilityservice.AccessibilityService") { 1500 actAccessibilityService = true; 1501 } else if (action == "android.printservice.PrintService") { 1502 actPrintService = true; 1503 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") { 1504 actHostApduService = true; 1505 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") { 1506 actOffHostApduService = true; 1507 } 1508 } 1509 if (action == "android.intent.action.SEARCH") { 1510 isSearchable = true; 1511 } 1512 } 1513 1514 if (tag == "category") { 1515 String8 category = getAttribute(tree, NAME_ATTR, &error); 1516 if (error != "") { 1517 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", 1518 error.string()); 1519 goto bail; 1520 } 1521 if (withinActivity) { 1522 if (category == "android.intent.category.LAUNCHER") { 1523 isLauncherActivity = true; 1524 } 1525 } 1526 } 1527 } 1528 } 1529 1530 // Pre-1.6 implicitly granted permission compatibility logic 1531 if (targetSdk < 4) { 1532 if (!hasWriteExternalStoragePermission) { 1533 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n"); 1534 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \ 1535 "'targetSdkVersion < 4'\n"); 1536 hasWriteExternalStoragePermission = true; 1537 } 1538 if (!hasReadPhoneStatePermission) { 1539 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n"); 1540 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \ 1541 "'targetSdkVersion < 4'\n"); 1542 } 1543 } 1544 1545 // If the application has requested WRITE_EXTERNAL_STORAGE, we will 1546 // force them to always take READ_EXTERNAL_STORAGE as well. We always 1547 // do this (regardless of target API version) because we can't have 1548 // an app with write permission but not read permission. 1549 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) { 1550 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n"); 1551 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \ 1552 "'requested WRITE_EXTERNAL_STORAGE'\n"); 1553 } 1554 1555 // Pre-JellyBean call log permission compatibility. 1556 if (targetSdk < 16) { 1557 if (!hasReadCallLogPermission && hasReadContactsPermission) { 1558 printf("uses-permission:'android.permission.READ_CALL_LOG'\n"); 1559 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \ 1560 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n"); 1561 } 1562 if (!hasWriteCallLogPermission && hasWriteContactsPermission) { 1563 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n"); 1564 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \ 1565 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n"); 1566 } 1567 } 1568 1569 /* The following blocks handle printing "inferred" uses-features, based 1570 * on whether related features or permissions are used by the app. 1571 * Note that the various spec*Feature variables denote whether the 1572 * relevant tag was *present* in the AndroidManfest, not that it was 1573 * present and set to true. 1574 */ 1575 // Camera-related back-compatibility logic 1576 if (!specCameraFeature) { 1577 if (reqCameraFlashFeature) { 1578 // if app requested a sub-feature (autofocus or flash) and didn't 1579 // request the base camera feature, we infer that it meant to 1580 printf("uses-feature:'android.hardware.camera'\n"); 1581 printf("uses-implied-feature:'android.hardware.camera'," \ 1582 "'requested android.hardware.camera.flash feature'\n"); 1583 } else if (reqCameraAutofocusFeature) { 1584 // if app requested a sub-feature (autofocus or flash) and didn't 1585 // request the base camera feature, we infer that it meant to 1586 printf("uses-feature:'android.hardware.camera'\n"); 1587 printf("uses-implied-feature:'android.hardware.camera'," \ 1588 "'requested android.hardware.camera.autofocus feature'\n"); 1589 } else if (hasCameraPermission) { 1590 // if app wants to use camera but didn't request the feature, we infer 1591 // that it meant to, and further that it wants autofocus 1592 // (which was the 1.0 - 1.5 behavior) 1593 printf("uses-feature:'android.hardware.camera'\n"); 1594 if (!specCameraAutofocusFeature) { 1595 printf("uses-feature:'android.hardware.camera.autofocus'\n"); 1596 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \ 1597 "'requested android.permission.CAMERA permission'\n"); 1598 } 1599 } 1600 } 1601 1602 // Location-related back-compatibility logic 1603 if (!specLocationFeature && 1604 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission || 1605 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) { 1606 // if app either takes a location-related permission or requests one of the 1607 // sub-features, we infer that it also meant to request the base location feature 1608 printf("uses-feature:'android.hardware.location'\n"); 1609 printf("uses-implied-feature:'android.hardware.location'," \ 1610 "'requested a location access permission'\n"); 1611 } 1612 if (!specGpsFeature && hasGpsPermission) { 1613 // if app takes GPS (FINE location) perm but does not request the GPS 1614 // feature, we infer that it meant to 1615 printf("uses-feature:'android.hardware.location.gps'\n"); 1616 printf("uses-implied-feature:'android.hardware.location.gps'," \ 1617 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n"); 1618 } 1619 if (!specNetworkLocFeature && hasCoarseLocPermission) { 1620 // if app takes Network location (COARSE location) perm but does not request the 1621 // network location feature, we infer that it meant to 1622 printf("uses-feature:'android.hardware.location.network'\n"); 1623 printf("uses-implied-feature:'android.hardware.location.network'," \ 1624 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n"); 1625 } 1626 1627 // Bluetooth-related compatibility logic 1628 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) { 1629 // if app takes a Bluetooth permission but does not request the Bluetooth 1630 // feature, we infer that it meant to 1631 printf("uses-feature:'android.hardware.bluetooth'\n"); 1632 printf("uses-implied-feature:'android.hardware.bluetooth'," \ 1633 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \ 1634 "permission and targetSdkVersion > 4'\n"); 1635 } 1636 1637 // Microphone-related compatibility logic 1638 if (!specMicrophoneFeature && hasRecordAudioPermission) { 1639 // if app takes the record-audio permission but does not request the microphone 1640 // feature, we infer that it meant to 1641 printf("uses-feature:'android.hardware.microphone'\n"); 1642 printf("uses-implied-feature:'android.hardware.microphone'," \ 1643 "'requested android.permission.RECORD_AUDIO permission'\n"); 1644 } 1645 1646 // WiFi-related compatibility logic 1647 if (!specWiFiFeature && hasWiFiPermission) { 1648 // if app takes one of the WiFi permissions but does not request the WiFi 1649 // feature, we infer that it meant to 1650 printf("uses-feature:'android.hardware.wifi'\n"); 1651 printf("uses-implied-feature:'android.hardware.wifi'," \ 1652 "'requested android.permission.ACCESS_WIFI_STATE, " \ 1653 "android.permission.CHANGE_WIFI_STATE, or " \ 1654 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n"); 1655 } 1656 1657 // Telephony-related compatibility logic 1658 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) { 1659 // if app takes one of the telephony permissions or requests a sub-feature but 1660 // does not request the base telephony feature, we infer that it meant to 1661 printf("uses-feature:'android.hardware.telephony'\n"); 1662 printf("uses-implied-feature:'android.hardware.telephony'," \ 1663 "'requested a telephony-related permission or feature'\n"); 1664 } 1665 1666 // Touchscreen-related back-compatibility logic 1667 if (!specTouchscreenFeature) { // not a typo! 1668 // all apps are presumed to require a touchscreen, unless they explicitly say 1669 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/> 1670 // Note that specTouchscreenFeature is true if the tag is present, regardless 1671 // of whether its value is true or false, so this is safe 1672 printf("uses-feature:'android.hardware.touchscreen'\n"); 1673 printf("uses-implied-feature:'android.hardware.touchscreen'," \ 1674 "'assumed you require a touch screen unless explicitly made optional'\n"); 1675 } 1676 if (!specMultitouchFeature && reqDistinctMultitouchFeature) { 1677 // if app takes one of the telephony permissions or requests a sub-feature but 1678 // does not request the base telephony feature, we infer that it meant to 1679 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n"); 1680 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \ 1681 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n"); 1682 } 1683 1684 // Landscape/portrait-related compatibility logic 1685 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) { 1686 // If the app has specified any activities in its manifest 1687 // that request a specific orientation, then assume that 1688 // orientation is required. 1689 if (reqScreenLandscapeFeature) { 1690 printf("uses-feature:'android.hardware.screen.landscape'\n"); 1691 printf("uses-implied-feature:'android.hardware.screen.landscape'," \ 1692 "'one or more activities have specified a landscape orientation'\n"); 1693 } 1694 if (reqScreenPortraitFeature) { 1695 printf("uses-feature:'android.hardware.screen.portrait'\n"); 1696 printf("uses-implied-feature:'android.hardware.screen.portrait'," \ 1697 "'one or more activities have specified a portrait orientation'\n"); 1698 } 1699 } 1700 1701 if (hasMainActivity) { 1702 printf("main\n"); 1703 } 1704 if (hasWidgetReceivers) { 1705 printf("app-widget\n"); 1706 } 1707 if (hasDeviceAdminReceiver) { 1708 printf("device-admin\n"); 1709 } 1710 if (hasImeService) { 1711 printf("ime\n"); 1712 } 1713 if (hasWallpaperService) { 1714 printf("wallpaper\n"); 1715 } 1716 if (hasAccessibilityService) { 1717 printf("accessibility\n"); 1718 } 1719 if (hasPrintService) { 1720 printf("print\n"); 1721 } 1722 if (hasPaymentService) { 1723 printf("payment\n"); 1724 } 1725 if (hasOtherActivities) { 1726 printf("other-activities\n"); 1727 } 1728 if (isSearchable) { 1729 printf("search\n"); 1730 } 1731 if (hasOtherReceivers) { 1732 printf("other-receivers\n"); 1733 } 1734 if (hasOtherServices) { 1735 printf("other-services\n"); 1736 } 1737 1738 // For modern apps, if screen size buckets haven't been specified 1739 // but the new width ranges have, then infer the buckets from them. 1740 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0 1741 && requiresSmallestWidthDp > 0) { 1742 int compatWidth = compatibleWidthLimitDp; 1743 if (compatWidth <= 0) { 1744 compatWidth = requiresSmallestWidthDp; 1745 } 1746 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) { 1747 smallScreen = -1; 1748 } else { 1749 smallScreen = 0; 1750 } 1751 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) { 1752 normalScreen = -1; 1753 } else { 1754 normalScreen = 0; 1755 } 1756 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) { 1757 largeScreen = -1; 1758 } else { 1759 largeScreen = 0; 1760 } 1761 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) { 1762 xlargeScreen = -1; 1763 } else { 1764 xlargeScreen = 0; 1765 } 1766 } 1767 1768 // Determine default values for any unspecified screen sizes, 1769 // based on the target SDK of the package. As of 4 (donut) 1770 // the screen size support was introduced, so all default to 1771 // enabled. 1772 if (smallScreen > 0) { 1773 smallScreen = targetSdk >= 4 ? -1 : 0; 1774 } 1775 if (normalScreen > 0) { 1776 normalScreen = -1; 1777 } 1778 if (largeScreen > 0) { 1779 largeScreen = targetSdk >= 4 ? -1 : 0; 1780 } 1781 if (xlargeScreen > 0) { 1782 // Introduced in Gingerbread. 1783 xlargeScreen = targetSdk >= 9 ? -1 : 0; 1784 } 1785 if (anyDensity > 0) { 1786 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0 1787 || compatibleWidthLimitDp > 0) ? -1 : 0; 1788 } 1789 printf("supports-screens:"); 1790 if (smallScreen != 0) { 1791 printf(" 'small'"); 1792 } 1793 if (normalScreen != 0) { 1794 printf(" 'normal'"); 1795 } 1796 if (largeScreen != 0) { 1797 printf(" 'large'"); 1798 } 1799 if (xlargeScreen != 0) { 1800 printf(" 'xlarge'"); 1801 } 1802 printf("\n"); 1803 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false"); 1804 if (requiresSmallestWidthDp > 0) { 1805 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp); 1806 } 1807 if (compatibleWidthLimitDp > 0) { 1808 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp); 1809 } 1810 if (largestWidthLimitDp > 0) { 1811 printf("largest-width-limit:'%d'\n", largestWidthLimitDp); 1812 } 1813 1814 printf("locales:"); 1815 const size_t NL = locales.size(); 1816 for (size_t i=0; i<NL; i++) { 1817 const char* localeStr = locales[i].string(); 1818 if (localeStr == NULL || strlen(localeStr) == 0) { 1819 localeStr = "--_--"; 1820 } 1821 printf(" '%s'", localeStr); 1822 } 1823 printf("\n"); 1824 1825 printf("densities:"); 1826 const size_t ND = densities.size(); 1827 for (size_t i=0; i<ND; i++) { 1828 printf(" '%d'", densities[i]); 1829 } 1830 printf("\n"); 1831 1832 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib"); 1833 if (dir != NULL) { 1834 if (dir->getFileCount() > 0) { 1835 printf("native-code:"); 1836 for (size_t i=0; i<dir->getFileCount(); i++) { 1837 printf(" '%s'", ResTable::normalizeForOutput( 1838 dir->getFileName(i).string()).string()); 1839 } 1840 printf("\n"); 1841 } 1842 delete dir; 1843 } 1844 } else if (strcmp("badger", option) == 0) { 1845 printf("%s", CONSOLE_DATA); 1846 } else if (strcmp("configurations", option) == 0) { 1847 Vector<ResTable_config> configs; 1848 res.getConfigurations(&configs); 1849 const size_t N = configs.size(); 1850 for (size_t i=0; i<N; i++) { 1851 printf("%s\n", configs[i].toString().string()); 1852 } 1853 } else { 1854 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option); 1855 goto bail; 1856 } 1857 } 1858 1859 result = NO_ERROR; 1860 1861bail: 1862 if (asset) { 1863 delete asset; 1864 } 1865 return (result != NO_ERROR); 1866} 1867 1868 1869/* 1870 * Handle the "add" command, which wants to add files to a new or 1871 * pre-existing archive. 1872 */ 1873int doAdd(Bundle* bundle) 1874{ 1875 ZipFile* zip = NULL; 1876 status_t result = UNKNOWN_ERROR; 1877 const char* zipFileName; 1878 1879 if (bundle->getUpdate()) { 1880 /* avoid confusion */ 1881 fprintf(stderr, "ERROR: can't use '-u' with add\n"); 1882 goto bail; 1883 } 1884 1885 if (bundle->getFileSpecCount() < 1) { 1886 fprintf(stderr, "ERROR: must specify zip file name\n"); 1887 goto bail; 1888 } 1889 zipFileName = bundle->getFileSpecEntry(0); 1890 1891 if (bundle->getFileSpecCount() < 2) { 1892 fprintf(stderr, "NOTE: nothing to do\n"); 1893 goto bail; 1894 } 1895 1896 zip = openReadWrite(zipFileName, true); 1897 if (zip == NULL) { 1898 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName); 1899 goto bail; 1900 } 1901 1902 for (int i = 1; i < bundle->getFileSpecCount(); i++) { 1903 const char* fileName = bundle->getFileSpecEntry(i); 1904 1905 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) { 1906 printf(" '%s'... (from gzip)\n", fileName); 1907 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL); 1908 } else { 1909 if (bundle->getJunkPath()) { 1910 String8 storageName = String8(fileName).getPathLeaf(); 1911 printf(" '%s' as '%s'...\n", fileName, 1912 ResTable::normalizeForOutput(storageName.string()).string()); 1913 result = zip->add(fileName, storageName.string(), 1914 bundle->getCompressionMethod(), NULL); 1915 } else { 1916 printf(" '%s'...\n", fileName); 1917 result = zip->add(fileName, bundle->getCompressionMethod(), NULL); 1918 } 1919 } 1920 if (result != NO_ERROR) { 1921 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName); 1922 if (result == NAME_NOT_FOUND) { 1923 fprintf(stderr, ": file not found\n"); 1924 } else if (result == ALREADY_EXISTS) { 1925 fprintf(stderr, ": already exists in archive\n"); 1926 } else { 1927 fprintf(stderr, "\n"); 1928 } 1929 goto bail; 1930 } 1931 } 1932 1933 result = NO_ERROR; 1934 1935bail: 1936 delete zip; 1937 return (result != NO_ERROR); 1938} 1939 1940 1941/* 1942 * Delete files from an existing archive. 1943 */ 1944int doRemove(Bundle* bundle) 1945{ 1946 ZipFile* zip = NULL; 1947 status_t result = UNKNOWN_ERROR; 1948 const char* zipFileName; 1949 1950 if (bundle->getFileSpecCount() < 1) { 1951 fprintf(stderr, "ERROR: must specify zip file name\n"); 1952 goto bail; 1953 } 1954 zipFileName = bundle->getFileSpecEntry(0); 1955 1956 if (bundle->getFileSpecCount() < 2) { 1957 fprintf(stderr, "NOTE: nothing to do\n"); 1958 goto bail; 1959 } 1960 1961 zip = openReadWrite(zipFileName, false); 1962 if (zip == NULL) { 1963 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n", 1964 zipFileName); 1965 goto bail; 1966 } 1967 1968 for (int i = 1; i < bundle->getFileSpecCount(); i++) { 1969 const char* fileName = bundle->getFileSpecEntry(i); 1970 ZipEntry* entry; 1971 1972 entry = zip->getEntryByName(fileName); 1973 if (entry == NULL) { 1974 printf(" '%s' NOT FOUND\n", fileName); 1975 continue; 1976 } 1977 1978 result = zip->remove(entry); 1979 1980 if (result != NO_ERROR) { 1981 fprintf(stderr, "Unable to delete '%s' from '%s'\n", 1982 bundle->getFileSpecEntry(i), zipFileName); 1983 goto bail; 1984 } 1985 } 1986 1987 /* update the archive */ 1988 zip->flush(); 1989 1990bail: 1991 delete zip; 1992 return (result != NO_ERROR); 1993} 1994 1995 1996/* 1997 * Package up an asset directory and associated application files. 1998 */ 1999int doPackage(Bundle* bundle) 2000{ 2001 const char* outputAPKFile; 2002 int retVal = 1; 2003 status_t err; 2004 sp<AaptAssets> assets; 2005 int N; 2006 FILE* fp; 2007 String8 dependencyFile; 2008 2009 // -c zz_ZZ means do pseudolocalization 2010 ResourceFilter filter; 2011 err = filter.parse(bundle->getConfigurations()); 2012 if (err != NO_ERROR) { 2013 goto bail; 2014 } 2015 if (filter.containsPseudo()) { 2016 bundle->setPseudolocalize(true); 2017 } 2018 2019 N = bundle->getFileSpecCount(); 2020 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0 2021 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) { 2022 fprintf(stderr, "ERROR: no input files\n"); 2023 goto bail; 2024 } 2025 2026 outputAPKFile = bundle->getOutputAPKFile(); 2027 2028 // Make sure the filenames provided exist and are of the appropriate type. 2029 if (outputAPKFile) { 2030 FileType type; 2031 type = getFileType(outputAPKFile); 2032 if (type != kFileTypeNonexistent && type != kFileTypeRegular) { 2033 fprintf(stderr, 2034 "ERROR: output file '%s' exists but is not regular file\n", 2035 outputAPKFile); 2036 goto bail; 2037 } 2038 } 2039 2040 // Load the assets. 2041 assets = new AaptAssets(); 2042 2043 // Set up the resource gathering in assets if we're going to generate 2044 // dependency files. Every time we encounter a resource while slurping 2045 // the tree, we'll add it to these stores so we have full resource paths 2046 // to write to a dependency file. 2047 if (bundle->getGenDependencies()) { 2048 sp<FilePathStore> resPathStore = new FilePathStore; 2049 assets->setFullResPaths(resPathStore); 2050 sp<FilePathStore> assetPathStore = new FilePathStore; 2051 assets->setFullAssetPaths(assetPathStore); 2052 } 2053 2054 err = assets->slurpFromArgs(bundle); 2055 if (err < 0) { 2056 goto bail; 2057 } 2058 2059 if (bundle->getVerbose()) { 2060 assets->print(String8()); 2061 } 2062 2063 // If they asked for any fileAs that need to be compiled, do so. 2064 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) { 2065 err = buildResources(bundle, assets); 2066 if (err != 0) { 2067 goto bail; 2068 } 2069 } 2070 2071 // At this point we've read everything and processed everything. From here 2072 // on out it's just writing output files. 2073 if (SourcePos::hasErrors()) { 2074 goto bail; 2075 } 2076 2077 // Update symbols with information about which ones are needed as Java symbols. 2078 assets->applyJavaSymbols(); 2079 if (SourcePos::hasErrors()) { 2080 goto bail; 2081 } 2082 2083 // If we've been asked to generate a dependency file, do that here 2084 if (bundle->getGenDependencies()) { 2085 // If this is the packaging step, generate the dependency file next to 2086 // the output apk (e.g. bin/resources.ap_.d) 2087 if (outputAPKFile) { 2088 dependencyFile = String8(outputAPKFile); 2089 // Add the .d extension to the dependency file. 2090 dependencyFile.append(".d"); 2091 } else { 2092 // Else if this is the R.java dependency generation step, 2093 // generate the dependency file in the R.java package subdirectory 2094 // e.g. gen/com/foo/app/R.java.d 2095 dependencyFile = String8(bundle->getRClassDir()); 2096 dependencyFile.appendPath("R.java.d"); 2097 } 2098 // Make sure we have a clean dependency file to start with 2099 fp = fopen(dependencyFile, "w"); 2100 fclose(fp); 2101 } 2102 2103 // Write out R.java constants 2104 if (!assets->havePrivateSymbols()) { 2105 if (bundle->getCustomPackage() == NULL) { 2106 // Write the R.java file into the appropriate class directory 2107 // e.g. gen/com/foo/app/R.java 2108 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true); 2109 } else { 2110 const String8 customPkg(bundle->getCustomPackage()); 2111 err = writeResourceSymbols(bundle, assets, customPkg, true); 2112 } 2113 if (err < 0) { 2114 goto bail; 2115 } 2116 // If we have library files, we're going to write our R.java file into 2117 // the appropriate class directory for those libraries as well. 2118 // e.g. gen/com/foo/app/lib/R.java 2119 if (bundle->getExtraPackages() != NULL) { 2120 // Split on colon 2121 String8 libs(bundle->getExtraPackages()); 2122 char* packageString = strtok(libs.lockBuffer(libs.length()), ":"); 2123 while (packageString != NULL) { 2124 // Write the R.java file out with the correct package name 2125 err = writeResourceSymbols(bundle, assets, String8(packageString), true); 2126 if (err < 0) { 2127 goto bail; 2128 } 2129 packageString = strtok(NULL, ":"); 2130 } 2131 libs.unlockBuffer(); 2132 } 2133 } else { 2134 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false); 2135 if (err < 0) { 2136 goto bail; 2137 } 2138 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true); 2139 if (err < 0) { 2140 goto bail; 2141 } 2142 } 2143 2144 // Write out the ProGuard file 2145 err = writeProguardFile(bundle, assets); 2146 if (err < 0) { 2147 goto bail; 2148 } 2149 2150 // Write the apk 2151 if (outputAPKFile) { 2152 err = writeAPK(bundle, assets, String8(outputAPKFile)); 2153 if (err != NO_ERROR) { 2154 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile); 2155 goto bail; 2156 } 2157 } 2158 2159 // If we've been asked to generate a dependency file, we need to finish up here. 2160 // the writeResourceSymbols and writeAPK functions have already written the target 2161 // half of the dependency file, now we need to write the prerequisites. (files that 2162 // the R.java file or .ap_ file depend on) 2163 if (bundle->getGenDependencies()) { 2164 // Now that writeResourceSymbols or writeAPK has taken care of writing 2165 // the targets to our dependency file, we'll write the prereqs 2166 fp = fopen(dependencyFile, "a+"); 2167 fprintf(fp, " : "); 2168 bool includeRaw = (outputAPKFile != NULL); 2169 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw); 2170 // Also manually add the AndroidManifeset since it's not under res/ or assets/ 2171 // and therefore was not added to our pathstores during slurping 2172 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile()); 2173 fclose(fp); 2174 } 2175 2176 retVal = 0; 2177bail: 2178 if (SourcePos::hasErrors()) { 2179 SourcePos::printErrors(stderr); 2180 } 2181 return retVal; 2182} 2183 2184/* 2185 * Do PNG Crunching 2186 * PRECONDITIONS 2187 * -S flag points to a source directory containing drawable* folders 2188 * -C flag points to destination directory. The folder structure in the 2189 * source directory will be mirrored to the destination (cache) directory 2190 * 2191 * POSTCONDITIONS 2192 * Destination directory will be updated to match the PNG files in 2193 * the source directory. 2194 */ 2195int doCrunch(Bundle* bundle) 2196{ 2197 fprintf(stdout, "Crunching PNG Files in "); 2198 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]); 2199 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir()); 2200 2201 updatePreProcessedCache(bundle); 2202 2203 return NO_ERROR; 2204} 2205 2206/* 2207 * Do PNG Crunching on a single flag 2208 * -i points to a single png file 2209 * -o points to a single png output file 2210 */ 2211int doSingleCrunch(Bundle* bundle) 2212{ 2213 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile()); 2214 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile()); 2215 2216 String8 input(bundle->getSingleCrunchInputFile()); 2217 String8 output(bundle->getSingleCrunchOutputFile()); 2218 2219 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) { 2220 // we can't return the status_t as it gets truncate to the lower 8 bits. 2221 return 42; 2222 } 2223 2224 return NO_ERROR; 2225} 2226 2227char CONSOLE_DATA[2925] = { 2228 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2229 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2230 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2231 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 2232 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63, 2233 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83, 2234 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2235 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2236 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81, 2237 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32, 2238 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 2239 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2240 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59, 2241 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2242 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2243 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 2244 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32, 2245 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2246 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2247 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87, 2248 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32, 2249 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 2250 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 2251 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58, 2252 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2253 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2254 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81, 2255 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32, 2256 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 2257 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2258 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59, 2259 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2260 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2261 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59, 2262 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32, 2263 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2264 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2265 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81, 2266 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 2267 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 2268 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2269 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81, 2270 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2271 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2272 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109, 2273 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32, 2274 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 2275 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59, 2276 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59, 2277 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2278 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59, 2279 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46, 2280 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32, 2281 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2282 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 2283 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81, 2284 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32, 2285 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81, 2286 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2287 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58, 2288 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32, 2289 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59, 2290 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37, 2291 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59, 2292 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 2293 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59, 2294 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96, 2295 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 2296 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32, 2297 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61, 2298 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59, 2299 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32, 2300 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2301 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119, 2302 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2303 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10, 2304 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2305 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81, 2306 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41, 2307 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 2308 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81, 2309 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 2310 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32, 2311 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2312 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 2313 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 2314 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 2315 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 2316 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 2317 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2318 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2319 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81, 2320 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32, 2321 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2322 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2323 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2324 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2325 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 2326 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106, 2327 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59, 2328 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2329 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2330 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81, 2331 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 2332 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 2333 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2334 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59, 2335 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2336 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2337 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81, 2338 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32, 2339 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2340 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2341 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2342 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2343 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 2344 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 2345 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59, 2346 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2347 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2348 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33, 2349 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 2350 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 2351 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 2352 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59, 2353 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 2354 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 2355 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95, 2356 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59, 2357 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2358 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2359 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45, 2360 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32, 2361 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 2362 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59, 2363 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59, 2364 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2365 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2366 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32, 2367 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32, 2368 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 2369 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2370 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61, 2371 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2372 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 2373 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 2374 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59, 2375 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2376 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2377 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32, 2378 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32, 2379 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 2380 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61, 2381 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 2382 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2383 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2384 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32, 2385 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32, 2386 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2387 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2388 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2389 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2390 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10 2391 }; 2392