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