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