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