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