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