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