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