Command.cpp revision 25e9d55e964c180ec6e57ba1d977d6c2e1115f5a
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 } else if (res.getError() != NO_ERROR) { 638 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n"); 639 goto bail; 640 } 641 642 if (strcmp("resources", option) == 0) { 643#ifndef HAVE_ANDROID_OS 644 res.print(bundle->getValues()); 645#endif 646 647 } else if (strcmp("strings", option) == 0) { 648 const ResStringPool* pool = res.getTableStringBlock(0); 649 printStringPool(pool); 650 651 } else if (strcmp("xmltree", option) == 0) { 652 if (bundle->getFileSpecCount() < 3) { 653 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n"); 654 goto bail; 655 } 656 657 for (int i=2; i<bundle->getFileSpecCount(); i++) { 658 const char* resname = bundle->getFileSpecEntry(i); 659 ResXMLTree tree; 660 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER); 661 if (asset == NULL) { 662 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname); 663 goto bail; 664 } 665 666 if (tree.setTo(asset->getBuffer(true), 667 asset->getLength()) != NO_ERROR) { 668 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname); 669 goto bail; 670 } 671 tree.restart(); 672 printXMLBlock(&tree); 673 tree.uninit(); 674 delete asset; 675 asset = NULL; 676 } 677 678 } else if (strcmp("xmlstrings", option) == 0) { 679 if (bundle->getFileSpecCount() < 3) { 680 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n"); 681 goto bail; 682 } 683 684 for (int i=2; i<bundle->getFileSpecCount(); i++) { 685 const char* resname = bundle->getFileSpecEntry(i); 686 ResXMLTree tree; 687 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER); 688 if (asset == NULL) { 689 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname); 690 goto bail; 691 } 692 693 if (tree.setTo(asset->getBuffer(true), 694 asset->getLength()) != NO_ERROR) { 695 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname); 696 goto bail; 697 } 698 printStringPool(&tree.getStrings()); 699 delete asset; 700 asset = NULL; 701 } 702 703 } else { 704 ResXMLTree tree; 705 asset = assets.openNonAsset("AndroidManifest.xml", 706 Asset::ACCESS_BUFFER); 707 if (asset == NULL) { 708 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n"); 709 goto bail; 710 } 711 712 if (tree.setTo(asset->getBuffer(true), 713 asset->getLength()) != NO_ERROR) { 714 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n"); 715 goto bail; 716 } 717 tree.restart(); 718 719 if (strcmp("permissions", option) == 0) { 720 size_t len; 721 ResXMLTree::event_code_t code; 722 int depth = 0; 723 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 724 if (code == ResXMLTree::END_TAG) { 725 depth--; 726 continue; 727 } 728 if (code != ResXMLTree::START_TAG) { 729 continue; 730 } 731 depth++; 732 const char16_t* ctag16 = tree.getElementName(&len); 733 if (ctag16 == NULL) { 734 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n"); 735 goto bail; 736 } 737 String8 tag(ctag16); 738 //printf("Depth %d tag %s\n", depth, tag.string()); 739 if (depth == 1) { 740 if (tag != "manifest") { 741 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 742 goto bail; 743 } 744 String8 pkg = getAttribute(tree, NULL, "package", NULL); 745 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string()); 746 } else if (depth == 2 && tag == "permission") { 747 String8 error; 748 String8 name = getAttribute(tree, NAME_ATTR, &error); 749 if (error != "") { 750 fprintf(stderr, "ERROR: %s\n", error.string()); 751 goto bail; 752 } 753 printf("permission: %s\n", 754 ResTable::normalizeForOutput(name.string()).string()); 755 } else if (depth == 2 && tag == "uses-permission") { 756 String8 error; 757 String8 name = getAttribute(tree, NAME_ATTR, &error); 758 if (error != "") { 759 fprintf(stderr, "ERROR: %s\n", error.string()); 760 goto bail; 761 } 762 printUsesPermission(name, 763 getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0, 764 getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1)); 765 } 766 } 767 } else if (strcmp("badging", option) == 0) { 768 Vector<String8> locales; 769 res.getLocales(&locales); 770 771 Vector<ResTable_config> configs; 772 res.getConfigurations(&configs); 773 SortedVector<int> densities; 774 const size_t NC = configs.size(); 775 for (size_t i=0; i<NC; i++) { 776 int dens = configs[i].density; 777 if (dens == 0) { 778 dens = 160; 779 } 780 densities.add(dens); 781 } 782 783 size_t len; 784 ResXMLTree::event_code_t code; 785 int depth = 0; 786 String8 error; 787 bool withinActivity = false; 788 bool isMainActivity = false; 789 bool isLauncherActivity = false; 790 bool isLeanbackLauncherActivity = false; 791 bool isSearchable = false; 792 bool withinApplication = false; 793 bool withinSupportsInput = false; 794 bool withinReceiver = false; 795 bool withinService = false; 796 bool withinIntentFilter = false; 797 bool hasMainActivity = false; 798 bool hasOtherActivities = false; 799 bool hasOtherReceivers = false; 800 bool hasOtherServices = false; 801 bool hasWallpaperService = false; 802 bool hasImeService = false; 803 bool hasAccessibilityService = false; 804 bool hasPrintService = false; 805 bool hasWidgetReceivers = false; 806 bool hasDeviceAdminReceiver = false; 807 bool hasIntentFilter = false; 808 bool hasPaymentService = false; 809 bool actMainActivity = false; 810 bool actWidgetReceivers = false; 811 bool actDeviceAdminEnabled = false; 812 bool actImeService = false; 813 bool actWallpaperService = false; 814 bool actAccessibilityService = false; 815 bool actPrintService = false; 816 bool actHostApduService = false; 817 bool actOffHostApduService = false; 818 bool hasMetaHostPaymentCategory = false; 819 bool hasMetaOffHostPaymentCategory = false; 820 821 // These permissions are required by services implementing services 822 // the system binds to (IME, Accessibility, PrintServices, etc.) 823 bool hasBindDeviceAdminPermission = false; 824 bool hasBindInputMethodPermission = false; 825 bool hasBindAccessibilityServicePermission = false; 826 bool hasBindPrintServicePermission = false; 827 bool hasBindNfcServicePermission = false; 828 829 // These two implement the implicit permissions that are granted 830 // to pre-1.6 applications. 831 bool hasWriteExternalStoragePermission = false; 832 bool hasReadPhoneStatePermission = false; 833 834 // If an app requests write storage, they will also get read storage. 835 bool hasReadExternalStoragePermission = false; 836 837 // Implement transition to read and write call log. 838 bool hasReadContactsPermission = false; 839 bool hasWriteContactsPermission = false; 840 bool hasReadCallLogPermission = false; 841 bool hasWriteCallLogPermission = false; 842 843 // This next group of variables is used to implement a group of 844 // backward-compatibility heuristics necessitated by the addition of 845 // some new uses-feature constants in 2.1 and 2.2. In most cases, the 846 // heuristic is "if an app requests a permission but doesn't explicitly 847 // request the corresponding <uses-feature>, presume it's there anyway". 848 bool specCameraFeature = false; // camera-related 849 bool specCameraAutofocusFeature = false; 850 bool reqCameraAutofocusFeature = false; 851 bool reqCameraFlashFeature = false; 852 bool hasCameraPermission = false; 853 bool specLocationFeature = false; // location-related 854 bool specNetworkLocFeature = false; 855 bool reqNetworkLocFeature = false; 856 bool specGpsFeature = false; 857 bool reqGpsFeature = false; 858 bool hasMockLocPermission = false; 859 bool hasCoarseLocPermission = false; 860 bool hasGpsPermission = false; 861 bool hasGeneralLocPermission = false; 862 bool specBluetoothFeature = false; // Bluetooth API-related 863 bool hasBluetoothPermission = false; 864 bool specMicrophoneFeature = false; // microphone-related 865 bool hasRecordAudioPermission = false; 866 bool specWiFiFeature = false; 867 bool hasWiFiPermission = false; 868 bool specTelephonyFeature = false; // telephony-related 869 bool reqTelephonySubFeature = false; 870 bool hasTelephonyPermission = false; 871 bool specTouchscreenFeature = false; // touchscreen-related 872 bool specMultitouchFeature = false; 873 bool reqDistinctMultitouchFeature = false; 874 bool specScreenPortraitFeature = false; 875 bool specScreenLandscapeFeature = false; 876 bool reqScreenPortraitFeature = false; 877 bool reqScreenLandscapeFeature = false; 878 // 2.2 also added some other features that apps can request, but that 879 // have no corresponding permission, so we cannot implement any 880 // back-compatibility heuristic for them. The below are thus unnecessary 881 // (but are retained here for documentary purposes.) 882 //bool specCompassFeature = false; 883 //bool specAccelerometerFeature = false; 884 //bool specProximityFeature = false; 885 //bool specAmbientLightFeature = false; 886 //bool specLiveWallpaperFeature = false; 887 888 int targetSdk = 0; 889 int smallScreen = 1; 890 int normalScreen = 1; 891 int largeScreen = 1; 892 int xlargeScreen = 1; 893 int anyDensity = 1; 894 int requiresSmallestWidthDp = 0; 895 int compatibleWidthLimitDp = 0; 896 int largestWidthLimitDp = 0; 897 String8 pkg; 898 String8 activityName; 899 String8 activityLabel; 900 String8 activityIcon; 901 String8 activityBanner; 902 String8 receiverName; 903 String8 serviceName; 904 Vector<String8> supportedInput; 905 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 906 if (code == ResXMLTree::END_TAG) { 907 depth--; 908 if (depth < 2) { 909 if (withinSupportsInput && !supportedInput.isEmpty()) { 910 printf("supports-input: '"); 911 const size_t N = supportedInput.size(); 912 for (size_t i=0; i<N; i++) { 913 printf("%s", ResTable::normalizeForOutput( 914 supportedInput[i].string()).string()); 915 if (i != N - 1) { 916 printf("' '"); 917 } else { 918 printf("'\n"); 919 } 920 } 921 supportedInput.clear(); 922 } 923 withinApplication = false; 924 withinSupportsInput = false; 925 } else if (depth < 3) { 926 if (withinActivity && isMainActivity) { 927 String8 aName(getComponentName(pkg, activityName)); 928 if (isLauncherActivity) { 929 printf("launchable-activity:"); 930 if (aName.length() > 0) { 931 printf(" name='%s' ", 932 ResTable::normalizeForOutput(aName.string()).string()); 933 } 934 printf(" label='%s' icon='%s'\n", 935 ResTable::normalizeForOutput(activityLabel.string()).string(), 936 ResTable::normalizeForOutput(activityIcon.string()).string()); 937 } 938 if (isLeanbackLauncherActivity) { 939 printf("leanback-launchable-activity:"); 940 if (aName.length() > 0) { 941 printf(" name='%s' ", 942 ResTable::normalizeForOutput(aName.string()).string()); 943 } 944 printf(" label='%s' icon='%s' banner='%s'\n", 945 ResTable::normalizeForOutput(activityLabel.string()).string(), 946 ResTable::normalizeForOutput(activityIcon.string()).string(), 947 ResTable::normalizeForOutput(activityBanner.string()).string()); 948 } 949 } 950 if (!hasIntentFilter) { 951 hasOtherActivities |= withinActivity; 952 hasOtherReceivers |= withinReceiver; 953 hasOtherServices |= withinService; 954 } else { 955 if (withinService) { 956 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory && 957 hasBindNfcServicePermission); 958 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory && 959 hasBindNfcServicePermission); 960 } 961 } 962 withinActivity = false; 963 withinService = false; 964 withinReceiver = false; 965 hasIntentFilter = false; 966 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false; 967 } else if (depth < 4) { 968 if (withinIntentFilter) { 969 if (withinActivity) { 970 hasMainActivity |= actMainActivity; 971 hasOtherActivities |= !actMainActivity; 972 } else if (withinReceiver) { 973 hasWidgetReceivers |= actWidgetReceivers; 974 hasDeviceAdminReceiver |= (actDeviceAdminEnabled && 975 hasBindDeviceAdminPermission); 976 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled); 977 } else if (withinService) { 978 hasImeService |= actImeService; 979 hasWallpaperService |= actWallpaperService; 980 hasAccessibilityService |= (actAccessibilityService && 981 hasBindAccessibilityServicePermission); 982 hasPrintService |= (actPrintService && hasBindPrintServicePermission); 983 hasOtherServices |= (!actImeService && !actWallpaperService && 984 !actAccessibilityService && !actPrintService && 985 !actHostApduService && !actOffHostApduService); 986 } 987 } 988 withinIntentFilter = false; 989 } 990 continue; 991 } 992 if (code != ResXMLTree::START_TAG) { 993 continue; 994 } 995 depth++; 996 997 const char16_t* ctag16 = tree.getElementName(&len); 998 if (ctag16 == NULL) { 999 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n"); 1000 goto bail; 1001 } 1002 String8 tag(ctag16); 1003 //printf("Depth %d, %s\n", depth, tag.string()); 1004 if (depth == 1) { 1005 if (tag != "manifest") { 1006 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 1007 goto bail; 1008 } 1009 pkg = getAttribute(tree, NULL, "package", NULL); 1010 printf("package: name='%s' ", 1011 ResTable::normalizeForOutput(pkg.string()).string()); 1012 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error); 1013 if (error != "") { 1014 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string()); 1015 goto bail; 1016 } 1017 if (versionCode > 0) { 1018 printf("versionCode='%d' ", versionCode); 1019 } else { 1020 printf("versionCode='' "); 1021 } 1022 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error); 1023 if (error != "") { 1024 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string()); 1025 goto bail; 1026 } 1027 printf("versionName='%s'\n", 1028 ResTable::normalizeForOutput(versionName.string()).string()); 1029 } else if (depth == 2) { 1030 withinApplication = false; 1031 if (tag == "application") { 1032 withinApplication = true; 1033 1034 String8 label; 1035 const size_t NL = locales.size(); 1036 for (size_t i=0; i<NL; i++) { 1037 const char* localeStr = locales[i].string(); 1038 assets.setLocale(localeStr != NULL ? localeStr : ""); 1039 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); 1040 if (llabel != "") { 1041 if (localeStr == NULL || strlen(localeStr) == 0) { 1042 label = llabel; 1043 printf("application-label:'%s'\n", 1044 ResTable::normalizeForOutput(llabel.string()).string()); 1045 } else { 1046 if (label == "") { 1047 label = llabel; 1048 } 1049 printf("application-label-%s:'%s'\n", localeStr, 1050 ResTable::normalizeForOutput(llabel.string()).string()); 1051 } 1052 } 1053 } 1054 1055 ResTable_config tmpConfig = config; 1056 const size_t ND = densities.size(); 1057 for (size_t i=0; i<ND; i++) { 1058 tmpConfig.density = densities[i]; 1059 assets.setConfiguration(tmpConfig); 1060 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 1061 if (icon != "") { 1062 printf("application-icon-%d:'%s'\n", densities[i], 1063 ResTable::normalizeForOutput(icon.string()).string()); 1064 } 1065 } 1066 assets.setConfiguration(config); 1067 1068 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 1069 if (error != "") { 1070 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string()); 1071 goto bail; 1072 } 1073 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0); 1074 if (error != "") { 1075 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string()); 1076 goto bail; 1077 } 1078 printf("application: label='%s' ", 1079 ResTable::normalizeForOutput(label.string()).string()); 1080 printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string()); 1081 if (testOnly != 0) { 1082 printf("testOnly='%d'\n", testOnly); 1083 } 1084 1085 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0); 1086 if (error != "") { 1087 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string()); 1088 goto bail; 1089 } 1090 if (debuggable != 0) { 1091 printf("application-debuggable\n"); 1092 } 1093 } else if (tag == "uses-sdk") { 1094 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error); 1095 if (error != "") { 1096 error = ""; 1097 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error); 1098 if (error != "") { 1099 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n", 1100 error.string()); 1101 goto bail; 1102 } 1103 if (name == "Donut") targetSdk = 4; 1104 printf("sdkVersion:'%s'\n", 1105 ResTable::normalizeForOutput(name.string()).string()); 1106 } else if (code != -1) { 1107 targetSdk = code; 1108 printf("sdkVersion:'%d'\n", code); 1109 } 1110 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1); 1111 if (code != -1) { 1112 printf("maxSdkVersion:'%d'\n", code); 1113 } 1114 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error); 1115 if (error != "") { 1116 error = ""; 1117 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error); 1118 if (error != "") { 1119 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n", 1120 error.string()); 1121 goto bail; 1122 } 1123 if (name == "Donut" && targetSdk < 4) targetSdk = 4; 1124 printf("targetSdkVersion:'%s'\n", 1125 ResTable::normalizeForOutput(name.string()).string()); 1126 } else if (code != -1) { 1127 if (targetSdk < code) { 1128 targetSdk = code; 1129 } 1130 printf("targetSdkVersion:'%d'\n", code); 1131 } 1132 } else if (tag == "uses-configuration") { 1133 int32_t reqTouchScreen = getIntegerAttribute(tree, 1134 REQ_TOUCH_SCREEN_ATTR, NULL, 0); 1135 int32_t reqKeyboardType = getIntegerAttribute(tree, 1136 REQ_KEYBOARD_TYPE_ATTR, NULL, 0); 1137 int32_t reqHardKeyboard = getIntegerAttribute(tree, 1138 REQ_HARD_KEYBOARD_ATTR, NULL, 0); 1139 int32_t reqNavigation = getIntegerAttribute(tree, 1140 REQ_NAVIGATION_ATTR, NULL, 0); 1141 int32_t reqFiveWayNav = getIntegerAttribute(tree, 1142 REQ_FIVE_WAY_NAV_ATTR, NULL, 0); 1143 printf("uses-configuration:"); 1144 if (reqTouchScreen != 0) { 1145 printf(" reqTouchScreen='%d'", reqTouchScreen); 1146 } 1147 if (reqKeyboardType != 0) { 1148 printf(" reqKeyboardType='%d'", reqKeyboardType); 1149 } 1150 if (reqHardKeyboard != 0) { 1151 printf(" reqHardKeyboard='%d'", reqHardKeyboard); 1152 } 1153 if (reqNavigation != 0) { 1154 printf(" reqNavigation='%d'", reqNavigation); 1155 } 1156 if (reqFiveWayNav != 0) { 1157 printf(" reqFiveWayNav='%d'", reqFiveWayNav); 1158 } 1159 printf("\n"); 1160 } else if (tag == "supports-input") { 1161 withinSupportsInput = true; 1162 } else if (tag == "supports-screens") { 1163 smallScreen = getIntegerAttribute(tree, 1164 SMALL_SCREEN_ATTR, NULL, 1); 1165 normalScreen = getIntegerAttribute(tree, 1166 NORMAL_SCREEN_ATTR, NULL, 1); 1167 largeScreen = getIntegerAttribute(tree, 1168 LARGE_SCREEN_ATTR, NULL, 1); 1169 xlargeScreen = getIntegerAttribute(tree, 1170 XLARGE_SCREEN_ATTR, NULL, 1); 1171 anyDensity = getIntegerAttribute(tree, 1172 ANY_DENSITY_ATTR, NULL, 1); 1173 requiresSmallestWidthDp = getIntegerAttribute(tree, 1174 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0); 1175 compatibleWidthLimitDp = getIntegerAttribute(tree, 1176 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0); 1177 largestWidthLimitDp = getIntegerAttribute(tree, 1178 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0); 1179 } else if (tag == "uses-feature") { 1180 String8 name = getAttribute(tree, NAME_ATTR, &error); 1181 1182 if (name != "" && error == "") { 1183 int req = getIntegerAttribute(tree, 1184 REQUIRED_ATTR, NULL, 1); 1185 1186 if (name == "android.hardware.camera") { 1187 specCameraFeature = true; 1188 } else if (name == "android.hardware.camera.autofocus") { 1189 // these have no corresponding permission to check for, 1190 // but should imply the foundational camera permission 1191 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req; 1192 specCameraAutofocusFeature = true; 1193 } else if (req && (name == "android.hardware.camera.flash")) { 1194 // these have no corresponding permission to check for, 1195 // but should imply the foundational camera permission 1196 reqCameraFlashFeature = true; 1197 } else if (name == "android.hardware.location") { 1198 specLocationFeature = true; 1199 } else if (name == "android.hardware.location.network") { 1200 specNetworkLocFeature = true; 1201 reqNetworkLocFeature = reqNetworkLocFeature || req; 1202 } else if (name == "android.hardware.location.gps") { 1203 specGpsFeature = true; 1204 reqGpsFeature = reqGpsFeature || req; 1205 } else if (name == "android.hardware.bluetooth") { 1206 specBluetoothFeature = true; 1207 } else if (name == "android.hardware.touchscreen") { 1208 specTouchscreenFeature = true; 1209 } else if (name == "android.hardware.touchscreen.multitouch") { 1210 specMultitouchFeature = true; 1211 } else if (name == "android.hardware.touchscreen.multitouch.distinct") { 1212 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req; 1213 } else if (name == "android.hardware.microphone") { 1214 specMicrophoneFeature = true; 1215 } else if (name == "android.hardware.wifi") { 1216 specWiFiFeature = true; 1217 } else if (name == "android.hardware.telephony") { 1218 specTelephonyFeature = true; 1219 } else if (req && (name == "android.hardware.telephony.gsm" || 1220 name == "android.hardware.telephony.cdma")) { 1221 // these have no corresponding permission to check for, 1222 // but should imply the foundational telephony permission 1223 reqTelephonySubFeature = true; 1224 } else if (name == "android.hardware.screen.portrait") { 1225 specScreenPortraitFeature = true; 1226 } else if (name == "android.hardware.screen.landscape") { 1227 specScreenLandscapeFeature = true; 1228 } 1229 printf("uses-feature%s:'%s'\n", 1230 req ? "" : "-not-required", 1231 ResTable::normalizeForOutput(name.string()).string()); 1232 } else { 1233 int vers = getIntegerAttribute(tree, 1234 GL_ES_VERSION_ATTR, &error); 1235 if (error == "") { 1236 printf("uses-gl-es:'0x%x'\n", vers); 1237 } 1238 } 1239 } else if (tag == "uses-permission") { 1240 String8 name = getAttribute(tree, NAME_ATTR, &error); 1241 if (name != "" && error == "") { 1242 if (name == "android.permission.CAMERA") { 1243 hasCameraPermission = true; 1244 } else if (name == "android.permission.ACCESS_FINE_LOCATION") { 1245 hasGpsPermission = true; 1246 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") { 1247 hasMockLocPermission = true; 1248 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") { 1249 hasCoarseLocPermission = true; 1250 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" || 1251 name == "android.permission.INSTALL_LOCATION_PROVIDER") { 1252 hasGeneralLocPermission = true; 1253 } else if (name == "android.permission.BLUETOOTH" || 1254 name == "android.permission.BLUETOOTH_ADMIN") { 1255 hasBluetoothPermission = true; 1256 } else if (name == "android.permission.RECORD_AUDIO") { 1257 hasRecordAudioPermission = true; 1258 } else if (name == "android.permission.ACCESS_WIFI_STATE" || 1259 name == "android.permission.CHANGE_WIFI_STATE" || 1260 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") { 1261 hasWiFiPermission = true; 1262 } else if (name == "android.permission.CALL_PHONE" || 1263 name == "android.permission.CALL_PRIVILEGED" || 1264 name == "android.permission.MODIFY_PHONE_STATE" || 1265 name == "android.permission.PROCESS_OUTGOING_CALLS" || 1266 name == "android.permission.READ_SMS" || 1267 name == "android.permission.RECEIVE_SMS" || 1268 name == "android.permission.RECEIVE_MMS" || 1269 name == "android.permission.RECEIVE_WAP_PUSH" || 1270 name == "android.permission.SEND_SMS" || 1271 name == "android.permission.WRITE_APN_SETTINGS" || 1272 name == "android.permission.WRITE_SMS") { 1273 hasTelephonyPermission = true; 1274 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") { 1275 hasWriteExternalStoragePermission = true; 1276 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") { 1277 hasReadExternalStoragePermission = true; 1278 } else if (name == "android.permission.READ_PHONE_STATE") { 1279 hasReadPhoneStatePermission = true; 1280 } else if (name == "android.permission.READ_CONTACTS") { 1281 hasReadContactsPermission = true; 1282 } else if (name == "android.permission.WRITE_CONTACTS") { 1283 hasWriteContactsPermission = true; 1284 } else if (name == "android.permission.READ_CALL_LOG") { 1285 hasReadCallLogPermission = true; 1286 } else if (name == "android.permission.WRITE_CALL_LOG") { 1287 hasWriteCallLogPermission = true; 1288 } 1289 1290 printUsesPermission(name, 1291 getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0, 1292 getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1)); 1293 } else { 1294 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1295 error.string()); 1296 goto bail; 1297 } 1298 } else if (tag == "uses-package") { 1299 String8 name = getAttribute(tree, NAME_ATTR, &error); 1300 if (name != "" && error == "") { 1301 printf("uses-package:'%s'\n", 1302 ResTable::normalizeForOutput(name.string()).string()); 1303 } else { 1304 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1305 error.string()); 1306 goto bail; 1307 } 1308 } else if (tag == "original-package") { 1309 String8 name = getAttribute(tree, NAME_ATTR, &error); 1310 if (name != "" && error == "") { 1311 printf("original-package:'%s'\n", 1312 ResTable::normalizeForOutput(name.string()).string()); 1313 } else { 1314 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1315 error.string()); 1316 goto bail; 1317 } 1318 } else if (tag == "supports-gl-texture") { 1319 String8 name = getAttribute(tree, NAME_ATTR, &error); 1320 if (name != "" && error == "") { 1321 printf("supports-gl-texture:'%s'\n", 1322 ResTable::normalizeForOutput(name.string()).string()); 1323 } else { 1324 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1325 error.string()); 1326 goto bail; 1327 } 1328 } else if (tag == "compatible-screens") { 1329 printCompatibleScreens(tree, &error); 1330 if (error != "") { 1331 fprintf(stderr, "ERROR getting compatible screens: %s\n", 1332 error.string()); 1333 goto bail; 1334 } 1335 depth--; 1336 } else if (tag == "package-verifier") { 1337 String8 name = getAttribute(tree, NAME_ATTR, &error); 1338 if (name != "" && error == "") { 1339 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error); 1340 if (publicKey != "" && error == "") { 1341 printf("package-verifier: name='%s' publicKey='%s'\n", 1342 ResTable::normalizeForOutput(name.string()).string(), 1343 ResTable::normalizeForOutput(publicKey.string()).string()); 1344 } 1345 } 1346 } 1347 } else if (depth == 3) { 1348 withinActivity = false; 1349 withinReceiver = false; 1350 withinService = false; 1351 hasIntentFilter = false; 1352 hasMetaHostPaymentCategory = false; 1353 hasMetaOffHostPaymentCategory = false; 1354 hasBindDeviceAdminPermission = false; 1355 hasBindInputMethodPermission = false; 1356 hasBindAccessibilityServicePermission = false; 1357 hasBindPrintServicePermission = false; 1358 hasBindNfcServicePermission = false; 1359 if (withinApplication) { 1360 if(tag == "activity") { 1361 withinActivity = true; 1362 activityName = getAttribute(tree, NAME_ATTR, &error); 1363 if (error != "") { 1364 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1365 error.string()); 1366 goto bail; 1367 } 1368 1369 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); 1370 if (error != "") { 1371 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", 1372 error.string()); 1373 goto bail; 1374 } 1375 1376 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 1377 if (error != "") { 1378 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", 1379 error.string()); 1380 goto bail; 1381 } 1382 1383 activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error); 1384 if (error != "") { 1385 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n", 1386 error.string()); 1387 goto bail; 1388 } 1389 1390 int32_t orien = getResolvedIntegerAttribute(&res, tree, 1391 SCREEN_ORIENTATION_ATTR, &error); 1392 if (error == "") { 1393 if (orien == 0 || orien == 6 || orien == 8) { 1394 // Requests landscape, sensorLandscape, or reverseLandscape. 1395 reqScreenLandscapeFeature = true; 1396 } else if (orien == 1 || orien == 7 || orien == 9) { 1397 // Requests portrait, sensorPortrait, or reversePortrait. 1398 reqScreenPortraitFeature = true; 1399 } 1400 } 1401 } else if (tag == "uses-library") { 1402 String8 libraryName = getAttribute(tree, NAME_ATTR, &error); 1403 if (error != "") { 1404 fprintf(stderr, 1405 "ERROR getting 'android:name' attribute for uses-library" 1406 " %s\n", error.string()); 1407 goto bail; 1408 } 1409 int req = getIntegerAttribute(tree, 1410 REQUIRED_ATTR, NULL, 1); 1411 printf("uses-library%s:'%s'\n", 1412 req ? "" : "-not-required", ResTable::normalizeForOutput( 1413 libraryName.string()).string()); 1414 } else if (tag == "receiver") { 1415 withinReceiver = true; 1416 receiverName = getAttribute(tree, NAME_ATTR, &error); 1417 1418 if (error != "") { 1419 fprintf(stderr, 1420 "ERROR getting 'android:name' attribute for receiver:" 1421 " %s\n", error.string()); 1422 goto bail; 1423 } 1424 1425 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error); 1426 if (error == "") { 1427 if (permission == "android.permission.BIND_DEVICE_ADMIN") { 1428 hasBindDeviceAdminPermission = true; 1429 } 1430 } else { 1431 fprintf(stderr, "ERROR getting 'android:permission' attribute for" 1432 " receiver '%s': %s\n", receiverName.string(), error.string()); 1433 } 1434 } else if (tag == "service") { 1435 withinService = true; 1436 serviceName = getAttribute(tree, NAME_ATTR, &error); 1437 1438 if (error != "") { 1439 fprintf(stderr, "ERROR getting 'android:name' attribute for " 1440 "service:%s\n", error.string()); 1441 goto bail; 1442 } 1443 1444 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error); 1445 if (error == "") { 1446 if (permission == "android.permission.BIND_INPUT_METHOD") { 1447 hasBindInputMethodPermission = true; 1448 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") { 1449 hasBindAccessibilityServicePermission = true; 1450 } else if (permission == "android.permission.BIND_PRINT_SERVICE") { 1451 hasBindPrintServicePermission = true; 1452 } else if (permission == "android.permission.BIND_NFC_SERVICE") { 1453 hasBindNfcServicePermission = true; 1454 } 1455 } else { 1456 fprintf(stderr, "ERROR getting 'android:permission' attribute for" 1457 " service '%s': %s\n", serviceName.string(), error.string()); 1458 } 1459 } else if (bundle->getIncludeMetaData() && tag == "meta-data") { 1460 String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error); 1461 if (error != "") { 1462 fprintf(stderr, "ERROR getting 'android:name' attribute for " 1463 "meta-data:%s\n", error.string()); 1464 goto bail; 1465 } 1466 printf("meta-data: name='%s' ", 1467 ResTable::normalizeForOutput(metaDataName.string()).string()); 1468 printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"), 1469 &error); 1470 if (error != "") { 1471 // Try looking for a RESOURCE_ATTR 1472 error = ""; 1473 printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR, 1474 String8("resource"), &error); 1475 if (error != "") { 1476 fprintf(stderr, "ERROR getting 'android:value' or " 1477 "'android:resource' attribute for " 1478 "meta-data:%s\n", error.string()); 1479 goto bail; 1480 } 1481 } 1482 printf("\n"); 1483 } else if (withinSupportsInput && tag == "input-type") { 1484 String8 name = getAttribute(tree, NAME_ATTR, &error); 1485 if (name != "" && error == "") { 1486 supportedInput.add(name); 1487 } else { 1488 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1489 error.string()); 1490 goto bail; 1491 } 1492 } 1493 } 1494 } else if (depth == 4) { 1495 if (tag == "intent-filter") { 1496 hasIntentFilter = true; 1497 withinIntentFilter = true; 1498 actMainActivity = false; 1499 actWidgetReceivers = false; 1500 actImeService = false; 1501 actWallpaperService = false; 1502 actAccessibilityService = false; 1503 actPrintService = false; 1504 actDeviceAdminEnabled = false; 1505 actHostApduService = false; 1506 actOffHostApduService = false; 1507 } else if (withinService && tag == "meta-data") { 1508 String8 name = getAttribute(tree, NAME_ATTR, &error); 1509 if (error != "") { 1510 fprintf(stderr, "ERROR getting 'android:name' attribute for" 1511 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string()); 1512 goto bail; 1513 } 1514 1515 if (name == "android.nfc.cardemulation.host_apdu_service" || 1516 name == "android.nfc.cardemulation.off_host_apdu_service") { 1517 bool offHost = true; 1518 if (name == "android.nfc.cardemulation.host_apdu_service") { 1519 offHost = false; 1520 } 1521 1522 String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error); 1523 if (error != "") { 1524 fprintf(stderr, "ERROR getting 'android:resource' attribute for" 1525 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string()); 1526 goto bail; 1527 } 1528 1529 Vector<String8> categories = getNfcAidCategories(assets, xmlPath, 1530 offHost, &error); 1531 if (error != "") { 1532 fprintf(stderr, "ERROR getting AID category for service '%s'\n", 1533 serviceName.string()); 1534 goto bail; 1535 } 1536 1537 const size_t catLen = categories.size(); 1538 for (size_t i = 0; i < catLen; i++) { 1539 bool paymentCategory = (categories[i] == "payment"); 1540 if (offHost) { 1541 hasMetaOffHostPaymentCategory |= paymentCategory; 1542 } else { 1543 hasMetaHostPaymentCategory |= paymentCategory; 1544 } 1545 } 1546 } 1547 } 1548 } else if ((depth == 5) && withinIntentFilter) { 1549 String8 action; 1550 if (tag == "action") { 1551 action = getAttribute(tree, NAME_ATTR, &error); 1552 if (error != "") { 1553 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1554 error.string()); 1555 goto bail; 1556 } 1557 1558 if (withinActivity) { 1559 if (action == "android.intent.action.MAIN") { 1560 isMainActivity = true; 1561 actMainActivity = true; 1562 } 1563 } else if (withinReceiver) { 1564 if (action == "android.appwidget.action.APPWIDGET_UPDATE") { 1565 actWidgetReceivers = true; 1566 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") { 1567 actDeviceAdminEnabled = true; 1568 } 1569 } else if (withinService) { 1570 if (action == "android.view.InputMethod") { 1571 actImeService = true; 1572 } else if (action == "android.service.wallpaper.WallpaperService") { 1573 actWallpaperService = true; 1574 } else if (action == "android.accessibilityservice.AccessibilityService") { 1575 actAccessibilityService = true; 1576 } else if (action == "android.printservice.PrintService") { 1577 actPrintService = true; 1578 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") { 1579 actHostApduService = true; 1580 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") { 1581 actOffHostApduService = true; 1582 } 1583 } 1584 if (action == "android.intent.action.SEARCH") { 1585 isSearchable = true; 1586 } 1587 } 1588 1589 if (tag == "category") { 1590 String8 category = getAttribute(tree, NAME_ATTR, &error); 1591 if (error != "") { 1592 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", 1593 error.string()); 1594 goto bail; 1595 } 1596 if (withinActivity) { 1597 if (category == "android.intent.category.LAUNCHER") { 1598 isLauncherActivity = true; 1599 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") { 1600 isLeanbackLauncherActivity = true; 1601 } 1602 } 1603 } 1604 } 1605 } 1606 1607 // Pre-1.6 implicitly granted permission compatibility logic 1608 if (targetSdk < 4) { 1609 if (!hasWriteExternalStoragePermission) { 1610 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE")); 1611 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"), 1612 String8("targetSdkVersion < 4")); 1613 hasWriteExternalStoragePermission = true; 1614 } 1615 if (!hasReadPhoneStatePermission) { 1616 printUsesPermission(String8("android.permission.READ_PHONE_STATE")); 1617 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"), 1618 String8("targetSdkVersion < 4")); 1619 } 1620 } 1621 1622 // If the application has requested WRITE_EXTERNAL_STORAGE, we will 1623 // force them to always take READ_EXTERNAL_STORAGE as well. We always 1624 // do this (regardless of target API version) because we can't have 1625 // an app with write permission but not read permission. 1626 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) { 1627 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE")); 1628 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"), 1629 String8("requested WRITE_EXTERNAL_STORAGE")); 1630 } 1631 1632 // Pre-JellyBean call log permission compatibility. 1633 if (targetSdk < 16) { 1634 if (!hasReadCallLogPermission && hasReadContactsPermission) { 1635 printUsesPermission(String8("android.permission.READ_CALL_LOG")); 1636 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"), 1637 String8("targetSdkVersion < 16 and requested READ_CONTACTS")); 1638 } 1639 if (!hasWriteCallLogPermission && hasWriteContactsPermission) { 1640 printUsesPermission(String8("android.permission.WRITE_CALL_LOG")); 1641 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"), 1642 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS")); 1643 } 1644 } 1645 1646 /* The following blocks handle printing "inferred" uses-features, based 1647 * on whether related features or permissions are used by the app. 1648 * Note that the various spec*Feature variables denote whether the 1649 * relevant tag was *present* in the AndroidManfest, not that it was 1650 * present and set to true. 1651 */ 1652 // Camera-related back-compatibility logic 1653 if (!specCameraFeature) { 1654 if (reqCameraFlashFeature) { 1655 // if app requested a sub-feature (autofocus or flash) and didn't 1656 // request the base camera feature, we infer that it meant to 1657 printf("uses-feature:'android.hardware.camera'\n"); 1658 printf("uses-implied-feature:'android.hardware.camera'," \ 1659 "'requested android.hardware.camera.flash feature'\n"); 1660 } else if (reqCameraAutofocusFeature) { 1661 // if app requested a sub-feature (autofocus or flash) and didn't 1662 // request the base camera feature, we infer that it meant to 1663 printf("uses-feature:'android.hardware.camera'\n"); 1664 printf("uses-implied-feature:'android.hardware.camera'," \ 1665 "'requested android.hardware.camera.autofocus feature'\n"); 1666 } else if (hasCameraPermission) { 1667 // if app wants to use camera but didn't request the feature, we infer 1668 // that it meant to, and further that it wants autofocus 1669 // (which was the 1.0 - 1.5 behavior) 1670 printf("uses-feature:'android.hardware.camera'\n"); 1671 if (!specCameraAutofocusFeature) { 1672 printf("uses-feature:'android.hardware.camera.autofocus'\n"); 1673 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \ 1674 "'requested android.permission.CAMERA permission'\n"); 1675 } 1676 } 1677 } 1678 1679 // Location-related back-compatibility logic 1680 if (!specLocationFeature && 1681 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission || 1682 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) { 1683 // if app either takes a location-related permission or requests one of the 1684 // sub-features, we infer that it also meant to request the base location feature 1685 printf("uses-feature:'android.hardware.location'\n"); 1686 printf("uses-implied-feature:'android.hardware.location'," \ 1687 "'requested a location access permission'\n"); 1688 } 1689 if (!specGpsFeature && hasGpsPermission) { 1690 // if app takes GPS (FINE location) perm but does not request the GPS 1691 // feature, we infer that it meant to 1692 printf("uses-feature:'android.hardware.location.gps'\n"); 1693 printf("uses-implied-feature:'android.hardware.location.gps'," \ 1694 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n"); 1695 } 1696 if (!specNetworkLocFeature && hasCoarseLocPermission) { 1697 // if app takes Network location (COARSE location) perm but does not request the 1698 // network location feature, we infer that it meant to 1699 printf("uses-feature:'android.hardware.location.network'\n"); 1700 printf("uses-implied-feature:'android.hardware.location.network'," \ 1701 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n"); 1702 } 1703 1704 // Bluetooth-related compatibility logic 1705 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) { 1706 // if app takes a Bluetooth permission but does not request the Bluetooth 1707 // feature, we infer that it meant to 1708 printf("uses-feature:'android.hardware.bluetooth'\n"); 1709 printf("uses-implied-feature:'android.hardware.bluetooth'," \ 1710 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \ 1711 "permission and targetSdkVersion > 4'\n"); 1712 } 1713 1714 // Microphone-related compatibility logic 1715 if (!specMicrophoneFeature && hasRecordAudioPermission) { 1716 // if app takes the record-audio permission but does not request the microphone 1717 // feature, we infer that it meant to 1718 printf("uses-feature:'android.hardware.microphone'\n"); 1719 printf("uses-implied-feature:'android.hardware.microphone'," \ 1720 "'requested android.permission.RECORD_AUDIO permission'\n"); 1721 } 1722 1723 // WiFi-related compatibility logic 1724 if (!specWiFiFeature && hasWiFiPermission) { 1725 // if app takes one of the WiFi permissions but does not request the WiFi 1726 // feature, we infer that it meant to 1727 printf("uses-feature:'android.hardware.wifi'\n"); 1728 printf("uses-implied-feature:'android.hardware.wifi'," \ 1729 "'requested android.permission.ACCESS_WIFI_STATE, " \ 1730 "android.permission.CHANGE_WIFI_STATE, or " \ 1731 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n"); 1732 } 1733 1734 // Telephony-related compatibility logic 1735 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) { 1736 // if app takes one of the telephony permissions or requests a sub-feature but 1737 // does not request the base telephony feature, we infer that it meant to 1738 printf("uses-feature:'android.hardware.telephony'\n"); 1739 printf("uses-implied-feature:'android.hardware.telephony'," \ 1740 "'requested a telephony-related permission or feature'\n"); 1741 } 1742 1743 // Touchscreen-related back-compatibility logic 1744 if (!specTouchscreenFeature) { // not a typo! 1745 // all apps are presumed to require a touchscreen, unless they explicitly say 1746 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/> 1747 // Note that specTouchscreenFeature is true if the tag is present, regardless 1748 // of whether its value is true or false, so this is safe 1749 printf("uses-feature:'android.hardware.touchscreen'\n"); 1750 printf("uses-implied-feature:'android.hardware.touchscreen'," \ 1751 "'assumed you require a touch screen unless explicitly made optional'\n"); 1752 } 1753 if (!specMultitouchFeature && reqDistinctMultitouchFeature) { 1754 // if app takes one of the telephony permissions or requests a sub-feature but 1755 // does not request the base telephony feature, we infer that it meant to 1756 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n"); 1757 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \ 1758 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n"); 1759 } 1760 1761 // Landscape/portrait-related compatibility logic 1762 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) { 1763 // If the app has specified any activities in its manifest 1764 // that request a specific orientation, then assume that 1765 // orientation is required. 1766 if (reqScreenLandscapeFeature) { 1767 printf("uses-feature:'android.hardware.screen.landscape'\n"); 1768 printf("uses-implied-feature:'android.hardware.screen.landscape'," \ 1769 "'one or more activities have specified a landscape orientation'\n"); 1770 } 1771 if (reqScreenPortraitFeature) { 1772 printf("uses-feature:'android.hardware.screen.portrait'\n"); 1773 printf("uses-implied-feature:'android.hardware.screen.portrait'," \ 1774 "'one or more activities have specified a portrait orientation'\n"); 1775 } 1776 } 1777 1778 if (hasMainActivity) { 1779 printf("main\n"); 1780 } 1781 if (hasWidgetReceivers) { 1782 printf("app-widget\n"); 1783 } 1784 if (hasDeviceAdminReceiver) { 1785 printf("device-admin\n"); 1786 } 1787 if (hasImeService) { 1788 printf("ime\n"); 1789 } 1790 if (hasWallpaperService) { 1791 printf("wallpaper\n"); 1792 } 1793 if (hasAccessibilityService) { 1794 printf("accessibility\n"); 1795 } 1796 if (hasPrintService) { 1797 printf("print\n"); 1798 } 1799 if (hasPaymentService) { 1800 printf("payment\n"); 1801 } 1802 if (hasOtherActivities) { 1803 printf("other-activities\n"); 1804 } 1805 if (isSearchable) { 1806 printf("search\n"); 1807 } 1808 if (hasOtherReceivers) { 1809 printf("other-receivers\n"); 1810 } 1811 if (hasOtherServices) { 1812 printf("other-services\n"); 1813 } 1814 1815 // For modern apps, if screen size buckets haven't been specified 1816 // but the new width ranges have, then infer the buckets from them. 1817 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0 1818 && requiresSmallestWidthDp > 0) { 1819 int compatWidth = compatibleWidthLimitDp; 1820 if (compatWidth <= 0) { 1821 compatWidth = requiresSmallestWidthDp; 1822 } 1823 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) { 1824 smallScreen = -1; 1825 } else { 1826 smallScreen = 0; 1827 } 1828 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) { 1829 normalScreen = -1; 1830 } else { 1831 normalScreen = 0; 1832 } 1833 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) { 1834 largeScreen = -1; 1835 } else { 1836 largeScreen = 0; 1837 } 1838 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) { 1839 xlargeScreen = -1; 1840 } else { 1841 xlargeScreen = 0; 1842 } 1843 } 1844 1845 // Determine default values for any unspecified screen sizes, 1846 // based on the target SDK of the package. As of 4 (donut) 1847 // the screen size support was introduced, so all default to 1848 // enabled. 1849 if (smallScreen > 0) { 1850 smallScreen = targetSdk >= 4 ? -1 : 0; 1851 } 1852 if (normalScreen > 0) { 1853 normalScreen = -1; 1854 } 1855 if (largeScreen > 0) { 1856 largeScreen = targetSdk >= 4 ? -1 : 0; 1857 } 1858 if (xlargeScreen > 0) { 1859 // Introduced in Gingerbread. 1860 xlargeScreen = targetSdk >= 9 ? -1 : 0; 1861 } 1862 if (anyDensity > 0) { 1863 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0 1864 || compatibleWidthLimitDp > 0) ? -1 : 0; 1865 } 1866 printf("supports-screens:"); 1867 if (smallScreen != 0) { 1868 printf(" 'small'"); 1869 } 1870 if (normalScreen != 0) { 1871 printf(" 'normal'"); 1872 } 1873 if (largeScreen != 0) { 1874 printf(" 'large'"); 1875 } 1876 if (xlargeScreen != 0) { 1877 printf(" 'xlarge'"); 1878 } 1879 printf("\n"); 1880 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false"); 1881 if (requiresSmallestWidthDp > 0) { 1882 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp); 1883 } 1884 if (compatibleWidthLimitDp > 0) { 1885 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp); 1886 } 1887 if (largestWidthLimitDp > 0) { 1888 printf("largest-width-limit:'%d'\n", largestWidthLimitDp); 1889 } 1890 1891 printf("locales:"); 1892 const size_t NL = locales.size(); 1893 for (size_t i=0; i<NL; i++) { 1894 const char* localeStr = locales[i].string(); 1895 if (localeStr == NULL || strlen(localeStr) == 0) { 1896 localeStr = "--_--"; 1897 } 1898 printf(" '%s'", localeStr); 1899 } 1900 printf("\n"); 1901 1902 printf("densities:"); 1903 const size_t ND = densities.size(); 1904 for (size_t i=0; i<ND; i++) { 1905 printf(" '%d'", densities[i]); 1906 } 1907 printf("\n"); 1908 1909 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib"); 1910 if (dir != NULL) { 1911 if (dir->getFileCount() > 0) { 1912 printf("native-code:"); 1913 for (size_t i=0; i<dir->getFileCount(); i++) { 1914 printf(" '%s'", ResTable::normalizeForOutput( 1915 dir->getFileName(i).string()).string()); 1916 } 1917 printf("\n"); 1918 } 1919 delete dir; 1920 } 1921 } else if (strcmp("badger", option) == 0) { 1922 printf("%s", CONSOLE_DATA); 1923 } else if (strcmp("configurations", option) == 0) { 1924 Vector<ResTable_config> configs; 1925 res.getConfigurations(&configs); 1926 const size_t N = configs.size(); 1927 for (size_t i=0; i<N; i++) { 1928 printf("%s\n", configs[i].toString().string()); 1929 } 1930 } else { 1931 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option); 1932 goto bail; 1933 } 1934 } 1935 1936 result = NO_ERROR; 1937 1938bail: 1939 if (asset) { 1940 delete asset; 1941 } 1942 return (result != NO_ERROR); 1943} 1944 1945 1946/* 1947 * Handle the "add" command, which wants to add files to a new or 1948 * pre-existing archive. 1949 */ 1950int doAdd(Bundle* bundle) 1951{ 1952 ZipFile* zip = NULL; 1953 status_t result = UNKNOWN_ERROR; 1954 const char* zipFileName; 1955 1956 if (bundle->getUpdate()) { 1957 /* avoid confusion */ 1958 fprintf(stderr, "ERROR: can't use '-u' with add\n"); 1959 goto bail; 1960 } 1961 1962 if (bundle->getFileSpecCount() < 1) { 1963 fprintf(stderr, "ERROR: must specify zip file name\n"); 1964 goto bail; 1965 } 1966 zipFileName = bundle->getFileSpecEntry(0); 1967 1968 if (bundle->getFileSpecCount() < 2) { 1969 fprintf(stderr, "NOTE: nothing to do\n"); 1970 goto bail; 1971 } 1972 1973 zip = openReadWrite(zipFileName, true); 1974 if (zip == NULL) { 1975 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName); 1976 goto bail; 1977 } 1978 1979 for (int i = 1; i < bundle->getFileSpecCount(); i++) { 1980 const char* fileName = bundle->getFileSpecEntry(i); 1981 1982 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) { 1983 printf(" '%s'... (from gzip)\n", fileName); 1984 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL); 1985 } else { 1986 if (bundle->getJunkPath()) { 1987 String8 storageName = String8(fileName).getPathLeaf(); 1988 printf(" '%s' as '%s'...\n", fileName, 1989 ResTable::normalizeForOutput(storageName.string()).string()); 1990 result = zip->add(fileName, storageName.string(), 1991 bundle->getCompressionMethod(), NULL); 1992 } else { 1993 printf(" '%s'...\n", fileName); 1994 result = zip->add(fileName, bundle->getCompressionMethod(), NULL); 1995 } 1996 } 1997 if (result != NO_ERROR) { 1998 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName); 1999 if (result == NAME_NOT_FOUND) { 2000 fprintf(stderr, ": file not found\n"); 2001 } else if (result == ALREADY_EXISTS) { 2002 fprintf(stderr, ": already exists in archive\n"); 2003 } else { 2004 fprintf(stderr, "\n"); 2005 } 2006 goto bail; 2007 } 2008 } 2009 2010 result = NO_ERROR; 2011 2012bail: 2013 delete zip; 2014 return (result != NO_ERROR); 2015} 2016 2017 2018/* 2019 * Delete files from an existing archive. 2020 */ 2021int doRemove(Bundle* bundle) 2022{ 2023 ZipFile* zip = NULL; 2024 status_t result = UNKNOWN_ERROR; 2025 const char* zipFileName; 2026 2027 if (bundle->getFileSpecCount() < 1) { 2028 fprintf(stderr, "ERROR: must specify zip file name\n"); 2029 goto bail; 2030 } 2031 zipFileName = bundle->getFileSpecEntry(0); 2032 2033 if (bundle->getFileSpecCount() < 2) { 2034 fprintf(stderr, "NOTE: nothing to do\n"); 2035 goto bail; 2036 } 2037 2038 zip = openReadWrite(zipFileName, false); 2039 if (zip == NULL) { 2040 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n", 2041 zipFileName); 2042 goto bail; 2043 } 2044 2045 for (int i = 1; i < bundle->getFileSpecCount(); i++) { 2046 const char* fileName = bundle->getFileSpecEntry(i); 2047 ZipEntry* entry; 2048 2049 entry = zip->getEntryByName(fileName); 2050 if (entry == NULL) { 2051 printf(" '%s' NOT FOUND\n", fileName); 2052 continue; 2053 } 2054 2055 result = zip->remove(entry); 2056 2057 if (result != NO_ERROR) { 2058 fprintf(stderr, "Unable to delete '%s' from '%s'\n", 2059 bundle->getFileSpecEntry(i), zipFileName); 2060 goto bail; 2061 } 2062 } 2063 2064 /* update the archive */ 2065 zip->flush(); 2066 2067bail: 2068 delete zip; 2069 return (result != NO_ERROR); 2070} 2071 2072static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) { 2073 const size_t numDirs = dir->getDirs().size(); 2074 for (size_t i = 0; i < numDirs; i++) { 2075 bool ignore = ignoreConfig; 2076 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i); 2077 const char* dirStr = subDir->getLeaf().string(); 2078 if (!ignore && strstr(dirStr, "mipmap") == dirStr) { 2079 ignore = true; 2080 } 2081 status_t err = addResourcesToBuilder(subDir, builder, ignore); 2082 if (err != NO_ERROR) { 2083 return err; 2084 } 2085 } 2086 2087 const size_t numFiles = dir->getFiles().size(); 2088 for (size_t i = 0; i < numFiles; i++) { 2089 sp<AaptGroup> gp = dir->getFiles().valueAt(i); 2090 const size_t numConfigs = gp->getFiles().size(); 2091 for (size_t j = 0; j < numConfigs; j++) { 2092 status_t err = NO_ERROR; 2093 if (ignoreConfig) { 2094 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j)); 2095 } else { 2096 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j)); 2097 } 2098 if (err != NO_ERROR) { 2099 fprintf(stderr, "Failed to add %s (%s) to builder.\n", 2100 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string()); 2101 return err; 2102 } 2103 } 2104 } 2105 return NO_ERROR; 2106} 2107 2108static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) { 2109 if (split->isBase()) { 2110 return original; 2111 } 2112 2113 String8 ext(original.getPathExtension()); 2114 if (ext == String8(".apk")) { 2115 return String8::format("%s_%s%s", 2116 original.getBasePath().string(), 2117 split->getDirectorySafeName().string(), 2118 ext.string()); 2119 } 2120 2121 return String8::format("%s_%s", original.string(), 2122 split->getDirectorySafeName().string()); 2123} 2124 2125/* 2126 * Package up an asset directory and associated application files. 2127 */ 2128int doPackage(Bundle* bundle) 2129{ 2130 const char* outputAPKFile; 2131 int retVal = 1; 2132 status_t err; 2133 sp<AaptAssets> assets; 2134 int N; 2135 FILE* fp; 2136 String8 dependencyFile; 2137 sp<ApkBuilder> builder; 2138 2139 // -c en_XA or/and ar_XB means do pseudolocalization 2140 sp<WeakResourceFilter> configFilter = new WeakResourceFilter(); 2141 err = configFilter->parse(bundle->getConfigurations()); 2142 if (err != NO_ERROR) { 2143 goto bail; 2144 } 2145 if (configFilter->containsPseudo()) { 2146 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED); 2147 } 2148 if (configFilter->containsPseudoBidi()) { 2149 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI); 2150 } 2151 2152 N = bundle->getFileSpecCount(); 2153 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0 2154 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) { 2155 fprintf(stderr, "ERROR: no input files\n"); 2156 goto bail; 2157 } 2158 2159 outputAPKFile = bundle->getOutputAPKFile(); 2160 2161 // Make sure the filenames provided exist and are of the appropriate type. 2162 if (outputAPKFile) { 2163 FileType type; 2164 type = getFileType(outputAPKFile); 2165 if (type != kFileTypeNonexistent && type != kFileTypeRegular) { 2166 fprintf(stderr, 2167 "ERROR: output file '%s' exists but is not regular file\n", 2168 outputAPKFile); 2169 goto bail; 2170 } 2171 } 2172 2173 // Load the assets. 2174 assets = new AaptAssets(); 2175 2176 // Set up the resource gathering in assets if we're going to generate 2177 // dependency files. Every time we encounter a resource while slurping 2178 // the tree, we'll add it to these stores so we have full resource paths 2179 // to write to a dependency file. 2180 if (bundle->getGenDependencies()) { 2181 sp<FilePathStore> resPathStore = new FilePathStore; 2182 assets->setFullResPaths(resPathStore); 2183 sp<FilePathStore> assetPathStore = new FilePathStore; 2184 assets->setFullAssetPaths(assetPathStore); 2185 } 2186 2187 err = assets->slurpFromArgs(bundle); 2188 if (err < 0) { 2189 goto bail; 2190 } 2191 2192 if (bundle->getVerbose()) { 2193 assets->print(String8()); 2194 } 2195 2196 // Create the ApkBuilder, which will collect the compiled files 2197 // to write to the final APK (or sets of APKs if we are building 2198 // a Split APK. 2199 builder = new ApkBuilder(configFilter); 2200 2201 // If we are generating a Split APK, find out which configurations to split on. 2202 if (bundle->getSplitConfigurations().size() > 0) { 2203 const Vector<String8>& splitStrs = bundle->getSplitConfigurations(); 2204 const size_t numSplits = splitStrs.size(); 2205 for (size_t i = 0; i < numSplits; i++) { 2206 std::set<ConfigDescription> configs; 2207 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) { 2208 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string()); 2209 goto bail; 2210 } 2211 2212 err = builder->createSplitForConfigs(configs); 2213 if (err != NO_ERROR) { 2214 goto bail; 2215 } 2216 } 2217 } 2218 2219 // If they asked for any fileAs that need to be compiled, do so. 2220 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) { 2221 err = buildResources(bundle, assets, builder); 2222 if (err != 0) { 2223 goto bail; 2224 } 2225 } 2226 2227 // At this point we've read everything and processed everything. From here 2228 // on out it's just writing output files. 2229 if (SourcePos::hasErrors()) { 2230 goto bail; 2231 } 2232 2233 // Update symbols with information about which ones are needed as Java symbols. 2234 assets->applyJavaSymbols(); 2235 if (SourcePos::hasErrors()) { 2236 goto bail; 2237 } 2238 2239 // If we've been asked to generate a dependency file, do that here 2240 if (bundle->getGenDependencies()) { 2241 // If this is the packaging step, generate the dependency file next to 2242 // the output apk (e.g. bin/resources.ap_.d) 2243 if (outputAPKFile) { 2244 dependencyFile = String8(outputAPKFile); 2245 // Add the .d extension to the dependency file. 2246 dependencyFile.append(".d"); 2247 } else { 2248 // Else if this is the R.java dependency generation step, 2249 // generate the dependency file in the R.java package subdirectory 2250 // e.g. gen/com/foo/app/R.java.d 2251 dependencyFile = String8(bundle->getRClassDir()); 2252 dependencyFile.appendPath("R.java.d"); 2253 } 2254 // Make sure we have a clean dependency file to start with 2255 fp = fopen(dependencyFile, "w"); 2256 fclose(fp); 2257 } 2258 2259 // Write out R.java constants 2260 if (!assets->havePrivateSymbols()) { 2261 if (bundle->getCustomPackage() == NULL) { 2262 // Write the R.java file into the appropriate class directory 2263 // e.g. gen/com/foo/app/R.java 2264 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true); 2265 } else { 2266 const String8 customPkg(bundle->getCustomPackage()); 2267 err = writeResourceSymbols(bundle, assets, customPkg, true); 2268 } 2269 if (err < 0) { 2270 goto bail; 2271 } 2272 // If we have library files, we're going to write our R.java file into 2273 // the appropriate class directory for those libraries as well. 2274 // e.g. gen/com/foo/app/lib/R.java 2275 if (bundle->getExtraPackages() != NULL) { 2276 // Split on colon 2277 String8 libs(bundle->getExtraPackages()); 2278 char* packageString = strtok(libs.lockBuffer(libs.length()), ":"); 2279 while (packageString != NULL) { 2280 // Write the R.java file out with the correct package name 2281 err = writeResourceSymbols(bundle, assets, String8(packageString), true); 2282 if (err < 0) { 2283 goto bail; 2284 } 2285 packageString = strtok(NULL, ":"); 2286 } 2287 libs.unlockBuffer(); 2288 } 2289 } else { 2290 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false); 2291 if (err < 0) { 2292 goto bail; 2293 } 2294 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true); 2295 if (err < 0) { 2296 goto bail; 2297 } 2298 } 2299 2300 // Write out the ProGuard file 2301 err = writeProguardFile(bundle, assets); 2302 if (err < 0) { 2303 goto bail; 2304 } 2305 2306 // Write the apk 2307 if (outputAPKFile) { 2308 // Gather all resources and add them to the APK Builder. The builder will then 2309 // figure out which Split they belong in. 2310 err = addResourcesToBuilder(assets, builder); 2311 if (err != NO_ERROR) { 2312 goto bail; 2313 } 2314 2315 const Vector<sp<ApkSplit> >& splits = builder->getSplits(); 2316 const size_t numSplits = splits.size(); 2317 for (size_t i = 0; i < numSplits; i++) { 2318 const sp<ApkSplit>& split = splits[i]; 2319 String8 outputPath = buildApkName(String8(outputAPKFile), split); 2320 err = writeAPK(bundle, outputPath, split); 2321 if (err != NO_ERROR) { 2322 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string()); 2323 goto bail; 2324 } 2325 } 2326 } 2327 2328 // If we've been asked to generate a dependency file, we need to finish up here. 2329 // the writeResourceSymbols and writeAPK functions have already written the target 2330 // half of the dependency file, now we need to write the prerequisites. (files that 2331 // the R.java file or .ap_ file depend on) 2332 if (bundle->getGenDependencies()) { 2333 // Now that writeResourceSymbols or writeAPK has taken care of writing 2334 // the targets to our dependency file, we'll write the prereqs 2335 fp = fopen(dependencyFile, "a+"); 2336 fprintf(fp, " : "); 2337 bool includeRaw = (outputAPKFile != NULL); 2338 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw); 2339 // Also manually add the AndroidManifeset since it's not under res/ or assets/ 2340 // and therefore was not added to our pathstores during slurping 2341 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile()); 2342 fclose(fp); 2343 } 2344 2345 retVal = 0; 2346bail: 2347 if (SourcePos::hasErrors()) { 2348 SourcePos::printErrors(stderr); 2349 } 2350 return retVal; 2351} 2352 2353/* 2354 * Do PNG Crunching 2355 * PRECONDITIONS 2356 * -S flag points to a source directory containing drawable* folders 2357 * -C flag points to destination directory. The folder structure in the 2358 * source directory will be mirrored to the destination (cache) directory 2359 * 2360 * POSTCONDITIONS 2361 * Destination directory will be updated to match the PNG files in 2362 * the source directory. 2363 */ 2364int doCrunch(Bundle* bundle) 2365{ 2366 fprintf(stdout, "Crunching PNG Files in "); 2367 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]); 2368 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir()); 2369 2370 updatePreProcessedCache(bundle); 2371 2372 return NO_ERROR; 2373} 2374 2375/* 2376 * Do PNG Crunching on a single flag 2377 * -i points to a single png file 2378 * -o points to a single png output file 2379 */ 2380int doSingleCrunch(Bundle* bundle) 2381{ 2382 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile()); 2383 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile()); 2384 2385 String8 input(bundle->getSingleCrunchInputFile()); 2386 String8 output(bundle->getSingleCrunchOutputFile()); 2387 2388 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) { 2389 // we can't return the status_t as it gets truncate to the lower 8 bits. 2390 return 42; 2391 } 2392 2393 return NO_ERROR; 2394} 2395 2396char CONSOLE_DATA[2925] = { 2397 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2398 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2399 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2400 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 2401 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63, 2402 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83, 2403 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2404 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2405 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81, 2406 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32, 2407 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 2408 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2409 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59, 2410 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2411 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2412 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 2413 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32, 2414 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2415 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2416 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87, 2417 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32, 2418 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 2419 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 2420 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58, 2421 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2422 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2423 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81, 2424 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32, 2425 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 2426 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2427 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59, 2428 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2429 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2430 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59, 2431 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32, 2432 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2433 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2434 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81, 2435 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 2436 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 2437 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2438 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81, 2439 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2440 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2441 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109, 2442 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32, 2443 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 2444 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59, 2445 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59, 2446 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2447 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59, 2448 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46, 2449 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32, 2450 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2451 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 2452 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81, 2453 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32, 2454 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81, 2455 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2456 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58, 2457 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32, 2458 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59, 2459 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37, 2460 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59, 2461 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 2462 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59, 2463 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96, 2464 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 2465 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32, 2466 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61, 2467 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59, 2468 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32, 2469 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2470 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119, 2471 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2472 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10, 2473 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2474 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81, 2475 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41, 2476 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 2477 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81, 2478 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 2479 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32, 2480 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2481 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 2482 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 2483 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 2484 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 2485 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 2486 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2487 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2488 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81, 2489 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32, 2490 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2491 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2492 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2493 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2494 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 2495 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106, 2496 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59, 2497 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2498 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2499 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81, 2500 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 2501 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 2502 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2503 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59, 2504 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2505 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2506 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81, 2507 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32, 2508 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2509 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2510 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2511 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2512 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 2513 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 2514 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59, 2515 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2516 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2517 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33, 2518 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 2519 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 2520 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 2521 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59, 2522 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 2523 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 2524 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95, 2525 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59, 2526 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2527 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2528 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45, 2529 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32, 2530 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 2531 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59, 2532 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59, 2533 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2534 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2535 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32, 2536 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32, 2537 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 2538 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2539 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61, 2540 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2541 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 2542 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 2543 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59, 2544 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2545 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2546 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32, 2547 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32, 2548 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 2549 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61, 2550 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 2551 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2552 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2553 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32, 2554 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32, 2555 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2556 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2557 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2558 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2559 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10 2560 }; 2561