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