Command.cpp revision b8dc7bc582ee4acd9d6c1379701a002c5ddbe323
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 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 */ 41ZipFile* 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 */ 69ZipFile* 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 87bail: 88 return zip; 89} 90 91 92/* 93 * Return a short string describing the compression method. 94 */ 95const 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 */ 108int 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 */ 123int 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#ifdef HAVE_ANDROID_OS 200 static const bool kHaveAndroidOs = true; 201#else 202 static const bool kHaveAndroidOs = false; 203#endif 204 const ResTable& res = assets.getResources(false); 205 if (!kHaveAndroidOs) { 206 printf("\nResource table:\n"); 207 res.print(false); 208 } 209 210 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml", 211 Asset::ACCESS_BUFFER); 212 if (manifestAsset == NULL) { 213 printf("\nNo AndroidManifest.xml found.\n"); 214 } else { 215 printf("\nAndroid manifest:\n"); 216 ResXMLTree tree; 217 tree.setTo(manifestAsset->getBuffer(true), 218 manifestAsset->getLength()); 219 printXMLBlock(&tree); 220 } 221 delete manifestAsset; 222 } 223 224 result = 0; 225 226bail: 227 delete zip; 228 return result; 229} 230 231static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes) 232{ 233 size_t N = tree.getAttributeCount(); 234 for (size_t i=0; i<N; i++) { 235 if (tree.getAttributeNameResID(i) == attrRes) { 236 return (ssize_t)i; 237 } 238 } 239 return -1; 240} 241 242String8 getAttribute(const ResXMLTree& tree, const char* ns, 243 const char* attr, String8* outError) 244{ 245 ssize_t idx = tree.indexOfAttribute(ns, attr); 246 if (idx < 0) { 247 return String8(); 248 } 249 Res_value value; 250 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 251 if (value.dataType != Res_value::TYPE_STRING) { 252 if (outError != NULL) *outError = "attribute is not a string value"; 253 return String8(); 254 } 255 } 256 size_t len; 257 const char16_t* str = tree.getAttributeStringValue(idx, &len); 258 return str ? String8(str, len) : String8(); 259} 260 261static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError) 262{ 263 ssize_t idx = indexOfAttribute(tree, attrRes); 264 if (idx < 0) { 265 return String8(); 266 } 267 Res_value value; 268 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 269 if (value.dataType != Res_value::TYPE_STRING) { 270 if (outError != NULL) *outError = "attribute is not a string value"; 271 return String8(); 272 } 273 } 274 size_t len; 275 const char16_t* str = tree.getAttributeStringValue(idx, &len); 276 return str ? String8(str, len) : String8(); 277} 278 279static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes, 280 String8* outError, int32_t defValue = -1) 281{ 282 ssize_t idx = indexOfAttribute(tree, attrRes); 283 if (idx < 0) { 284 return defValue; 285 } 286 Res_value value; 287 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 288 if (value.dataType < Res_value::TYPE_FIRST_INT 289 || value.dataType > Res_value::TYPE_LAST_INT) { 290 if (outError != NULL) *outError = "attribute is not an integer value"; 291 return defValue; 292 } 293 } 294 return value.data; 295} 296 297static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree, 298 uint32_t attrRes, String8* outError, int32_t defValue = -1) 299{ 300 ssize_t idx = indexOfAttribute(tree, attrRes); 301 if (idx < 0) { 302 return defValue; 303 } 304 Res_value value; 305 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 306 if (value.dataType == Res_value::TYPE_REFERENCE) { 307 resTable->resolveReference(&value, 0); 308 } 309 if (value.dataType < Res_value::TYPE_FIRST_INT 310 || value.dataType > Res_value::TYPE_LAST_INT) { 311 if (outError != NULL) *outError = "attribute is not an integer value"; 312 return defValue; 313 } 314 } 315 return value.data; 316} 317 318static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree, 319 uint32_t attrRes, String8* outError) 320{ 321 ssize_t idx = indexOfAttribute(tree, attrRes); 322 if (idx < 0) { 323 return String8(); 324 } 325 Res_value value; 326 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 327 if (value.dataType == Res_value::TYPE_STRING) { 328 size_t len; 329 const char16_t* str = tree.getAttributeStringValue(idx, &len); 330 return str ? String8(str, len) : String8(); 331 } 332 resTable->resolveReference(&value, 0); 333 if (value.dataType != Res_value::TYPE_STRING) { 334 if (outError != NULL) *outError = "attribute is not a string value"; 335 return String8(); 336 } 337 } 338 size_t len; 339 const Res_value* value2 = &value; 340 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len); 341 return str ? String8(str, len) : String8(); 342} 343 344// These are attribute resource constants for the platform, as found 345// in android.R.attr 346enum { 347 LABEL_ATTR = 0x01010001, 348 ICON_ATTR = 0x01010002, 349 NAME_ATTR = 0x01010003, 350 PERMISSION_ATTR = 0x01010006, 351 RESOURCE_ATTR = 0x01010025, 352 DEBUGGABLE_ATTR = 0x0101000f, 353 VERSION_CODE_ATTR = 0x0101021b, 354 VERSION_NAME_ATTR = 0x0101021c, 355 SCREEN_ORIENTATION_ATTR = 0x0101001e, 356 MIN_SDK_VERSION_ATTR = 0x0101020c, 357 MAX_SDK_VERSION_ATTR = 0x01010271, 358 REQ_TOUCH_SCREEN_ATTR = 0x01010227, 359 REQ_KEYBOARD_TYPE_ATTR = 0x01010228, 360 REQ_HARD_KEYBOARD_ATTR = 0x01010229, 361 REQ_NAVIGATION_ATTR = 0x0101022a, 362 REQ_FIVE_WAY_NAV_ATTR = 0x01010232, 363 TARGET_SDK_VERSION_ATTR = 0x01010270, 364 TEST_ONLY_ATTR = 0x01010272, 365 ANY_DENSITY_ATTR = 0x0101026c, 366 GL_ES_VERSION_ATTR = 0x01010281, 367 SMALL_SCREEN_ATTR = 0x01010284, 368 NORMAL_SCREEN_ATTR = 0x01010285, 369 LARGE_SCREEN_ATTR = 0x01010286, 370 XLARGE_SCREEN_ATTR = 0x010102bf, 371 REQUIRED_ATTR = 0x0101028e, 372 SCREEN_SIZE_ATTR = 0x010102ca, 373 SCREEN_DENSITY_ATTR = 0x010102cb, 374 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364, 375 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365, 376 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366, 377 PUBLIC_KEY_ATTR = 0x010103a6, 378 CATEGORY_ATTR = 0x010103e8, 379}; 380 381const char *getComponentName(String8 &pkgName, String8 &componentName) { 382 ssize_t idx = componentName.find("."); 383 String8 retStr(pkgName); 384 if (idx == 0) { 385 retStr += componentName; 386 } else if (idx < 0) { 387 retStr += "."; 388 retStr += componentName; 389 } else { 390 return componentName.string(); 391 } 392 return retStr.string(); 393} 394 395static void printCompatibleScreens(ResXMLTree& tree) { 396 size_t len; 397 ResXMLTree::event_code_t code; 398 int depth = 0; 399 bool first = true; 400 printf("compatible-screens:"); 401 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 402 if (code == ResXMLTree::END_TAG) { 403 depth--; 404 if (depth < 0) { 405 break; 406 } 407 continue; 408 } 409 if (code != ResXMLTree::START_TAG) { 410 continue; 411 } 412 depth++; 413 String8 tag(tree.getElementName(&len)); 414 if (tag == "screen") { 415 int32_t screenSize = getIntegerAttribute(tree, 416 SCREEN_SIZE_ATTR, NULL, -1); 417 int32_t screenDensity = getIntegerAttribute(tree, 418 SCREEN_DENSITY_ATTR, NULL, -1); 419 if (screenSize > 0 && screenDensity > 0) { 420 if (!first) { 421 printf(","); 422 } 423 first = false; 424 printf("'%d/%d'", screenSize, screenDensity); 425 } 426 } 427 } 428 printf("\n"); 429} 430 431Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost, 432 String8 *outError = NULL) 433{ 434 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER); 435 if (aidAsset == NULL) { 436 if (outError != NULL) *outError = "xml resource does not exist"; 437 return Vector<String8>(); 438 } 439 440 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service"); 441 442 bool withinApduService = false; 443 Vector<String8> categories; 444 445 String8 error; 446 ResXMLTree tree; 447 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength()); 448 449 size_t len; 450 int depth = 0; 451 ResXMLTree::event_code_t code; 452 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 453 if (code == ResXMLTree::END_TAG) { 454 depth--; 455 String8 tag(tree.getElementName(&len)); 456 457 if (depth == 0 && tag == serviceTagName) { 458 withinApduService = false; 459 } 460 461 } else if (code == ResXMLTree::START_TAG) { 462 depth++; 463 String8 tag(tree.getElementName(&len)); 464 465 if (depth == 1) { 466 if (tag == serviceTagName) { 467 withinApduService = true; 468 } 469 } else if (depth == 2 && withinApduService) { 470 if (tag == "aid-group") { 471 String8 category = getAttribute(tree, CATEGORY_ATTR, &error); 472 if (error != "") { 473 if (outError != NULL) *outError = error; 474 return Vector<String8>(); 475 } 476 477 categories.add(category); 478 } 479 } 480 } 481 } 482 aidAsset->close(); 483 return categories; 484} 485 486/* 487 * Handle the "dump" command, to extract select data from an archive. 488 */ 489extern char CONSOLE_DATA[2925]; // see EOF 490int doDump(Bundle* bundle) 491{ 492 status_t result = UNKNOWN_ERROR; 493 Asset* asset = NULL; 494 495 if (bundle->getFileSpecCount() < 1) { 496 fprintf(stderr, "ERROR: no dump option specified\n"); 497 return 1; 498 } 499 500 if (bundle->getFileSpecCount() < 2) { 501 fprintf(stderr, "ERROR: no dump file specified\n"); 502 return 1; 503 } 504 505 const char* option = bundle->getFileSpecEntry(0); 506 const char* filename = bundle->getFileSpecEntry(1); 507 508 AssetManager assets; 509 int32_t assetsCookie; 510 if (!assets.addAssetPath(String8(filename), &assetsCookie)) { 511 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n"); 512 return 1; 513 } 514 515 // Make a dummy config for retrieving resources... we need to supply 516 // non-default values for some configs so that we can retrieve resources 517 // in the app that don't have a default. The most important of these is 518 // the API version because key resources like icons will have an implicit 519 // version if they are using newer config types like density. 520 ResTable_config config; 521 memset(&config, 0, sizeof(ResTable_config)); 522 config.language[0] = 'e'; 523 config.language[1] = 'n'; 524 config.country[0] = 'U'; 525 config.country[1] = 'S'; 526 config.orientation = ResTable_config::ORIENTATION_PORT; 527 config.density = ResTable_config::DENSITY_MEDIUM; 528 config.sdkVersion = 10000; // Very high. 529 config.screenWidthDp = 320; 530 config.screenHeightDp = 480; 531 config.smallestScreenWidthDp = 320; 532 assets.setConfiguration(config); 533 534 const ResTable& res = assets.getResources(false); 535 if (strcmp("resources", option) == 0) { 536#ifndef HAVE_ANDROID_OS 537 res.print(bundle->getValues()); 538#endif 539 540 } else if (strcmp("strings", option) == 0) { 541 const ResStringPool* pool = res.getTableStringBlock(0); 542 printStringPool(pool); 543 544 } else if (strcmp("xmltree", option) == 0) { 545 if (bundle->getFileSpecCount() < 3) { 546 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n"); 547 goto bail; 548 } 549 550 for (int i=2; i<bundle->getFileSpecCount(); i++) { 551 const char* resname = bundle->getFileSpecEntry(i); 552 ResXMLTree tree; 553 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER); 554 if (asset == NULL) { 555 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname); 556 goto bail; 557 } 558 559 if (tree.setTo(asset->getBuffer(true), 560 asset->getLength()) != NO_ERROR) { 561 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname); 562 goto bail; 563 } 564 tree.restart(); 565 printXMLBlock(&tree); 566 tree.uninit(); 567 delete asset; 568 asset = NULL; 569 } 570 571 } else if (strcmp("xmlstrings", option) == 0) { 572 if (bundle->getFileSpecCount() < 3) { 573 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n"); 574 goto bail; 575 } 576 577 for (int i=2; i<bundle->getFileSpecCount(); i++) { 578 const char* resname = bundle->getFileSpecEntry(i); 579 ResXMLTree tree; 580 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER); 581 if (asset == NULL) { 582 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname); 583 goto bail; 584 } 585 586 if (tree.setTo(asset->getBuffer(true), 587 asset->getLength()) != NO_ERROR) { 588 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname); 589 goto bail; 590 } 591 printStringPool(&tree.getStrings()); 592 delete asset; 593 asset = NULL; 594 } 595 596 } else { 597 ResXMLTree tree; 598 asset = assets.openNonAsset("AndroidManifest.xml", 599 Asset::ACCESS_BUFFER); 600 if (asset == NULL) { 601 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n"); 602 goto bail; 603 } 604 605 if (tree.setTo(asset->getBuffer(true), 606 asset->getLength()) != NO_ERROR) { 607 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n"); 608 goto bail; 609 } 610 tree.restart(); 611 612 if (strcmp("permissions", option) == 0) { 613 size_t len; 614 ResXMLTree::event_code_t code; 615 int depth = 0; 616 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 617 if (code == ResXMLTree::END_TAG) { 618 depth--; 619 continue; 620 } 621 if (code != ResXMLTree::START_TAG) { 622 continue; 623 } 624 depth++; 625 String8 tag(tree.getElementName(&len)); 626 //printf("Depth %d tag %s\n", depth, tag.string()); 627 if (depth == 1) { 628 if (tag != "manifest") { 629 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 630 goto bail; 631 } 632 String8 pkg = getAttribute(tree, NULL, "package", NULL); 633 printf("package: %s\n", pkg.string()); 634 } else if (depth == 2 && tag == "permission") { 635 String8 error; 636 String8 name = getAttribute(tree, NAME_ATTR, &error); 637 if (error != "") { 638 fprintf(stderr, "ERROR: %s\n", error.string()); 639 goto bail; 640 } 641 printf("permission: %s\n", name.string()); 642 } else if (depth == 2 && tag == "uses-permission") { 643 String8 error; 644 String8 name = getAttribute(tree, NAME_ATTR, &error); 645 if (error != "") { 646 fprintf(stderr, "ERROR: %s\n", error.string()); 647 goto bail; 648 } 649 printf("uses-permission: %s\n", name.string()); 650 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1); 651 if (!req) { 652 printf("optional-permission: %s\n", name.string()); 653 } 654 } 655 } 656 } else if (strcmp("badging", option) == 0) { 657 Vector<String8> locales; 658 res.getLocales(&locales); 659 660 Vector<ResTable_config> configs; 661 res.getConfigurations(&configs); 662 SortedVector<int> densities; 663 const size_t NC = configs.size(); 664 for (size_t i=0; i<NC; i++) { 665 int dens = configs[i].density; 666 if (dens == 0) dens = 160; 667 densities.add(dens); 668 } 669 670 size_t len; 671 ResXMLTree::event_code_t code; 672 int depth = 0; 673 String8 error; 674 bool withinActivity = false; 675 bool isMainActivity = false; 676 bool isLauncherActivity = false; 677 bool isSearchable = false; 678 bool withinApplication = false; 679 bool withinSupportsInput = false; 680 bool withinReceiver = false; 681 bool withinService = false; 682 bool withinIntentFilter = false; 683 bool hasMainActivity = false; 684 bool hasOtherActivities = false; 685 bool hasOtherReceivers = false; 686 bool hasOtherServices = false; 687 bool hasWallpaperService = false; 688 bool hasImeService = false; 689 bool hasAccessibilityService = false; 690 bool hasPrintService = false; 691 bool hasWidgetReceivers = false; 692 bool hasDeviceAdminReceiver = false; 693 bool hasIntentFilter = false; 694 bool hasPaymentService = false; 695 bool actMainActivity = false; 696 bool actWidgetReceivers = false; 697 bool actDeviceAdminEnabled = false; 698 bool actImeService = false; 699 bool actWallpaperService = false; 700 bool actAccessibilityService = false; 701 bool actPrintService = false; 702 bool actHostApduService = false; 703 bool actOffHostApduService = false; 704 bool hasMetaHostPaymentCategory = false; 705 bool hasMetaOffHostPaymentCategory = false; 706 707 // These permissions are required by services implementing services 708 // the system binds to (IME, Accessibility, PrintServices, etc.) 709 bool hasBindDeviceAdminPermission = false; 710 bool hasBindInputMethodPermission = false; 711 bool hasBindAccessibilityServicePermission = false; 712 bool hasBindPrintServicePermission = false; 713 bool hasBindNfcServicePermission = false; 714 715 // These two implement the implicit permissions that are granted 716 // to pre-1.6 applications. 717 bool hasWriteExternalStoragePermission = false; 718 bool hasReadPhoneStatePermission = false; 719 720 // If an app requests write storage, they will also get read storage. 721 bool hasReadExternalStoragePermission = false; 722 723 // Implement transition to read and write call log. 724 bool hasReadContactsPermission = false; 725 bool hasWriteContactsPermission = false; 726 bool hasReadCallLogPermission = false; 727 bool hasWriteCallLogPermission = false; 728 729 // This next group of variables is used to implement a group of 730 // backward-compatibility heuristics necessitated by the addition of 731 // some new uses-feature constants in 2.1 and 2.2. In most cases, the 732 // heuristic is "if an app requests a permission but doesn't explicitly 733 // request the corresponding <uses-feature>, presume it's there anyway". 734 bool specCameraFeature = false; // camera-related 735 bool specCameraAutofocusFeature = false; 736 bool reqCameraAutofocusFeature = false; 737 bool reqCameraFlashFeature = false; 738 bool hasCameraPermission = false; 739 bool specLocationFeature = false; // location-related 740 bool specNetworkLocFeature = false; 741 bool reqNetworkLocFeature = false; 742 bool specGpsFeature = false; 743 bool reqGpsFeature = false; 744 bool hasMockLocPermission = false; 745 bool hasCoarseLocPermission = false; 746 bool hasGpsPermission = false; 747 bool hasGeneralLocPermission = false; 748 bool specBluetoothFeature = false; // Bluetooth API-related 749 bool hasBluetoothPermission = false; 750 bool specMicrophoneFeature = false; // microphone-related 751 bool hasRecordAudioPermission = false; 752 bool specWiFiFeature = false; 753 bool hasWiFiPermission = false; 754 bool specTelephonyFeature = false; // telephony-related 755 bool reqTelephonySubFeature = false; 756 bool hasTelephonyPermission = false; 757 bool specTouchscreenFeature = false; // touchscreen-related 758 bool specMultitouchFeature = false; 759 bool reqDistinctMultitouchFeature = false; 760 bool specScreenPortraitFeature = false; 761 bool specScreenLandscapeFeature = false; 762 bool reqScreenPortraitFeature = false; 763 bool reqScreenLandscapeFeature = false; 764 // 2.2 also added some other features that apps can request, but that 765 // have no corresponding permission, so we cannot implement any 766 // back-compatibility heuristic for them. The below are thus unnecessary 767 // (but are retained here for documentary purposes.) 768 //bool specCompassFeature = false; 769 //bool specAccelerometerFeature = false; 770 //bool specProximityFeature = false; 771 //bool specAmbientLightFeature = false; 772 //bool specLiveWallpaperFeature = false; 773 774 int targetSdk = 0; 775 int smallScreen = 1; 776 int normalScreen = 1; 777 int largeScreen = 1; 778 int xlargeScreen = 1; 779 int anyDensity = 1; 780 int requiresSmallestWidthDp = 0; 781 int compatibleWidthLimitDp = 0; 782 int largestWidthLimitDp = 0; 783 String8 pkg; 784 String8 activityName; 785 String8 activityLabel; 786 String8 activityIcon; 787 String8 receiverName; 788 String8 serviceName; 789 Vector<String8> supportedInput; 790 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 791 if (code == ResXMLTree::END_TAG) { 792 depth--; 793 if (depth < 2) { 794 if (withinSupportsInput && !supportedInput.isEmpty()) { 795 printf("supports-input: '"); 796 const size_t N = supportedInput.size(); 797 for (size_t i=0; i<N; i++) { 798 printf("%s", supportedInput[i].string()); 799 if (i != N - 1) { 800 printf("' '"); 801 } else { 802 printf("'\n"); 803 } 804 } 805 supportedInput.clear(); 806 } 807 withinApplication = false; 808 withinSupportsInput = false; 809 } else if (depth < 3) { 810 if (withinActivity && isMainActivity && isLauncherActivity) { 811 const char *aName = getComponentName(pkg, activityName); 812 printf("launchable-activity:"); 813 if (aName != NULL) { 814 printf(" name='%s' ", aName); 815 } 816 printf(" label='%s' icon='%s'\n", 817 activityLabel.string(), 818 activityIcon.string()); 819 } 820 if (!hasIntentFilter) { 821 hasOtherActivities |= withinActivity; 822 hasOtherReceivers |= withinReceiver; 823 hasOtherServices |= withinService; 824 } else { 825 if (withinService) { 826 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory && 827 hasBindNfcServicePermission); 828 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory && 829 hasBindNfcServicePermission); 830 } 831 } 832 withinActivity = false; 833 withinService = false; 834 withinReceiver = false; 835 hasIntentFilter = false; 836 isMainActivity = isLauncherActivity = false; 837 } else if (depth < 4) { 838 if (withinIntentFilter) { 839 if (withinActivity) { 840 hasMainActivity |= actMainActivity; 841 hasOtherActivities |= !actMainActivity; 842 } else if (withinReceiver) { 843 hasWidgetReceivers |= actWidgetReceivers; 844 hasDeviceAdminReceiver |= (actDeviceAdminEnabled && 845 hasBindDeviceAdminPermission); 846 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled); 847 } else if (withinService) { 848 hasImeService |= actImeService; 849 hasWallpaperService |= actWallpaperService; 850 hasAccessibilityService |= (actAccessibilityService && 851 hasBindAccessibilityServicePermission); 852 hasPrintService |= (actPrintService && hasBindPrintServicePermission); 853 hasOtherServices |= (!actImeService && !actWallpaperService && 854 !actAccessibilityService && !actPrintService && 855 !actHostApduService && !actOffHostApduService); 856 } 857 } 858 withinIntentFilter = false; 859 } 860 continue; 861 } 862 if (code != ResXMLTree::START_TAG) { 863 continue; 864 } 865 depth++; 866 String8 tag(tree.getElementName(&len)); 867 //printf("Depth %d, %s\n", depth, tag.string()); 868 if (depth == 1) { 869 if (tag != "manifest") { 870 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 871 goto bail; 872 } 873 pkg = getAttribute(tree, NULL, "package", NULL); 874 printf("package: name='%s' ", pkg.string()); 875 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error); 876 if (error != "") { 877 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string()); 878 goto bail; 879 } 880 if (versionCode > 0) { 881 printf("versionCode='%d' ", versionCode); 882 } else { 883 printf("versionCode='' "); 884 } 885 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error); 886 if (error != "") { 887 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string()); 888 goto bail; 889 } 890 printf("versionName='%s'\n", versionName.string()); 891 } else if (depth == 2) { 892 withinApplication = false; 893 if (tag == "application") { 894 withinApplication = true; 895 896 String8 label; 897 const size_t NL = locales.size(); 898 for (size_t i=0; i<NL; i++) { 899 const char* localeStr = locales[i].string(); 900 assets.setLocale(localeStr != NULL ? localeStr : ""); 901 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); 902 if (llabel != "") { 903 if (localeStr == NULL || strlen(localeStr) == 0) { 904 label = llabel; 905 printf("application-label:'%s'\n", llabel.string()); 906 } else { 907 if (label == "") { 908 label = llabel; 909 } 910 printf("application-label-%s:'%s'\n", localeStr, 911 llabel.string()); 912 } 913 } 914 } 915 916 ResTable_config tmpConfig = config; 917 const size_t ND = densities.size(); 918 for (size_t i=0; i<ND; i++) { 919 tmpConfig.density = densities[i]; 920 assets.setConfiguration(tmpConfig); 921 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 922 if (icon != "") { 923 printf("application-icon-%d:'%s'\n", densities[i], icon.string()); 924 } 925 } 926 assets.setConfiguration(config); 927 928 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 929 if (error != "") { 930 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string()); 931 goto bail; 932 } 933 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0); 934 if (error != "") { 935 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string()); 936 goto bail; 937 } 938 printf("application: label='%s' ", label.string()); 939 printf("icon='%s'\n", icon.string()); 940 if (testOnly != 0) { 941 printf("testOnly='%d'\n", testOnly); 942 } 943 944 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0); 945 if (error != "") { 946 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string()); 947 goto bail; 948 } 949 if (debuggable != 0) { 950 printf("application-debuggable\n"); 951 } 952 } else if (tag == "uses-sdk") { 953 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error); 954 if (error != "") { 955 error = ""; 956 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error); 957 if (error != "") { 958 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n", 959 error.string()); 960 goto bail; 961 } 962 if (name == "Donut") targetSdk = 4; 963 printf("sdkVersion:'%s'\n", name.string()); 964 } else if (code != -1) { 965 targetSdk = code; 966 printf("sdkVersion:'%d'\n", code); 967 } 968 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1); 969 if (code != -1) { 970 printf("maxSdkVersion:'%d'\n", code); 971 } 972 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error); 973 if (error != "") { 974 error = ""; 975 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error); 976 if (error != "") { 977 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n", 978 error.string()); 979 goto bail; 980 } 981 if (name == "Donut" && targetSdk < 4) targetSdk = 4; 982 printf("targetSdkVersion:'%s'\n", name.string()); 983 } else if (code != -1) { 984 if (targetSdk < code) { 985 targetSdk = code; 986 } 987 printf("targetSdkVersion:'%d'\n", code); 988 } 989 } else if (tag == "uses-configuration") { 990 int32_t reqTouchScreen = getIntegerAttribute(tree, 991 REQ_TOUCH_SCREEN_ATTR, NULL, 0); 992 int32_t reqKeyboardType = getIntegerAttribute(tree, 993 REQ_KEYBOARD_TYPE_ATTR, NULL, 0); 994 int32_t reqHardKeyboard = getIntegerAttribute(tree, 995 REQ_HARD_KEYBOARD_ATTR, NULL, 0); 996 int32_t reqNavigation = getIntegerAttribute(tree, 997 REQ_NAVIGATION_ATTR, NULL, 0); 998 int32_t reqFiveWayNav = getIntegerAttribute(tree, 999 REQ_FIVE_WAY_NAV_ATTR, NULL, 0); 1000 printf("uses-configuration:"); 1001 if (reqTouchScreen != 0) { 1002 printf(" reqTouchScreen='%d'", reqTouchScreen); 1003 } 1004 if (reqKeyboardType != 0) { 1005 printf(" reqKeyboardType='%d'", reqKeyboardType); 1006 } 1007 if (reqHardKeyboard != 0) { 1008 printf(" reqHardKeyboard='%d'", reqHardKeyboard); 1009 } 1010 if (reqNavigation != 0) { 1011 printf(" reqNavigation='%d'", reqNavigation); 1012 } 1013 if (reqFiveWayNav != 0) { 1014 printf(" reqFiveWayNav='%d'", reqFiveWayNav); 1015 } 1016 printf("\n"); 1017 } else if (tag == "supports-input") { 1018 withinSupportsInput = true; 1019 } else if (tag == "supports-screens") { 1020 smallScreen = getIntegerAttribute(tree, 1021 SMALL_SCREEN_ATTR, NULL, 1); 1022 normalScreen = getIntegerAttribute(tree, 1023 NORMAL_SCREEN_ATTR, NULL, 1); 1024 largeScreen = getIntegerAttribute(tree, 1025 LARGE_SCREEN_ATTR, NULL, 1); 1026 xlargeScreen = getIntegerAttribute(tree, 1027 XLARGE_SCREEN_ATTR, NULL, 1); 1028 anyDensity = getIntegerAttribute(tree, 1029 ANY_DENSITY_ATTR, NULL, 1); 1030 requiresSmallestWidthDp = getIntegerAttribute(tree, 1031 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0); 1032 compatibleWidthLimitDp = getIntegerAttribute(tree, 1033 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0); 1034 largestWidthLimitDp = getIntegerAttribute(tree, 1035 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0); 1036 } else if (tag == "uses-feature") { 1037 String8 name = getAttribute(tree, NAME_ATTR, &error); 1038 1039 if (name != "" && error == "") { 1040 int req = getIntegerAttribute(tree, 1041 REQUIRED_ATTR, NULL, 1); 1042 1043 if (name == "android.hardware.camera") { 1044 specCameraFeature = true; 1045 } else if (name == "android.hardware.camera.autofocus") { 1046 // these have no corresponding permission to check for, 1047 // but should imply the foundational camera permission 1048 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req; 1049 specCameraAutofocusFeature = true; 1050 } else if (req && (name == "android.hardware.camera.flash")) { 1051 // these have no corresponding permission to check for, 1052 // but should imply the foundational camera permission 1053 reqCameraFlashFeature = true; 1054 } else if (name == "android.hardware.location") { 1055 specLocationFeature = true; 1056 } else if (name == "android.hardware.location.network") { 1057 specNetworkLocFeature = true; 1058 reqNetworkLocFeature = reqNetworkLocFeature || req; 1059 } else if (name == "android.hardware.location.gps") { 1060 specGpsFeature = true; 1061 reqGpsFeature = reqGpsFeature || req; 1062 } else if (name == "android.hardware.bluetooth") { 1063 specBluetoothFeature = true; 1064 } else if (name == "android.hardware.touchscreen") { 1065 specTouchscreenFeature = true; 1066 } else if (name == "android.hardware.touchscreen.multitouch") { 1067 specMultitouchFeature = true; 1068 } else if (name == "android.hardware.touchscreen.multitouch.distinct") { 1069 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req; 1070 } else if (name == "android.hardware.microphone") { 1071 specMicrophoneFeature = true; 1072 } else if (name == "android.hardware.wifi") { 1073 specWiFiFeature = true; 1074 } else if (name == "android.hardware.telephony") { 1075 specTelephonyFeature = true; 1076 } else if (req && (name == "android.hardware.telephony.gsm" || 1077 name == "android.hardware.telephony.cdma")) { 1078 // these have no corresponding permission to check for, 1079 // but should imply the foundational telephony permission 1080 reqTelephonySubFeature = true; 1081 } else if (name == "android.hardware.screen.portrait") { 1082 specScreenPortraitFeature = true; 1083 } else if (name == "android.hardware.screen.landscape") { 1084 specScreenLandscapeFeature = true; 1085 } 1086 printf("uses-feature%s:'%s'\n", 1087 req ? "" : "-not-required", name.string()); 1088 } else { 1089 int vers = getIntegerAttribute(tree, 1090 GL_ES_VERSION_ATTR, &error); 1091 if (error == "") { 1092 printf("uses-gl-es:'0x%x'\n", vers); 1093 } 1094 } 1095 } else if (tag == "uses-permission") { 1096 String8 name = getAttribute(tree, NAME_ATTR, &error); 1097 if (name != "" && error == "") { 1098 if (name == "android.permission.CAMERA") { 1099 hasCameraPermission = true; 1100 } else if (name == "android.permission.ACCESS_FINE_LOCATION") { 1101 hasGpsPermission = true; 1102 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") { 1103 hasMockLocPermission = true; 1104 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") { 1105 hasCoarseLocPermission = true; 1106 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" || 1107 name == "android.permission.INSTALL_LOCATION_PROVIDER") { 1108 hasGeneralLocPermission = true; 1109 } else if (name == "android.permission.BLUETOOTH" || 1110 name == "android.permission.BLUETOOTH_ADMIN") { 1111 hasBluetoothPermission = true; 1112 } else if (name == "android.permission.RECORD_AUDIO") { 1113 hasRecordAudioPermission = true; 1114 } else if (name == "android.permission.ACCESS_WIFI_STATE" || 1115 name == "android.permission.CHANGE_WIFI_STATE" || 1116 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") { 1117 hasWiFiPermission = true; 1118 } else if (name == "android.permission.CALL_PHONE" || 1119 name == "android.permission.CALL_PRIVILEGED" || 1120 name == "android.permission.MODIFY_PHONE_STATE" || 1121 name == "android.permission.PROCESS_OUTGOING_CALLS" || 1122 name == "android.permission.READ_SMS" || 1123 name == "android.permission.RECEIVE_SMS" || 1124 name == "android.permission.RECEIVE_MMS" || 1125 name == "android.permission.RECEIVE_WAP_PUSH" || 1126 name == "android.permission.SEND_SMS" || 1127 name == "android.permission.WRITE_APN_SETTINGS" || 1128 name == "android.permission.WRITE_SMS") { 1129 hasTelephonyPermission = true; 1130 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") { 1131 hasWriteExternalStoragePermission = true; 1132 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") { 1133 hasReadExternalStoragePermission = true; 1134 } else if (name == "android.permission.READ_PHONE_STATE") { 1135 hasReadPhoneStatePermission = true; 1136 } else if (name == "android.permission.READ_CONTACTS") { 1137 hasReadContactsPermission = true; 1138 } else if (name == "android.permission.WRITE_CONTACTS") { 1139 hasWriteContactsPermission = true; 1140 } else if (name == "android.permission.READ_CALL_LOG") { 1141 hasReadCallLogPermission = true; 1142 } else if (name == "android.permission.WRITE_CALL_LOG") { 1143 hasWriteCallLogPermission = true; 1144 } 1145 printf("uses-permission:'%s'\n", name.string()); 1146 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1); 1147 if (!req) { 1148 printf("optional-permission:'%s'\n", name.string()); 1149 } 1150 } else { 1151 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1152 error.string()); 1153 goto bail; 1154 } 1155 } else if (tag == "uses-package") { 1156 String8 name = getAttribute(tree, NAME_ATTR, &error); 1157 if (name != "" && error == "") { 1158 printf("uses-package:'%s'\n", name.string()); 1159 } else { 1160 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1161 error.string()); 1162 goto bail; 1163 } 1164 } else if (tag == "original-package") { 1165 String8 name = getAttribute(tree, NAME_ATTR, &error); 1166 if (name != "" && error == "") { 1167 printf("original-package:'%s'\n", name.string()); 1168 } else { 1169 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1170 error.string()); 1171 goto bail; 1172 } 1173 } else if (tag == "supports-gl-texture") { 1174 String8 name = getAttribute(tree, NAME_ATTR, &error); 1175 if (name != "" && error == "") { 1176 printf("supports-gl-texture:'%s'\n", name.string()); 1177 } else { 1178 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1179 error.string()); 1180 goto bail; 1181 } 1182 } else if (tag == "compatible-screens") { 1183 printCompatibleScreens(tree); 1184 depth--; 1185 } else if (tag == "package-verifier") { 1186 String8 name = getAttribute(tree, NAME_ATTR, &error); 1187 if (name != "" && error == "") { 1188 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error); 1189 if (publicKey != "" && error == "") { 1190 printf("package-verifier: name='%s' publicKey='%s'\n", 1191 name.string(), publicKey.string()); 1192 } 1193 } 1194 } 1195 } else if (depth == 3) { 1196 withinActivity = false; 1197 withinReceiver = false; 1198 withinService = false; 1199 hasIntentFilter = false; 1200 hasMetaHostPaymentCategory = false; 1201 hasMetaOffHostPaymentCategory = false; 1202 hasBindDeviceAdminPermission = false; 1203 hasBindInputMethodPermission = false; 1204 hasBindAccessibilityServicePermission = false; 1205 hasBindPrintServicePermission = false; 1206 hasBindNfcServicePermission = false; 1207 if (withinApplication) { 1208 if(tag == "activity") { 1209 withinActivity = true; 1210 activityName = getAttribute(tree, NAME_ATTR, &error); 1211 if (error != "") { 1212 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1213 error.string()); 1214 goto bail; 1215 } 1216 1217 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); 1218 if (error != "") { 1219 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", 1220 error.string()); 1221 goto bail; 1222 } 1223 1224 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 1225 if (error != "") { 1226 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", 1227 error.string()); 1228 goto bail; 1229 } 1230 1231 int32_t orien = getResolvedIntegerAttribute(&res, tree, 1232 SCREEN_ORIENTATION_ATTR, &error); 1233 if (error == "") { 1234 if (orien == 0 || orien == 6 || orien == 8) { 1235 // Requests landscape, sensorLandscape, or reverseLandscape. 1236 reqScreenLandscapeFeature = true; 1237 } else if (orien == 1 || orien == 7 || orien == 9) { 1238 // Requests portrait, sensorPortrait, or reversePortrait. 1239 reqScreenPortraitFeature = true; 1240 } 1241 } 1242 } else if (tag == "uses-library") { 1243 String8 libraryName = getAttribute(tree, NAME_ATTR, &error); 1244 if (error != "") { 1245 fprintf(stderr, 1246 "ERROR getting 'android:name' attribute for uses-library" 1247 " %s\n", error.string()); 1248 goto bail; 1249 } 1250 int req = getIntegerAttribute(tree, 1251 REQUIRED_ATTR, NULL, 1); 1252 printf("uses-library%s:'%s'\n", 1253 req ? "" : "-not-required", libraryName.string()); 1254 } else if (tag == "receiver") { 1255 withinReceiver = true; 1256 receiverName = getAttribute(tree, NAME_ATTR, &error); 1257 1258 if (error != "") { 1259 fprintf(stderr, 1260 "ERROR getting 'android:name' attribute for receiver:" 1261 " %s\n", error.string()); 1262 goto bail; 1263 } 1264 1265 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error); 1266 if (error == "") { 1267 if (permission == "android.permission.BIND_DEVICE_ADMIN") { 1268 hasBindDeviceAdminPermission = true; 1269 } 1270 } else { 1271 fprintf(stderr, "ERROR getting 'android:permission' attribute for" 1272 " receiver '%s': %s\n", receiverName.string(), error.string()); 1273 } 1274 } else if (tag == "service") { 1275 withinService = true; 1276 serviceName = getAttribute(tree, NAME_ATTR, &error); 1277 1278 if (error != "") { 1279 fprintf(stderr, "ERROR getting 'android:name' attribute for" 1280 " service: %s\n", error.string()); 1281 goto bail; 1282 } 1283 1284 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error); 1285 if (error == "") { 1286 if (permission == "android.permission.BIND_INPUT_METHOD") { 1287 hasBindInputMethodPermission = true; 1288 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") { 1289 hasBindAccessibilityServicePermission = true; 1290 } else if (permission == "android.permission.BIND_PRINT_SERVICE") { 1291 hasBindPrintServicePermission = true; 1292 } else if (permission == "android.permission.BIND_NFC_SERVICE") { 1293 hasBindNfcServicePermission = true; 1294 } 1295 } else { 1296 fprintf(stderr, "ERROR getting 'android:permission' attribute for" 1297 " service '%s': %s\n", serviceName.string(), error.string()); 1298 } 1299 } 1300 } else if (withinSupportsInput && tag == "input-type") { 1301 String8 name = getAttribute(tree, NAME_ATTR, &error); 1302 if (name != "" && error == "") { 1303 supportedInput.add(name); 1304 } else { 1305 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1306 error.string()); 1307 goto bail; 1308 } 1309 } 1310 } else if (depth == 4) { 1311 if (tag == "intent-filter") { 1312 hasIntentFilter = true; 1313 withinIntentFilter = true; 1314 actMainActivity = false; 1315 actWidgetReceivers = false; 1316 actImeService = false; 1317 actWallpaperService = false; 1318 actAccessibilityService = false; 1319 actPrintService = false; 1320 actDeviceAdminEnabled = false; 1321 actHostApduService = false; 1322 actOffHostApduService = false; 1323 } else if (withinService && tag == "meta-data") { 1324 String8 name = getAttribute(tree, NAME_ATTR, &error); 1325 if (error != "") { 1326 fprintf(stderr, "ERROR getting 'android:name' attribute for" 1327 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string()); 1328 goto bail; 1329 } 1330 1331 if (name == "android.nfc.cardemulation.host_apdu_service" || 1332 name == "android.nfc.cardemulation.off_host_apdu_service") { 1333 bool offHost = true; 1334 if (name == "android.nfc.cardemulation.host_apdu_service") { 1335 offHost = false; 1336 } 1337 1338 String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error); 1339 if (error != "") { 1340 fprintf(stderr, "ERROR getting 'android:resource' attribute for" 1341 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string()); 1342 goto bail; 1343 } 1344 1345 Vector<String8> categories = getNfcAidCategories(assets, xmlPath, 1346 offHost, &error); 1347 if (error != "") { 1348 fprintf(stderr, "ERROR getting AID category for service '%s'\n", 1349 serviceName.string()); 1350 goto bail; 1351 } 1352 1353 const size_t catLen = categories.size(); 1354 for (size_t i = 0; i < catLen; i++) { 1355 bool paymentCategory = (categories[i] == "payment"); 1356 if (offHost) { 1357 hasMetaOffHostPaymentCategory |= paymentCategory; 1358 } else { 1359 hasMetaHostPaymentCategory |= paymentCategory; 1360 } 1361 } 1362 } 1363 } 1364 } else if ((depth == 5) && withinIntentFilter){ 1365 String8 action; 1366 if (tag == "action") { 1367 action = getAttribute(tree, NAME_ATTR, &error); 1368 if (error != "") { 1369 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); 1370 goto bail; 1371 } 1372 if (withinActivity) { 1373 if (action == "android.intent.action.MAIN") { 1374 isMainActivity = true; 1375 actMainActivity = true; 1376 } 1377 } else if (withinReceiver) { 1378 if (action == "android.appwidget.action.APPWIDGET_UPDATE") { 1379 actWidgetReceivers = true; 1380 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") { 1381 actDeviceAdminEnabled = true; 1382 } 1383 } else if (withinService) { 1384 if (action == "android.view.InputMethod") { 1385 actImeService = true; 1386 } else if (action == "android.service.wallpaper.WallpaperService") { 1387 actWallpaperService = true; 1388 } else if (action == "android.accessibilityservice.AccessibilityService") { 1389 actAccessibilityService = true; 1390 } else if (action == "android.printservice.PrintService") { 1391 actPrintService = true; 1392 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") { 1393 actHostApduService = true; 1394 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") { 1395 actOffHostApduService = true; 1396 } 1397 } 1398 if (action == "android.intent.action.SEARCH") { 1399 isSearchable = true; 1400 } 1401 } 1402 1403 if (tag == "category") { 1404 String8 category = getAttribute(tree, NAME_ATTR, &error); 1405 if (error != "") { 1406 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string()); 1407 goto bail; 1408 } 1409 if (withinActivity) { 1410 if (category == "android.intent.category.LAUNCHER") { 1411 isLauncherActivity = true; 1412 } 1413 } 1414 } 1415 } 1416 } 1417 1418 // Pre-1.6 implicitly granted permission compatibility logic 1419 if (targetSdk < 4) { 1420 if (!hasWriteExternalStoragePermission) { 1421 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n"); 1422 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \ 1423 "'targetSdkVersion < 4'\n"); 1424 hasWriteExternalStoragePermission = true; 1425 } 1426 if (!hasReadPhoneStatePermission) { 1427 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n"); 1428 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \ 1429 "'targetSdkVersion < 4'\n"); 1430 } 1431 } 1432 1433 // If the application has requested WRITE_EXTERNAL_STORAGE, we will 1434 // force them to always take READ_EXTERNAL_STORAGE as well. We always 1435 // do this (regardless of target API version) because we can't have 1436 // an app with write permission but not read permission. 1437 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) { 1438 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n"); 1439 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \ 1440 "'requested WRITE_EXTERNAL_STORAGE'\n"); 1441 } 1442 1443 // Pre-JellyBean call log permission compatibility. 1444 if (targetSdk < 16) { 1445 if (!hasReadCallLogPermission && hasReadContactsPermission) { 1446 printf("uses-permission:'android.permission.READ_CALL_LOG'\n"); 1447 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \ 1448 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n"); 1449 } 1450 if (!hasWriteCallLogPermission && hasWriteContactsPermission) { 1451 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n"); 1452 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \ 1453 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n"); 1454 } 1455 } 1456 1457 /* The following blocks handle printing "inferred" uses-features, based 1458 * on whether related features or permissions are used by the app. 1459 * Note that the various spec*Feature variables denote whether the 1460 * relevant tag was *present* in the AndroidManfest, not that it was 1461 * present and set to true. 1462 */ 1463 // Camera-related back-compatibility logic 1464 if (!specCameraFeature) { 1465 if (reqCameraFlashFeature) { 1466 // if app requested a sub-feature (autofocus or flash) and didn't 1467 // request the base camera feature, we infer that it meant to 1468 printf("uses-feature:'android.hardware.camera'\n"); 1469 printf("uses-implied-feature:'android.hardware.camera'," \ 1470 "'requested android.hardware.camera.flash feature'\n"); 1471 } else if (reqCameraAutofocusFeature) { 1472 // if app requested a sub-feature (autofocus or flash) and didn't 1473 // request the base camera feature, we infer that it meant to 1474 printf("uses-feature:'android.hardware.camera'\n"); 1475 printf("uses-implied-feature:'android.hardware.camera'," \ 1476 "'requested android.hardware.camera.autofocus feature'\n"); 1477 } else if (hasCameraPermission) { 1478 // if app wants to use camera but didn't request the feature, we infer 1479 // that it meant to, and further that it wants autofocus 1480 // (which was the 1.0 - 1.5 behavior) 1481 printf("uses-feature:'android.hardware.camera'\n"); 1482 if (!specCameraAutofocusFeature) { 1483 printf("uses-feature:'android.hardware.camera.autofocus'\n"); 1484 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \ 1485 "'requested android.permission.CAMERA permission'\n"); 1486 } 1487 } 1488 } 1489 1490 // Location-related back-compatibility logic 1491 if (!specLocationFeature && 1492 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission || 1493 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) { 1494 // if app either takes a location-related permission or requests one of the 1495 // sub-features, we infer that it also meant to request the base location feature 1496 printf("uses-feature:'android.hardware.location'\n"); 1497 printf("uses-implied-feature:'android.hardware.location'," \ 1498 "'requested a location access permission'\n"); 1499 } 1500 if (!specGpsFeature && hasGpsPermission) { 1501 // if app takes GPS (FINE location) perm but does not request the GPS 1502 // feature, we infer that it meant to 1503 printf("uses-feature:'android.hardware.location.gps'\n"); 1504 printf("uses-implied-feature:'android.hardware.location.gps'," \ 1505 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n"); 1506 } 1507 if (!specNetworkLocFeature && hasCoarseLocPermission) { 1508 // if app takes Network location (COARSE location) perm but does not request the 1509 // network location feature, we infer that it meant to 1510 printf("uses-feature:'android.hardware.location.network'\n"); 1511 printf("uses-implied-feature:'android.hardware.location.network'," \ 1512 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n"); 1513 } 1514 1515 // Bluetooth-related compatibility logic 1516 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) { 1517 // if app takes a Bluetooth permission but does not request the Bluetooth 1518 // feature, we infer that it meant to 1519 printf("uses-feature:'android.hardware.bluetooth'\n"); 1520 printf("uses-implied-feature:'android.hardware.bluetooth'," \ 1521 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \ 1522 "permission and targetSdkVersion > 4'\n"); 1523 } 1524 1525 // Microphone-related compatibility logic 1526 if (!specMicrophoneFeature && hasRecordAudioPermission) { 1527 // if app takes the record-audio permission but does not request the microphone 1528 // feature, we infer that it meant to 1529 printf("uses-feature:'android.hardware.microphone'\n"); 1530 printf("uses-implied-feature:'android.hardware.microphone'," \ 1531 "'requested android.permission.RECORD_AUDIO permission'\n"); 1532 } 1533 1534 // WiFi-related compatibility logic 1535 if (!specWiFiFeature && hasWiFiPermission) { 1536 // if app takes one of the WiFi permissions but does not request the WiFi 1537 // feature, we infer that it meant to 1538 printf("uses-feature:'android.hardware.wifi'\n"); 1539 printf("uses-implied-feature:'android.hardware.wifi'," \ 1540 "'requested android.permission.ACCESS_WIFI_STATE, " \ 1541 "android.permission.CHANGE_WIFI_STATE, or " \ 1542 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n"); 1543 } 1544 1545 // Telephony-related compatibility logic 1546 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) { 1547 // if app takes one of the telephony permissions or requests a sub-feature but 1548 // does not request the base telephony feature, we infer that it meant to 1549 printf("uses-feature:'android.hardware.telephony'\n"); 1550 printf("uses-implied-feature:'android.hardware.telephony'," \ 1551 "'requested a telephony-related permission or feature'\n"); 1552 } 1553 1554 // Touchscreen-related back-compatibility logic 1555 if (!specTouchscreenFeature) { // not a typo! 1556 // all apps are presumed to require a touchscreen, unless they explicitly say 1557 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/> 1558 // Note that specTouchscreenFeature is true if the tag is present, regardless 1559 // of whether its value is true or false, so this is safe 1560 printf("uses-feature:'android.hardware.touchscreen'\n"); 1561 printf("uses-implied-feature:'android.hardware.touchscreen'," \ 1562 "'assumed you require a touch screen unless explicitly made optional'\n"); 1563 } 1564 if (!specMultitouchFeature && reqDistinctMultitouchFeature) { 1565 // if app takes one of the telephony permissions or requests a sub-feature but 1566 // does not request the base telephony feature, we infer that it meant to 1567 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n"); 1568 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \ 1569 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n"); 1570 } 1571 1572 // Landscape/portrait-related compatibility logic 1573 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) { 1574 // If the app has specified any activities in its manifest 1575 // that request a specific orientation, then assume that 1576 // orientation is required. 1577 if (reqScreenLandscapeFeature) { 1578 printf("uses-feature:'android.hardware.screen.landscape'\n"); 1579 printf("uses-implied-feature:'android.hardware.screen.landscape'," \ 1580 "'one or more activities have specified a landscape orientation'\n"); 1581 } 1582 if (reqScreenPortraitFeature) { 1583 printf("uses-feature:'android.hardware.screen.portrait'\n"); 1584 printf("uses-implied-feature:'android.hardware.screen.portrait'," \ 1585 "'one or more activities have specified a portrait orientation'\n"); 1586 } 1587 } 1588 1589 if (hasMainActivity) { 1590 printf("main\n"); 1591 } 1592 if (hasWidgetReceivers) { 1593 printf("app-widget\n"); 1594 } 1595 if (hasDeviceAdminReceiver) { 1596 printf("device-admin\n"); 1597 } 1598 if (hasImeService) { 1599 printf("ime\n"); 1600 } 1601 if (hasWallpaperService) { 1602 printf("wallpaper\n"); 1603 } 1604 if (hasAccessibilityService) { 1605 printf("accessibility\n"); 1606 } 1607 if (hasPrintService) { 1608 printf("print\n"); 1609 } 1610 if (hasPaymentService) { 1611 printf("payment\n"); 1612 } 1613 if (hasOtherActivities) { 1614 printf("other-activities\n"); 1615 } 1616 if (isSearchable) { 1617 printf("search\n"); 1618 } 1619 if (hasOtherReceivers) { 1620 printf("other-receivers\n"); 1621 } 1622 if (hasOtherServices) { 1623 printf("other-services\n"); 1624 } 1625 1626 // For modern apps, if screen size buckets haven't been specified 1627 // but the new width ranges have, then infer the buckets from them. 1628 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0 1629 && requiresSmallestWidthDp > 0) { 1630 int compatWidth = compatibleWidthLimitDp; 1631 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp; 1632 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) { 1633 smallScreen = -1; 1634 } else { 1635 smallScreen = 0; 1636 } 1637 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) { 1638 normalScreen = -1; 1639 } else { 1640 normalScreen = 0; 1641 } 1642 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) { 1643 largeScreen = -1; 1644 } else { 1645 largeScreen = 0; 1646 } 1647 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) { 1648 xlargeScreen = -1; 1649 } else { 1650 xlargeScreen = 0; 1651 } 1652 } 1653 1654 // Determine default values for any unspecified screen sizes, 1655 // based on the target SDK of the package. As of 4 (donut) 1656 // the screen size support was introduced, so all default to 1657 // enabled. 1658 if (smallScreen > 0) { 1659 smallScreen = targetSdk >= 4 ? -1 : 0; 1660 } 1661 if (normalScreen > 0) { 1662 normalScreen = -1; 1663 } 1664 if (largeScreen > 0) { 1665 largeScreen = targetSdk >= 4 ? -1 : 0; 1666 } 1667 if (xlargeScreen > 0) { 1668 // Introduced in Gingerbread. 1669 xlargeScreen = targetSdk >= 9 ? -1 : 0; 1670 } 1671 if (anyDensity > 0) { 1672 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0 1673 || compatibleWidthLimitDp > 0) ? -1 : 0; 1674 } 1675 printf("supports-screens:"); 1676 if (smallScreen != 0) printf(" 'small'"); 1677 if (normalScreen != 0) printf(" 'normal'"); 1678 if (largeScreen != 0) printf(" 'large'"); 1679 if (xlargeScreen != 0) printf(" 'xlarge'"); 1680 printf("\n"); 1681 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false"); 1682 if (requiresSmallestWidthDp > 0) { 1683 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp); 1684 } 1685 if (compatibleWidthLimitDp > 0) { 1686 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp); 1687 } 1688 if (largestWidthLimitDp > 0) { 1689 printf("largest-width-limit:'%d'\n", largestWidthLimitDp); 1690 } 1691 1692 printf("locales:"); 1693 const size_t NL = locales.size(); 1694 for (size_t i=0; i<NL; i++) { 1695 const char* localeStr = locales[i].string(); 1696 if (localeStr == NULL || strlen(localeStr) == 0) { 1697 localeStr = "--_--"; 1698 } 1699 printf(" '%s'", localeStr); 1700 } 1701 printf("\n"); 1702 1703 printf("densities:"); 1704 const size_t ND = densities.size(); 1705 for (size_t i=0; i<ND; i++) { 1706 printf(" '%d'", densities[i]); 1707 } 1708 printf("\n"); 1709 1710 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib"); 1711 if (dir != NULL) { 1712 if (dir->getFileCount() > 0) { 1713 printf("native-code:"); 1714 for (size_t i=0; i<dir->getFileCount(); i++) { 1715 printf(" '%s'", dir->getFileName(i).string()); 1716 } 1717 printf("\n"); 1718 } 1719 delete dir; 1720 } 1721 } else if (strcmp("badger", option) == 0) { 1722 printf("%s", CONSOLE_DATA); 1723 } else if (strcmp("configurations", option) == 0) { 1724 Vector<ResTable_config> configs; 1725 res.getConfigurations(&configs); 1726 const size_t N = configs.size(); 1727 for (size_t i=0; i<N; i++) { 1728 printf("%s\n", configs[i].toString().string()); 1729 } 1730 } else { 1731 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option); 1732 goto bail; 1733 } 1734 } 1735 1736 result = NO_ERROR; 1737 1738bail: 1739 if (asset) { 1740 delete asset; 1741 } 1742 return (result != NO_ERROR); 1743} 1744 1745 1746/* 1747 * Handle the "add" command, which wants to add files to a new or 1748 * pre-existing archive. 1749 */ 1750int doAdd(Bundle* bundle) 1751{ 1752 ZipFile* zip = NULL; 1753 status_t result = UNKNOWN_ERROR; 1754 const char* zipFileName; 1755 1756 if (bundle->getUpdate()) { 1757 /* avoid confusion */ 1758 fprintf(stderr, "ERROR: can't use '-u' with add\n"); 1759 goto bail; 1760 } 1761 1762 if (bundle->getFileSpecCount() < 1) { 1763 fprintf(stderr, "ERROR: must specify zip file name\n"); 1764 goto bail; 1765 } 1766 zipFileName = bundle->getFileSpecEntry(0); 1767 1768 if (bundle->getFileSpecCount() < 2) { 1769 fprintf(stderr, "NOTE: nothing to do\n"); 1770 goto bail; 1771 } 1772 1773 zip = openReadWrite(zipFileName, true); 1774 if (zip == NULL) { 1775 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName); 1776 goto bail; 1777 } 1778 1779 for (int i = 1; i < bundle->getFileSpecCount(); i++) { 1780 const char* fileName = bundle->getFileSpecEntry(i); 1781 1782 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) { 1783 printf(" '%s'... (from gzip)\n", fileName); 1784 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL); 1785 } else { 1786 if (bundle->getJunkPath()) { 1787 String8 storageName = String8(fileName).getPathLeaf(); 1788 printf(" '%s' as '%s'...\n", fileName, storageName.string()); 1789 result = zip->add(fileName, storageName.string(), 1790 bundle->getCompressionMethod(), NULL); 1791 } else { 1792 printf(" '%s'...\n", fileName); 1793 result = zip->add(fileName, bundle->getCompressionMethod(), NULL); 1794 } 1795 } 1796 if (result != NO_ERROR) { 1797 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName); 1798 if (result == NAME_NOT_FOUND) 1799 fprintf(stderr, ": file not found\n"); 1800 else if (result == ALREADY_EXISTS) 1801 fprintf(stderr, ": already exists in archive\n"); 1802 else 1803 fprintf(stderr, "\n"); 1804 goto bail; 1805 } 1806 } 1807 1808 result = NO_ERROR; 1809 1810bail: 1811 delete zip; 1812 return (result != NO_ERROR); 1813} 1814 1815 1816/* 1817 * Delete files from an existing archive. 1818 */ 1819int doRemove(Bundle* bundle) 1820{ 1821 ZipFile* zip = NULL; 1822 status_t result = UNKNOWN_ERROR; 1823 const char* zipFileName; 1824 1825 if (bundle->getFileSpecCount() < 1) { 1826 fprintf(stderr, "ERROR: must specify zip file name\n"); 1827 goto bail; 1828 } 1829 zipFileName = bundle->getFileSpecEntry(0); 1830 1831 if (bundle->getFileSpecCount() < 2) { 1832 fprintf(stderr, "NOTE: nothing to do\n"); 1833 goto bail; 1834 } 1835 1836 zip = openReadWrite(zipFileName, false); 1837 if (zip == NULL) { 1838 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n", 1839 zipFileName); 1840 goto bail; 1841 } 1842 1843 for (int i = 1; i < bundle->getFileSpecCount(); i++) { 1844 const char* fileName = bundle->getFileSpecEntry(i); 1845 ZipEntry* entry; 1846 1847 entry = zip->getEntryByName(fileName); 1848 if (entry == NULL) { 1849 printf(" '%s' NOT FOUND\n", fileName); 1850 continue; 1851 } 1852 1853 result = zip->remove(entry); 1854 1855 if (result != NO_ERROR) { 1856 fprintf(stderr, "Unable to delete '%s' from '%s'\n", 1857 bundle->getFileSpecEntry(i), zipFileName); 1858 goto bail; 1859 } 1860 } 1861 1862 /* update the archive */ 1863 zip->flush(); 1864 1865bail: 1866 delete zip; 1867 return (result != NO_ERROR); 1868} 1869 1870 1871/* 1872 * Package up an asset directory and associated application files. 1873 */ 1874int doPackage(Bundle* bundle) 1875{ 1876 const char* outputAPKFile; 1877 int retVal = 1; 1878 status_t err; 1879 sp<AaptAssets> assets; 1880 int N; 1881 FILE* fp; 1882 String8 dependencyFile; 1883 1884 // -c en_XA or/and ar_XB means do pseudolocalization 1885 ResourceFilter filter; 1886 err = filter.parse(bundle->getConfigurations()); 1887 if (err != NO_ERROR) { 1888 goto bail; 1889 } 1890 if (filter.containsPseudo()) { 1891 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED); 1892 } 1893 if (filter.containsPseudoBidi()) { 1894 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI); 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; 2055bail: 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 */ 2073int 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 */ 2089int 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 2105char 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