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