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