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