Command.cpp revision 3921e87e6e67a063caa1987937d7944b24aef489
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, bool ignoreConfig=false) { 2039 const size_t numDirs = dir->getDirs().size(); 2040 for (size_t i = 0; i < numDirs; i++) { 2041 bool ignore = ignoreConfig; 2042 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i); 2043 const char* dirStr = subDir->getLeaf().string(); 2044 if (!ignore && strstr(dirStr, "mipmap") == dirStr) { 2045 ignore = true; 2046 } 2047 status_t err = addResourcesToBuilder(subDir, builder, ignore); 2048 if (err != NO_ERROR) { 2049 return err; 2050 } 2051 } 2052 2053 const size_t numFiles = dir->getFiles().size(); 2054 for (size_t i = 0; i < numFiles; i++) { 2055 sp<AaptGroup> gp = dir->getFiles().valueAt(i); 2056 const size_t numConfigs = gp->getFiles().size(); 2057 for (size_t j = 0; j < numConfigs; j++) { 2058 status_t err = NO_ERROR; 2059 if (ignoreConfig) { 2060 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j)); 2061 } else { 2062 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j)); 2063 } 2064 if (err != NO_ERROR) { 2065 fprintf(stderr, "Failed to add %s (%s) to builder.\n", 2066 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string()); 2067 return err; 2068 } 2069 } 2070 } 2071 return NO_ERROR; 2072} 2073 2074static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) { 2075 if (split->isBase()) { 2076 return original; 2077 } 2078 2079 String8 ext(original.getPathExtension()); 2080 if (ext == String8(".apk")) { 2081 return String8::format("%s_%s%s", 2082 original.getBasePath().string(), 2083 split->getDirectorySafeName().string(), 2084 ext.string()); 2085 } 2086 2087 return String8::format("%s_%s", original.string(), 2088 split->getDirectorySafeName().string()); 2089} 2090 2091/* 2092 * Package up an asset directory and associated application files. 2093 */ 2094int doPackage(Bundle* bundle) 2095{ 2096 const char* outputAPKFile; 2097 int retVal = 1; 2098 status_t err; 2099 sp<AaptAssets> assets; 2100 int N; 2101 FILE* fp; 2102 String8 dependencyFile; 2103 sp<ApkBuilder> builder; 2104 2105 // -c en_XA or/and ar_XB means do pseudolocalization 2106 sp<WeakResourceFilter> configFilter = new WeakResourceFilter(); 2107 err = configFilter->parse(bundle->getConfigurations()); 2108 if (err != NO_ERROR) { 2109 goto bail; 2110 } 2111 if (configFilter->containsPseudo()) { 2112 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED); 2113 } 2114 if (configFilter->containsPseudoBidi()) { 2115 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI); 2116 } 2117 2118 N = bundle->getFileSpecCount(); 2119 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0 2120 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) { 2121 fprintf(stderr, "ERROR: no input files\n"); 2122 goto bail; 2123 } 2124 2125 outputAPKFile = bundle->getOutputAPKFile(); 2126 2127 // Make sure the filenames provided exist and are of the appropriate type. 2128 if (outputAPKFile) { 2129 FileType type; 2130 type = getFileType(outputAPKFile); 2131 if (type != kFileTypeNonexistent && type != kFileTypeRegular) { 2132 fprintf(stderr, 2133 "ERROR: output file '%s' exists but is not regular file\n", 2134 outputAPKFile); 2135 goto bail; 2136 } 2137 } 2138 2139 // Load the assets. 2140 assets = new AaptAssets(); 2141 2142 // Set up the resource gathering in assets if we're going to generate 2143 // dependency files. Every time we encounter a resource while slurping 2144 // the tree, we'll add it to these stores so we have full resource paths 2145 // to write to a dependency file. 2146 if (bundle->getGenDependencies()) { 2147 sp<FilePathStore> resPathStore = new FilePathStore; 2148 assets->setFullResPaths(resPathStore); 2149 sp<FilePathStore> assetPathStore = new FilePathStore; 2150 assets->setFullAssetPaths(assetPathStore); 2151 } 2152 2153 err = assets->slurpFromArgs(bundle); 2154 if (err < 0) { 2155 goto bail; 2156 } 2157 2158 if (bundle->getVerbose()) { 2159 assets->print(String8()); 2160 } 2161 2162 // Create the ApkBuilder, which will collect the compiled files 2163 // to write to the final APK (or sets of APKs if we are building 2164 // a Split APK. 2165 builder = new ApkBuilder(configFilter); 2166 2167 // If we are generating a Split APK, find out which configurations to split on. 2168 if (bundle->getSplitConfigurations().size() > 0) { 2169 const Vector<String8>& splitStrs = bundle->getSplitConfigurations(); 2170 const size_t numSplits = splitStrs.size(); 2171 for (size_t i = 0; i < numSplits; i++) { 2172 std::set<ConfigDescription> configs; 2173 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) { 2174 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string()); 2175 goto bail; 2176 } 2177 2178 err = builder->createSplitForConfigs(configs); 2179 if (err != NO_ERROR) { 2180 goto bail; 2181 } 2182 } 2183 } 2184 2185 // If they asked for any fileAs that need to be compiled, do so. 2186 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) { 2187 err = buildResources(bundle, assets, builder); 2188 if (err != 0) { 2189 goto bail; 2190 } 2191 } 2192 2193 // At this point we've read everything and processed everything. From here 2194 // on out it's just writing output files. 2195 if (SourcePos::hasErrors()) { 2196 goto bail; 2197 } 2198 2199 // Update symbols with information about which ones are needed as Java symbols. 2200 assets->applyJavaSymbols(); 2201 if (SourcePos::hasErrors()) { 2202 goto bail; 2203 } 2204 2205 // If we've been asked to generate a dependency file, do that here 2206 if (bundle->getGenDependencies()) { 2207 // If this is the packaging step, generate the dependency file next to 2208 // the output apk (e.g. bin/resources.ap_.d) 2209 if (outputAPKFile) { 2210 dependencyFile = String8(outputAPKFile); 2211 // Add the .d extension to the dependency file. 2212 dependencyFile.append(".d"); 2213 } else { 2214 // Else if this is the R.java dependency generation step, 2215 // generate the dependency file in the R.java package subdirectory 2216 // e.g. gen/com/foo/app/R.java.d 2217 dependencyFile = String8(bundle->getRClassDir()); 2218 dependencyFile.appendPath("R.java.d"); 2219 } 2220 // Make sure we have a clean dependency file to start with 2221 fp = fopen(dependencyFile, "w"); 2222 fclose(fp); 2223 } 2224 2225 // Write out R.java constants 2226 if (!assets->havePrivateSymbols()) { 2227 if (bundle->getCustomPackage() == NULL) { 2228 // Write the R.java file into the appropriate class directory 2229 // e.g. gen/com/foo/app/R.java 2230 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true); 2231 } else { 2232 const String8 customPkg(bundle->getCustomPackage()); 2233 err = writeResourceSymbols(bundle, assets, customPkg, true); 2234 } 2235 if (err < 0) { 2236 goto bail; 2237 } 2238 // If we have library files, we're going to write our R.java file into 2239 // the appropriate class directory for those libraries as well. 2240 // e.g. gen/com/foo/app/lib/R.java 2241 if (bundle->getExtraPackages() != NULL) { 2242 // Split on colon 2243 String8 libs(bundle->getExtraPackages()); 2244 char* packageString = strtok(libs.lockBuffer(libs.length()), ":"); 2245 while (packageString != NULL) { 2246 // Write the R.java file out with the correct package name 2247 err = writeResourceSymbols(bundle, assets, String8(packageString), true); 2248 if (err < 0) { 2249 goto bail; 2250 } 2251 packageString = strtok(NULL, ":"); 2252 } 2253 libs.unlockBuffer(); 2254 } 2255 } else { 2256 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false); 2257 if (err < 0) { 2258 goto bail; 2259 } 2260 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true); 2261 if (err < 0) { 2262 goto bail; 2263 } 2264 } 2265 2266 // Write out the ProGuard file 2267 err = writeProguardFile(bundle, assets); 2268 if (err < 0) { 2269 goto bail; 2270 } 2271 2272 // Write the apk 2273 if (outputAPKFile) { 2274 // Gather all resources and add them to the APK Builder. The builder will then 2275 // figure out which Split they belong in. 2276 err = addResourcesToBuilder(assets, builder); 2277 if (err != NO_ERROR) { 2278 goto bail; 2279 } 2280 2281 const Vector<sp<ApkSplit> >& splits = builder->getSplits(); 2282 const size_t numSplits = splits.size(); 2283 for (size_t i = 0; i < numSplits; i++) { 2284 const sp<ApkSplit>& split = splits[i]; 2285 String8 outputPath = buildApkName(String8(outputAPKFile), split); 2286 err = writeAPK(bundle, outputPath, split); 2287 if (err != NO_ERROR) { 2288 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string()); 2289 goto bail; 2290 } 2291 } 2292 } 2293 2294 // If we've been asked to generate a dependency file, we need to finish up here. 2295 // the writeResourceSymbols and writeAPK functions have already written the target 2296 // half of the dependency file, now we need to write the prerequisites. (files that 2297 // the R.java file or .ap_ file depend on) 2298 if (bundle->getGenDependencies()) { 2299 // Now that writeResourceSymbols or writeAPK has taken care of writing 2300 // the targets to our dependency file, we'll write the prereqs 2301 fp = fopen(dependencyFile, "a+"); 2302 fprintf(fp, " : "); 2303 bool includeRaw = (outputAPKFile != NULL); 2304 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw); 2305 // Also manually add the AndroidManifeset since it's not under res/ or assets/ 2306 // and therefore was not added to our pathstores during slurping 2307 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile()); 2308 fclose(fp); 2309 } 2310 2311 retVal = 0; 2312bail: 2313 if (SourcePos::hasErrors()) { 2314 SourcePos::printErrors(stderr); 2315 } 2316 return retVal; 2317} 2318 2319/* 2320 * Do PNG Crunching 2321 * PRECONDITIONS 2322 * -S flag points to a source directory containing drawable* folders 2323 * -C flag points to destination directory. The folder structure in the 2324 * source directory will be mirrored to the destination (cache) directory 2325 * 2326 * POSTCONDITIONS 2327 * Destination directory will be updated to match the PNG files in 2328 * the source directory. 2329 */ 2330int doCrunch(Bundle* bundle) 2331{ 2332 fprintf(stdout, "Crunching PNG Files in "); 2333 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]); 2334 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir()); 2335 2336 updatePreProcessedCache(bundle); 2337 2338 return NO_ERROR; 2339} 2340 2341/* 2342 * Do PNG Crunching on a single flag 2343 * -i points to a single png file 2344 * -o points to a single png output file 2345 */ 2346int doSingleCrunch(Bundle* bundle) 2347{ 2348 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile()); 2349 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile()); 2350 2351 String8 input(bundle->getSingleCrunchInputFile()); 2352 String8 output(bundle->getSingleCrunchOutputFile()); 2353 2354 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) { 2355 // we can't return the status_t as it gets truncate to the lower 8 bits. 2356 return 42; 2357 } 2358 2359 return NO_ERROR; 2360} 2361 2362char CONSOLE_DATA[2925] = { 2363 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2364 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2365 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2366 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 2367 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63, 2368 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83, 2369 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2370 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2371 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81, 2372 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32, 2373 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 2374 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2375 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59, 2376 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2377 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2378 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 2379 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32, 2380 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2381 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2382 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87, 2383 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32, 2384 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 2385 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 2386 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58, 2387 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2388 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2389 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81, 2390 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32, 2391 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 2392 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2393 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59, 2394 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2395 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2396 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59, 2397 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32, 2398 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2399 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2400 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81, 2401 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 2402 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 2403 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2404 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81, 2405 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2406 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2407 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109, 2408 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32, 2409 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 2410 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59, 2411 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59, 2412 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2413 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59, 2414 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46, 2415 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32, 2416 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2417 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 2418 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81, 2419 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32, 2420 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81, 2421 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2422 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58, 2423 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32, 2424 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59, 2425 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37, 2426 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59, 2427 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 2428 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59, 2429 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96, 2430 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 2431 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32, 2432 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61, 2433 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59, 2434 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32, 2435 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2436 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119, 2437 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2438 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10, 2439 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2440 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81, 2441 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41, 2442 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 2443 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81, 2444 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 2445 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32, 2446 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2447 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 2448 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 2449 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 2450 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 2451 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 2452 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2453 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2454 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81, 2455 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32, 2456 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2457 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2458 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2459 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2460 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 2461 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106, 2462 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59, 2463 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2464 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2465 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81, 2466 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 2467 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 2468 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2469 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59, 2470 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2471 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2472 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81, 2473 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32, 2474 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2475 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2476 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2477 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2478 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 2479 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 2480 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59, 2481 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2482 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2483 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33, 2484 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 2485 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 2486 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 2487 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59, 2488 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 2489 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 2490 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95, 2491 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59, 2492 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2493 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2494 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45, 2495 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32, 2496 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 2497 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59, 2498 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59, 2499 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2500 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2501 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32, 2502 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32, 2503 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 2504 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2505 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61, 2506 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2507 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 2508 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 2509 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59, 2510 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2511 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2512 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32, 2513 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32, 2514 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 2515 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61, 2516 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 2517 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2518 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2519 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32, 2520 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32, 2521 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2522 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2523 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2524 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2525 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10 2526 }; 2527