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