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