1 // 2 // Copyright 2006 The Android Open Source Project 3 // 4 // Android Asset Packaging Tool main entry point. 5 // 6 #include "Main.h" 7 #include "Bundle.h" 8 #include "ResourceFilter.h" 9 #include "ResourceTable.h" 10 #include "Images.h" 11 #include "XMLNode.h" 12 13 #include <utils/Log.h> 14 #include <utils/threads.h> 15 #include <utils/List.h> 16 #include <utils/Errors.h> 17 18 #include <fcntl.h> 19 #include <errno.h> 20 21 using namespace android; 22 23 /* 24 * Show version info. All the cool kids do it. 25 */ 26 int doVersion(Bundle* bundle) 27 { 28 if (bundle->getFileSpecCount() != 0) 29 printf("(ignoring extra arguments)\n"); 30 printf("Android Asset Packaging Tool, v0.2\n"); 31 32 return 0; 33 } 34 35 36 /* 37 * Open the file read only. The call fails if the file doesn't exist. 38 * 39 * Returns NULL on failure. 40 */ 41 ZipFile* openReadOnly(const char* fileName) 42 { 43 ZipFile* zip; 44 status_t result; 45 46 zip = new ZipFile; 47 result = zip->open(fileName, ZipFile::kOpenReadOnly); 48 if (result != NO_ERROR) { 49 if (result == NAME_NOT_FOUND) 50 fprintf(stderr, "ERROR: '%s' not found\n", fileName); 51 else if (result == PERMISSION_DENIED) 52 fprintf(stderr, "ERROR: '%s' access denied\n", fileName); 53 else 54 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n", 55 fileName); 56 delete zip; 57 return NULL; 58 } 59 60 return zip; 61 } 62 63 /* 64 * Open the file read-write. The file will be created if it doesn't 65 * already exist and "okayToCreate" is set. 66 * 67 * Returns NULL on failure. 68 */ 69 ZipFile* openReadWrite(const char* fileName, bool okayToCreate) 70 { 71 ZipFile* zip = NULL; 72 status_t result; 73 int flags; 74 75 flags = ZipFile::kOpenReadWrite; 76 if (okayToCreate) 77 flags |= ZipFile::kOpenCreate; 78 79 zip = new ZipFile; 80 result = zip->open(fileName, flags); 81 if (result != NO_ERROR) { 82 delete zip; 83 zip = NULL; 84 goto bail; 85 } 86 87 bail: 88 return zip; 89 } 90 91 92 /* 93 * Return a short string describing the compression method. 94 */ 95 const char* compressionName(int method) 96 { 97 if (method == ZipEntry::kCompressStored) 98 return "Stored"; 99 else if (method == ZipEntry::kCompressDeflated) 100 return "Deflated"; 101 else 102 return "Unknown"; 103 } 104 105 /* 106 * Return the percent reduction in size (0% == no compression). 107 */ 108 int calcPercent(long uncompressedLen, long compressedLen) 109 { 110 if (!uncompressedLen) 111 return 0; 112 else 113 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5); 114 } 115 116 /* 117 * Handle the "list" command, which can be a simple file dump or 118 * a verbose listing. 119 * 120 * The verbose listing closely matches the output of the Info-ZIP "unzip" 121 * command. 122 */ 123 int doList(Bundle* bundle) 124 { 125 int result = 1; 126 ZipFile* zip = NULL; 127 const ZipEntry* entry; 128 long totalUncLen, totalCompLen; 129 const char* zipFileName; 130 131 if (bundle->getFileSpecCount() != 1) { 132 fprintf(stderr, "ERROR: specify zip file name (only)\n"); 133 goto bail; 134 } 135 zipFileName = bundle->getFileSpecEntry(0); 136 137 zip = openReadOnly(zipFileName); 138 if (zip == NULL) 139 goto bail; 140 141 int count, i; 142 143 if (bundle->getVerbose()) { 144 printf("Archive: %s\n", zipFileName); 145 printf( 146 " Length Method Size Ratio Offset Date Time CRC-32 Name\n"); 147 printf( 148 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n"); 149 } 150 151 totalUncLen = totalCompLen = 0; 152 153 count = zip->getNumEntries(); 154 for (i = 0; i < count; i++) { 155 entry = zip->getEntryByIndex(i); 156 if (bundle->getVerbose()) { 157 char dateBuf[32]; 158 time_t when; 159 160 when = entry->getModWhen(); 161 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M", 162 localtime(&when)); 163 164 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n", 165 (long) entry->getUncompressedLen(), 166 compressionName(entry->getCompressionMethod()), 167 (long) entry->getCompressedLen(), 168 calcPercent(entry->getUncompressedLen(), 169 entry->getCompressedLen()), 170 (size_t) entry->getLFHOffset(), 171 dateBuf, 172 entry->getCRC32(), 173 entry->getFileName()); 174 } else { 175 printf("%s\n", entry->getFileName()); 176 } 177 178 totalUncLen += entry->getUncompressedLen(); 179 totalCompLen += entry->getCompressedLen(); 180 } 181 182 if (bundle->getVerbose()) { 183 printf( 184 "-------- ------- --- -------\n"); 185 printf("%8ld %7ld %2d%% %d files\n", 186 totalUncLen, 187 totalCompLen, 188 calcPercent(totalUncLen, totalCompLen), 189 zip->getNumEntries()); 190 } 191 192 if (bundle->getAndroidList()) { 193 AssetManager assets; 194 if (!assets.addAssetPath(String8(zipFileName), NULL)) { 195 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n"); 196 goto bail; 197 } 198 199 const ResTable& res = assets.getResources(false); 200 if (&res == NULL) { 201 printf("\nNo resource table found.\n"); 202 } else { 203 #ifndef HAVE_ANDROID_OS 204 printf("\nResource table:\n"); 205 res.print(false); 206 #endif 207 } 208 209 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml", 210 Asset::ACCESS_BUFFER); 211 if (manifestAsset == NULL) { 212 printf("\nNo AndroidManifest.xml found.\n"); 213 } else { 214 printf("\nAndroid manifest:\n"); 215 ResXMLTree tree; 216 tree.setTo(manifestAsset->getBuffer(true), 217 manifestAsset->getLength()); 218 printXMLBlock(&tree); 219 } 220 delete manifestAsset; 221 } 222 223 result = 0; 224 225 bail: 226 delete zip; 227 return result; 228 } 229 230 static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes) 231 { 232 size_t N = tree.getAttributeCount(); 233 for (size_t i=0; i<N; i++) { 234 if (tree.getAttributeNameResID(i) == attrRes) { 235 return (ssize_t)i; 236 } 237 } 238 return -1; 239 } 240 241 String8 getAttribute(const ResXMLTree& tree, const char* ns, 242 const char* attr, String8* outError) 243 { 244 ssize_t idx = tree.indexOfAttribute(ns, attr); 245 if (idx < 0) { 246 return String8(); 247 } 248 Res_value value; 249 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 250 if (value.dataType != Res_value::TYPE_STRING) { 251 if (outError != NULL) *outError = "attribute is not a string value"; 252 return String8(); 253 } 254 } 255 size_t len; 256 const uint16_t* str = tree.getAttributeStringValue(idx, &len); 257 return str ? String8(str, len) : String8(); 258 } 259 260 static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError) 261 { 262 ssize_t idx = indexOfAttribute(tree, attrRes); 263 if (idx < 0) { 264 return String8(); 265 } 266 Res_value value; 267 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 268 if (value.dataType != Res_value::TYPE_STRING) { 269 if (outError != NULL) *outError = "attribute is not a string value"; 270 return String8(); 271 } 272 } 273 size_t len; 274 const uint16_t* str = tree.getAttributeStringValue(idx, &len); 275 return str ? String8(str, len) : String8(); 276 } 277 278 static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes, 279 String8* outError, int32_t defValue = -1) 280 { 281 ssize_t idx = indexOfAttribute(tree, attrRes); 282 if (idx < 0) { 283 return defValue; 284 } 285 Res_value value; 286 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 287 if (value.dataType < Res_value::TYPE_FIRST_INT 288 || value.dataType > Res_value::TYPE_LAST_INT) { 289 if (outError != NULL) *outError = "attribute is not an integer value"; 290 return defValue; 291 } 292 } 293 return value.data; 294 } 295 296 static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree, 297 uint32_t attrRes, String8* outError, int32_t defValue = -1) 298 { 299 ssize_t idx = indexOfAttribute(tree, attrRes); 300 if (idx < 0) { 301 return defValue; 302 } 303 Res_value value; 304 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 305 if (value.dataType == Res_value::TYPE_REFERENCE) { 306 resTable->resolveReference(&value, 0); 307 } 308 if (value.dataType < Res_value::TYPE_FIRST_INT 309 || value.dataType > Res_value::TYPE_LAST_INT) { 310 if (outError != NULL) *outError = "attribute is not an integer value"; 311 return defValue; 312 } 313 } 314 return value.data; 315 } 316 317 static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree, 318 uint32_t attrRes, String8* outError) 319 { 320 ssize_t idx = indexOfAttribute(tree, attrRes); 321 if (idx < 0) { 322 return String8(); 323 } 324 Res_value value; 325 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 326 if (value.dataType == Res_value::TYPE_STRING) { 327 size_t len; 328 const uint16_t* str = tree.getAttributeStringValue(idx, &len); 329 return str ? String8(str, len) : String8(); 330 } 331 resTable->resolveReference(&value, 0); 332 if (value.dataType != Res_value::TYPE_STRING) { 333 if (outError != NULL) *outError = "attribute is not a string value"; 334 return String8(); 335 } 336 } 337 size_t len; 338 const Res_value* value2 = &value; 339 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len); 340 return str ? String8(str, len) : String8(); 341 } 342 343 // These are attribute resource constants for the platform, as found 344 // in android.R.attr 345 enum { 346 LABEL_ATTR = 0x01010001, 347 ICON_ATTR = 0x01010002, 348 NAME_ATTR = 0x01010003, 349 DEBUGGABLE_ATTR = 0x0101000f, 350 VERSION_CODE_ATTR = 0x0101021b, 351 VERSION_NAME_ATTR = 0x0101021c, 352 SCREEN_ORIENTATION_ATTR = 0x0101001e, 353 MIN_SDK_VERSION_ATTR = 0x0101020c, 354 MAX_SDK_VERSION_ATTR = 0x01010271, 355 REQ_TOUCH_SCREEN_ATTR = 0x01010227, 356 REQ_KEYBOARD_TYPE_ATTR = 0x01010228, 357 REQ_HARD_KEYBOARD_ATTR = 0x01010229, 358 REQ_NAVIGATION_ATTR = 0x0101022a, 359 REQ_FIVE_WAY_NAV_ATTR = 0x01010232, 360 TARGET_SDK_VERSION_ATTR = 0x01010270, 361 TEST_ONLY_ATTR = 0x01010272, 362 ANY_DENSITY_ATTR = 0x0101026c, 363 GL_ES_VERSION_ATTR = 0x01010281, 364 SMALL_SCREEN_ATTR = 0x01010284, 365 NORMAL_SCREEN_ATTR = 0x01010285, 366 LARGE_SCREEN_ATTR = 0x01010286, 367 XLARGE_SCREEN_ATTR = 0x010102bf, 368 REQUIRED_ATTR = 0x0101028e, 369 SCREEN_SIZE_ATTR = 0x010102ca, 370 SCREEN_DENSITY_ATTR = 0x010102cb, 371 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364, 372 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365, 373 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366, 374 PUBLIC_KEY_ATTR = 0x010103a6, 375 }; 376 377 const char *getComponentName(String8 &pkgName, String8 &componentName) { 378 ssize_t idx = componentName.find("."); 379 String8 retStr(pkgName); 380 if (idx == 0) { 381 retStr += componentName; 382 } else if (idx < 0) { 383 retStr += "."; 384 retStr += componentName; 385 } else { 386 return componentName.string(); 387 } 388 return retStr.string(); 389 } 390 391 static void printCompatibleScreens(ResXMLTree& tree) { 392 size_t len; 393 ResXMLTree::event_code_t code; 394 int depth = 0; 395 bool first = true; 396 printf("compatible-screens:"); 397 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 398 if (code == ResXMLTree::END_TAG) { 399 depth--; 400 if (depth < 0) { 401 break; 402 } 403 continue; 404 } 405 if (code != ResXMLTree::START_TAG) { 406 continue; 407 } 408 depth++; 409 String8 tag(tree.getElementName(&len)); 410 if (tag == "screen") { 411 int32_t screenSize = getIntegerAttribute(tree, 412 SCREEN_SIZE_ATTR, NULL, -1); 413 int32_t screenDensity = getIntegerAttribute(tree, 414 SCREEN_DENSITY_ATTR, NULL, -1); 415 if (screenSize > 0 && screenDensity > 0) { 416 if (!first) { 417 printf(","); 418 } 419 first = false; 420 printf("'%d/%d'", screenSize, screenDensity); 421 } 422 } 423 } 424 printf("\n"); 425 } 426 427 /* 428 * Handle the "dump" command, to extract select data from an archive. 429 */ 430 extern char CONSOLE_DATA[2925]; // see EOF 431 int doDump(Bundle* bundle) 432 { 433 status_t result = UNKNOWN_ERROR; 434 Asset* asset = NULL; 435 436 if (bundle->getFileSpecCount() < 1) { 437 fprintf(stderr, "ERROR: no dump option specified\n"); 438 return 1; 439 } 440 441 if (bundle->getFileSpecCount() < 2) { 442 fprintf(stderr, "ERROR: no dump file specified\n"); 443 return 1; 444 } 445 446 const char* option = bundle->getFileSpecEntry(0); 447 const char* filename = bundle->getFileSpecEntry(1); 448 449 AssetManager assets; 450 void* assetsCookie; 451 if (!assets.addAssetPath(String8(filename), &assetsCookie)) { 452 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n"); 453 return 1; 454 } 455 456 // Make a dummy config for retrieving resources... we need to supply 457 // non-default values for some configs so that we can retrieve resources 458 // in the app that don't have a default. The most important of these is 459 // the API version because key resources like icons will have an implicit 460 // version if they are using newer config types like density. 461 ResTable_config config; 462 config.language[0] = 'e'; 463 config.language[1] = 'n'; 464 config.country[0] = 'U'; 465 config.country[1] = 'S'; 466 config.orientation = ResTable_config::ORIENTATION_PORT; 467 config.density = ResTable_config::DENSITY_MEDIUM; 468 config.sdkVersion = 10000; // Very high. 469 config.screenWidthDp = 320; 470 config.screenHeightDp = 480; 471 config.smallestScreenWidthDp = 320; 472 assets.setConfiguration(config); 473 474 const ResTable& res = assets.getResources(false); 475 if (&res == NULL) { 476 fprintf(stderr, "ERROR: dump failed because no resource table was found\n"); 477 goto bail; 478 } 479 480 if (strcmp("resources", option) == 0) { 481 #ifndef HAVE_ANDROID_OS 482 res.print(bundle->getValues()); 483 #endif 484 485 } else if (strcmp("strings", option) == 0) { 486 const ResStringPool* pool = res.getTableStringBlock(0); 487 printStringPool(pool); 488 489 } else if (strcmp("xmltree", option) == 0) { 490 if (bundle->getFileSpecCount() < 3) { 491 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n"); 492 goto bail; 493 } 494 495 for (int i=2; i<bundle->getFileSpecCount(); i++) { 496 const char* resname = bundle->getFileSpecEntry(i); 497 ResXMLTree tree; 498 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER); 499 if (asset == NULL) { 500 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname); 501 goto bail; 502 } 503 504 if (tree.setTo(asset->getBuffer(true), 505 asset->getLength()) != NO_ERROR) { 506 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname); 507 goto bail; 508 } 509 tree.restart(); 510 printXMLBlock(&tree); 511 tree.uninit(); 512 delete asset; 513 asset = NULL; 514 } 515 516 } else if (strcmp("xmlstrings", option) == 0) { 517 if (bundle->getFileSpecCount() < 3) { 518 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n"); 519 goto bail; 520 } 521 522 for (int i=2; i<bundle->getFileSpecCount(); i++) { 523 const char* resname = bundle->getFileSpecEntry(i); 524 ResXMLTree tree; 525 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER); 526 if (asset == NULL) { 527 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname); 528 goto bail; 529 } 530 531 if (tree.setTo(asset->getBuffer(true), 532 asset->getLength()) != NO_ERROR) { 533 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname); 534 goto bail; 535 } 536 printStringPool(&tree.getStrings()); 537 delete asset; 538 asset = NULL; 539 } 540 541 } else { 542 ResXMLTree tree; 543 asset = assets.openNonAsset("AndroidManifest.xml", 544 Asset::ACCESS_BUFFER); 545 if (asset == NULL) { 546 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n"); 547 goto bail; 548 } 549 550 if (tree.setTo(asset->getBuffer(true), 551 asset->getLength()) != NO_ERROR) { 552 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n"); 553 goto bail; 554 } 555 tree.restart(); 556 557 if (strcmp("permissions", option) == 0) { 558 size_t len; 559 ResXMLTree::event_code_t code; 560 int depth = 0; 561 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 562 if (code == ResXMLTree::END_TAG) { 563 depth--; 564 continue; 565 } 566 if (code != ResXMLTree::START_TAG) { 567 continue; 568 } 569 depth++; 570 String8 tag(tree.getElementName(&len)); 571 //printf("Depth %d tag %s\n", depth, tag.string()); 572 if (depth == 1) { 573 if (tag != "manifest") { 574 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 575 goto bail; 576 } 577 String8 pkg = getAttribute(tree, NULL, "package", NULL); 578 printf("package: %s\n", pkg.string()); 579 } else if (depth == 2 && tag == "permission") { 580 String8 error; 581 String8 name = getAttribute(tree, NAME_ATTR, &error); 582 if (error != "") { 583 fprintf(stderr, "ERROR: %s\n", error.string()); 584 goto bail; 585 } 586 printf("permission: %s\n", name.string()); 587 } else if (depth == 2 && tag == "uses-permission") { 588 String8 error; 589 String8 name = getAttribute(tree, NAME_ATTR, &error); 590 if (error != "") { 591 fprintf(stderr, "ERROR: %s\n", error.string()); 592 goto bail; 593 } 594 printf("uses-permission: %s\n", name.string()); 595 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1); 596 if (!req) { 597 printf("optional-permission: %s\n", name.string()); 598 } 599 } 600 } 601 } else if (strcmp("badging", option) == 0) { 602 Vector<String8> locales; 603 res.getLocales(&locales); 604 605 Vector<ResTable_config> configs; 606 res.getConfigurations(&configs); 607 SortedVector<int> densities; 608 const size_t NC = configs.size(); 609 for (size_t i=0; i<NC; i++) { 610 int dens = configs[i].density; 611 if (dens == 0) dens = 160; 612 densities.add(dens); 613 } 614 615 size_t len; 616 ResXMLTree::event_code_t code; 617 int depth = 0; 618 String8 error; 619 bool withinActivity = false; 620 bool isMainActivity = false; 621 bool isLauncherActivity = false; 622 bool isSearchable = false; 623 bool withinApplication = false; 624 bool withinReceiver = false; 625 bool withinService = false; 626 bool withinIntentFilter = false; 627 bool hasMainActivity = false; 628 bool hasOtherActivities = false; 629 bool hasOtherReceivers = false; 630 bool hasOtherServices = false; 631 bool hasWallpaperService = false; 632 bool hasImeService = false; 633 bool hasWidgetReceivers = false; 634 bool hasIntentFilter = false; 635 bool actMainActivity = false; 636 bool actWidgetReceivers = false; 637 bool actImeService = false; 638 bool actWallpaperService = false; 639 640 // These two implement the implicit permissions that are granted 641 // to pre-1.6 applications. 642 bool hasWriteExternalStoragePermission = false; 643 bool hasReadPhoneStatePermission = false; 644 645 // If an app requests write storage, they will also get read storage. 646 bool hasReadExternalStoragePermission = false; 647 648 // Implement transition to read and write call log. 649 bool hasReadContactsPermission = false; 650 bool hasWriteContactsPermission = false; 651 bool hasReadCallLogPermission = false; 652 bool hasWriteCallLogPermission = false; 653 654 // This next group of variables is used to implement a group of 655 // backward-compatibility heuristics necessitated by the addition of 656 // some new uses-feature constants in 2.1 and 2.2. In most cases, the 657 // heuristic is "if an app requests a permission but doesn't explicitly 658 // request the corresponding <uses-feature>, presume it's there anyway". 659 bool specCameraFeature = false; // camera-related 660 bool specCameraAutofocusFeature = false; 661 bool reqCameraAutofocusFeature = false; 662 bool reqCameraFlashFeature = false; 663 bool hasCameraPermission = false; 664 bool specLocationFeature = false; // location-related 665 bool specNetworkLocFeature = false; 666 bool reqNetworkLocFeature = false; 667 bool specGpsFeature = false; 668 bool reqGpsFeature = false; 669 bool hasMockLocPermission = false; 670 bool hasCoarseLocPermission = false; 671 bool hasGpsPermission = false; 672 bool hasGeneralLocPermission = false; 673 bool specBluetoothFeature = false; // Bluetooth API-related 674 bool hasBluetoothPermission = false; 675 bool specMicrophoneFeature = false; // microphone-related 676 bool hasRecordAudioPermission = false; 677 bool specWiFiFeature = false; 678 bool hasWiFiPermission = false; 679 bool specTelephonyFeature = false; // telephony-related 680 bool reqTelephonySubFeature = false; 681 bool hasTelephonyPermission = false; 682 bool specTouchscreenFeature = false; // touchscreen-related 683 bool specMultitouchFeature = false; 684 bool reqDistinctMultitouchFeature = false; 685 bool specScreenPortraitFeature = false; 686 bool specScreenLandscapeFeature = false; 687 bool reqScreenPortraitFeature = false; 688 bool reqScreenLandscapeFeature = false; 689 // 2.2 also added some other features that apps can request, but that 690 // have no corresponding permission, so we cannot implement any 691 // back-compatibility heuristic for them. The below are thus unnecessary 692 // (but are retained here for documentary purposes.) 693 //bool specCompassFeature = false; 694 //bool specAccelerometerFeature = false; 695 //bool specProximityFeature = false; 696 //bool specAmbientLightFeature = false; 697 //bool specLiveWallpaperFeature = false; 698 699 int targetSdk = 0; 700 int smallScreen = 1; 701 int normalScreen = 1; 702 int largeScreen = 1; 703 int xlargeScreen = 1; 704 int anyDensity = 1; 705 int requiresSmallestWidthDp = 0; 706 int compatibleWidthLimitDp = 0; 707 int largestWidthLimitDp = 0; 708 String8 pkg; 709 String8 activityName; 710 String8 activityLabel; 711 String8 activityIcon; 712 String8 receiverName; 713 String8 serviceName; 714 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 715 if (code == ResXMLTree::END_TAG) { 716 depth--; 717 if (depth < 2) { 718 withinApplication = false; 719 } else if (depth < 3) { 720 if (withinActivity && isMainActivity && isLauncherActivity) { 721 const char *aName = getComponentName(pkg, activityName); 722 printf("launchable-activity:"); 723 if (aName != NULL) { 724 printf(" name='%s' ", aName); 725 } 726 printf(" label='%s' icon='%s'\n", 727 activityLabel.string(), 728 activityIcon.string()); 729 } 730 if (!hasIntentFilter) { 731 hasOtherActivities |= withinActivity; 732 hasOtherReceivers |= withinReceiver; 733 hasOtherServices |= withinService; 734 } 735 withinActivity = false; 736 withinService = false; 737 withinReceiver = false; 738 hasIntentFilter = false; 739 isMainActivity = isLauncherActivity = false; 740 } else if (depth < 4) { 741 if (withinIntentFilter) { 742 if (withinActivity) { 743 hasMainActivity |= actMainActivity; 744 hasOtherActivities |= !actMainActivity; 745 } else if (withinReceiver) { 746 hasWidgetReceivers |= actWidgetReceivers; 747 hasOtherReceivers |= !actWidgetReceivers; 748 } else if (withinService) { 749 hasImeService |= actImeService; 750 hasWallpaperService |= actWallpaperService; 751 hasOtherServices |= (!actImeService && !actWallpaperService); 752 } 753 } 754 withinIntentFilter = false; 755 } 756 continue; 757 } 758 if (code != ResXMLTree::START_TAG) { 759 continue; 760 } 761 depth++; 762 String8 tag(tree.getElementName(&len)); 763 //printf("Depth %d, %s\n", depth, tag.string()); 764 if (depth == 1) { 765 if (tag != "manifest") { 766 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 767 goto bail; 768 } 769 pkg = getAttribute(tree, NULL, "package", NULL); 770 printf("package: name='%s' ", pkg.string()); 771 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error); 772 if (error != "") { 773 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string()); 774 goto bail; 775 } 776 if (versionCode > 0) { 777 printf("versionCode='%d' ", versionCode); 778 } else { 779 printf("versionCode='' "); 780 } 781 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error); 782 if (error != "") { 783 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string()); 784 goto bail; 785 } 786 printf("versionName='%s'\n", versionName.string()); 787 } else if (depth == 2) { 788 withinApplication = false; 789 if (tag == "application") { 790 withinApplication = true; 791 792 String8 label; 793 const size_t NL = locales.size(); 794 for (size_t i=0; i<NL; i++) { 795 const char* localeStr = locales[i].string(); 796 assets.setLocale(localeStr != NULL ? localeStr : ""); 797 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); 798 if (llabel != "") { 799 if (localeStr == NULL || strlen(localeStr) == 0) { 800 label = llabel; 801 printf("application-label:'%s'\n", llabel.string()); 802 } else { 803 if (label == "") { 804 label = llabel; 805 } 806 printf("application-label-%s:'%s'\n", localeStr, 807 llabel.string()); 808 } 809 } 810 } 811 812 ResTable_config tmpConfig = config; 813 const size_t ND = densities.size(); 814 for (size_t i=0; i<ND; i++) { 815 tmpConfig.density = densities[i]; 816 assets.setConfiguration(tmpConfig); 817 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 818 if (icon != "") { 819 printf("application-icon-%d:'%s'\n", densities[i], icon.string()); 820 } 821 } 822 assets.setConfiguration(config); 823 824 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 825 if (error != "") { 826 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string()); 827 goto bail; 828 } 829 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0); 830 if (error != "") { 831 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string()); 832 goto bail; 833 } 834 printf("application: label='%s' ", label.string()); 835 printf("icon='%s'\n", icon.string()); 836 if (testOnly != 0) { 837 printf("testOnly='%d'\n", testOnly); 838 } 839 840 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0); 841 if (error != "") { 842 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string()); 843 goto bail; 844 } 845 if (debuggable != 0) { 846 printf("application-debuggable\n"); 847 } 848 } else if (tag == "uses-sdk") { 849 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error); 850 if (error != "") { 851 error = ""; 852 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error); 853 if (error != "") { 854 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n", 855 error.string()); 856 goto bail; 857 } 858 if (name == "Donut") targetSdk = 4; 859 printf("sdkVersion:'%s'\n", name.string()); 860 } else if (code != -1) { 861 targetSdk = code; 862 printf("sdkVersion:'%d'\n", code); 863 } 864 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1); 865 if (code != -1) { 866 printf("maxSdkVersion:'%d'\n", code); 867 } 868 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error); 869 if (error != "") { 870 error = ""; 871 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error); 872 if (error != "") { 873 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n", 874 error.string()); 875 goto bail; 876 } 877 if (name == "Donut" && targetSdk < 4) targetSdk = 4; 878 printf("targetSdkVersion:'%s'\n", name.string()); 879 } else if (code != -1) { 880 if (targetSdk < code) { 881 targetSdk = code; 882 } 883 printf("targetSdkVersion:'%d'\n", code); 884 } 885 } else if (tag == "uses-configuration") { 886 int32_t reqTouchScreen = getIntegerAttribute(tree, 887 REQ_TOUCH_SCREEN_ATTR, NULL, 0); 888 int32_t reqKeyboardType = getIntegerAttribute(tree, 889 REQ_KEYBOARD_TYPE_ATTR, NULL, 0); 890 int32_t reqHardKeyboard = getIntegerAttribute(tree, 891 REQ_HARD_KEYBOARD_ATTR, NULL, 0); 892 int32_t reqNavigation = getIntegerAttribute(tree, 893 REQ_NAVIGATION_ATTR, NULL, 0); 894 int32_t reqFiveWayNav = getIntegerAttribute(tree, 895 REQ_FIVE_WAY_NAV_ATTR, NULL, 0); 896 printf("uses-configuration:"); 897 if (reqTouchScreen != 0) { 898 printf(" reqTouchScreen='%d'", reqTouchScreen); 899 } 900 if (reqKeyboardType != 0) { 901 printf(" reqKeyboardType='%d'", reqKeyboardType); 902 } 903 if (reqHardKeyboard != 0) { 904 printf(" reqHardKeyboard='%d'", reqHardKeyboard); 905 } 906 if (reqNavigation != 0) { 907 printf(" reqNavigation='%d'", reqNavigation); 908 } 909 if (reqFiveWayNav != 0) { 910 printf(" reqFiveWayNav='%d'", reqFiveWayNav); 911 } 912 printf("\n"); 913 } else if (tag == "supports-screens") { 914 smallScreen = getIntegerAttribute(tree, 915 SMALL_SCREEN_ATTR, NULL, 1); 916 normalScreen = getIntegerAttribute(tree, 917 NORMAL_SCREEN_ATTR, NULL, 1); 918 largeScreen = getIntegerAttribute(tree, 919 LARGE_SCREEN_ATTR, NULL, 1); 920 xlargeScreen = getIntegerAttribute(tree, 921 XLARGE_SCREEN_ATTR, NULL, 1); 922 anyDensity = getIntegerAttribute(tree, 923 ANY_DENSITY_ATTR, NULL, 1); 924 requiresSmallestWidthDp = getIntegerAttribute(tree, 925 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0); 926 compatibleWidthLimitDp = getIntegerAttribute(tree, 927 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0); 928 largestWidthLimitDp = getIntegerAttribute(tree, 929 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0); 930 } else if (tag == "uses-feature") { 931 String8 name = getAttribute(tree, NAME_ATTR, &error); 932 933 if (name != "" && error == "") { 934 int req = getIntegerAttribute(tree, 935 REQUIRED_ATTR, NULL, 1); 936 937 if (name == "android.hardware.camera") { 938 specCameraFeature = true; 939 } else if (name == "android.hardware.camera.autofocus") { 940 // these have no corresponding permission to check for, 941 // but should imply the foundational camera permission 942 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req; 943 specCameraAutofocusFeature = true; 944 } else if (req && (name == "android.hardware.camera.flash")) { 945 // these have no corresponding permission to check for, 946 // but should imply the foundational camera permission 947 reqCameraFlashFeature = true; 948 } else if (name == "android.hardware.location") { 949 specLocationFeature = true; 950 } else if (name == "android.hardware.location.network") { 951 specNetworkLocFeature = true; 952 reqNetworkLocFeature = reqNetworkLocFeature || req; 953 } else if (name == "android.hardware.location.gps") { 954 specGpsFeature = true; 955 reqGpsFeature = reqGpsFeature || req; 956 } else if (name == "android.hardware.bluetooth") { 957 specBluetoothFeature = true; 958 } else if (name == "android.hardware.touchscreen") { 959 specTouchscreenFeature = true; 960 } else if (name == "android.hardware.touchscreen.multitouch") { 961 specMultitouchFeature = true; 962 } else if (name == "android.hardware.touchscreen.multitouch.distinct") { 963 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req; 964 } else if (name == "android.hardware.microphone") { 965 specMicrophoneFeature = true; 966 } else if (name == "android.hardware.wifi") { 967 specWiFiFeature = true; 968 } else if (name == "android.hardware.telephony") { 969 specTelephonyFeature = true; 970 } else if (req && (name == "android.hardware.telephony.gsm" || 971 name == "android.hardware.telephony.cdma")) { 972 // these have no corresponding permission to check for, 973 // but should imply the foundational telephony permission 974 reqTelephonySubFeature = true; 975 } else if (name == "android.hardware.screen.portrait") { 976 specScreenPortraitFeature = true; 977 } else if (name == "android.hardware.screen.landscape") { 978 specScreenLandscapeFeature = true; 979 } 980 printf("uses-feature%s:'%s'\n", 981 req ? "" : "-not-required", name.string()); 982 } else { 983 int vers = getIntegerAttribute(tree, 984 GL_ES_VERSION_ATTR, &error); 985 if (error == "") { 986 printf("uses-gl-es:'0x%x'\n", vers); 987 } 988 } 989 } else if (tag == "uses-permission") { 990 String8 name = getAttribute(tree, NAME_ATTR, &error); 991 if (name != "" && error == "") { 992 if (name == "android.permission.CAMERA") { 993 hasCameraPermission = true; 994 } else if (name == "android.permission.ACCESS_FINE_LOCATION") { 995 hasGpsPermission = true; 996 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") { 997 hasMockLocPermission = true; 998 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") { 999 hasCoarseLocPermission = true; 1000 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" || 1001 name == "android.permission.INSTALL_LOCATION_PROVIDER") { 1002 hasGeneralLocPermission = true; 1003 } else if (name == "android.permission.BLUETOOTH" || 1004 name == "android.permission.BLUETOOTH_ADMIN") { 1005 hasBluetoothPermission = true; 1006 } else if (name == "android.permission.RECORD_AUDIO") { 1007 hasRecordAudioPermission = true; 1008 } else if (name == "android.permission.ACCESS_WIFI_STATE" || 1009 name == "android.permission.CHANGE_WIFI_STATE" || 1010 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") { 1011 hasWiFiPermission = true; 1012 } else if (name == "android.permission.CALL_PHONE" || 1013 name == "android.permission.CALL_PRIVILEGED" || 1014 name == "android.permission.MODIFY_PHONE_STATE" || 1015 name == "android.permission.PROCESS_OUTGOING_CALLS" || 1016 name == "android.permission.READ_SMS" || 1017 name == "android.permission.RECEIVE_SMS" || 1018 name == "android.permission.RECEIVE_MMS" || 1019 name == "android.permission.RECEIVE_WAP_PUSH" || 1020 name == "android.permission.SEND_SMS" || 1021 name == "android.permission.WRITE_APN_SETTINGS" || 1022 name == "android.permission.WRITE_SMS") { 1023 hasTelephonyPermission = true; 1024 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") { 1025 hasWriteExternalStoragePermission = true; 1026 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") { 1027 hasReadExternalStoragePermission = true; 1028 } else if (name == "android.permission.READ_PHONE_STATE") { 1029 hasReadPhoneStatePermission = true; 1030 } else if (name == "android.permission.READ_CONTACTS") { 1031 hasReadContactsPermission = true; 1032 } else if (name == "android.permission.WRITE_CONTACTS") { 1033 hasWriteContactsPermission = true; 1034 } else if (name == "android.permission.READ_CALL_LOG") { 1035 hasReadCallLogPermission = true; 1036 } else if (name == "android.permission.WRITE_CALL_LOG") { 1037 hasWriteCallLogPermission = true; 1038 } 1039 printf("uses-permission:'%s'\n", name.string()); 1040 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1); 1041 if (!req) { 1042 printf("optional-permission:'%s'\n", name.string()); 1043 } 1044 } else { 1045 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1046 error.string()); 1047 goto bail; 1048 } 1049 } else if (tag == "uses-package") { 1050 String8 name = getAttribute(tree, NAME_ATTR, &error); 1051 if (name != "" && error == "") { 1052 printf("uses-package:'%s'\n", name.string()); 1053 } else { 1054 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1055 error.string()); 1056 goto bail; 1057 } 1058 } else if (tag == "original-package") { 1059 String8 name = getAttribute(tree, NAME_ATTR, &error); 1060 if (name != "" && error == "") { 1061 printf("original-package:'%s'\n", name.string()); 1062 } else { 1063 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1064 error.string()); 1065 goto bail; 1066 } 1067 } else if (tag == "supports-gl-texture") { 1068 String8 name = getAttribute(tree, NAME_ATTR, &error); 1069 if (name != "" && error == "") { 1070 printf("supports-gl-texture:'%s'\n", name.string()); 1071 } else { 1072 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1073 error.string()); 1074 goto bail; 1075 } 1076 } else if (tag == "compatible-screens") { 1077 printCompatibleScreens(tree); 1078 depth--; 1079 } else if (tag == "package-verifier") { 1080 String8 name = getAttribute(tree, NAME_ATTR, &error); 1081 if (name != "" && error == "") { 1082 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error); 1083 if (publicKey != "" && error == "") { 1084 printf("package-verifier: name='%s' publicKey='%s'\n", 1085 name.string(), publicKey.string()); 1086 } 1087 } 1088 } 1089 } else if (depth == 3 && withinApplication) { 1090 withinActivity = false; 1091 withinReceiver = false; 1092 withinService = false; 1093 hasIntentFilter = false; 1094 if(tag == "activity") { 1095 withinActivity = true; 1096 activityName = getAttribute(tree, NAME_ATTR, &error); 1097 if (error != "") { 1098 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); 1099 goto bail; 1100 } 1101 1102 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); 1103 if (error != "") { 1104 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string()); 1105 goto bail; 1106 } 1107 1108 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 1109 if (error != "") { 1110 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string()); 1111 goto bail; 1112 } 1113 1114 int32_t orien = getResolvedIntegerAttribute(&res, tree, 1115 SCREEN_ORIENTATION_ATTR, &error); 1116 if (error == "") { 1117 if (orien == 0 || orien == 6 || orien == 8) { 1118 // Requests landscape, sensorLandscape, or reverseLandscape. 1119 reqScreenLandscapeFeature = true; 1120 } else if (orien == 1 || orien == 7 || orien == 9) { 1121 // Requests portrait, sensorPortrait, or reversePortrait. 1122 reqScreenPortraitFeature = true; 1123 } 1124 } 1125 } else if (tag == "uses-library") { 1126 String8 libraryName = getAttribute(tree, NAME_ATTR, &error); 1127 if (error != "") { 1128 fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string()); 1129 goto bail; 1130 } 1131 int req = getIntegerAttribute(tree, 1132 REQUIRED_ATTR, NULL, 1); 1133 printf("uses-library%s:'%s'\n", 1134 req ? "" : "-not-required", libraryName.string()); 1135 } else if (tag == "receiver") { 1136 withinReceiver = true; 1137 receiverName = getAttribute(tree, NAME_ATTR, &error); 1138 1139 if (error != "") { 1140 fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string()); 1141 goto bail; 1142 } 1143 } else if (tag == "service") { 1144 withinService = true; 1145 serviceName = getAttribute(tree, NAME_ATTR, &error); 1146 1147 if (error != "") { 1148 fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string()); 1149 goto bail; 1150 } 1151 } 1152 } else if ((depth == 4) && (tag == "intent-filter")) { 1153 hasIntentFilter = true; 1154 withinIntentFilter = true; 1155 actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false; 1156 } else if ((depth == 5) && withinIntentFilter){ 1157 String8 action; 1158 if (tag == "action") { 1159 action = getAttribute(tree, NAME_ATTR, &error); 1160 if (error != "") { 1161 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); 1162 goto bail; 1163 } 1164 if (withinActivity) { 1165 if (action == "android.intent.action.MAIN") { 1166 isMainActivity = true; 1167 actMainActivity = true; 1168 } 1169 } else if (withinReceiver) { 1170 if (action == "android.appwidget.action.APPWIDGET_UPDATE") { 1171 actWidgetReceivers = true; 1172 } 1173 } else if (withinService) { 1174 if (action == "android.view.InputMethod") { 1175 actImeService = true; 1176 } else if (action == "android.service.wallpaper.WallpaperService") { 1177 actWallpaperService = true; 1178 } 1179 } 1180 if (action == "android.intent.action.SEARCH") { 1181 isSearchable = true; 1182 } 1183 } 1184 1185 if (tag == "category") { 1186 String8 category = getAttribute(tree, NAME_ATTR, &error); 1187 if (error != "") { 1188 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string()); 1189 goto bail; 1190 } 1191 if (withinActivity) { 1192 if (category == "android.intent.category.LAUNCHER") { 1193 isLauncherActivity = true; 1194 } 1195 } 1196 } 1197 } 1198 } 1199 1200 // Pre-1.6 implicitly granted permission compatibility logic 1201 if (targetSdk < 4) { 1202 if (!hasWriteExternalStoragePermission) { 1203 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n"); 1204 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \ 1205 "'targetSdkVersion < 4'\n"); 1206 hasWriteExternalStoragePermission = true; 1207 } 1208 if (!hasReadPhoneStatePermission) { 1209 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n"); 1210 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \ 1211 "'targetSdkVersion < 4'\n"); 1212 } 1213 } 1214 1215 // If the application has requested WRITE_EXTERNAL_STORAGE, we will 1216 // force them to always take READ_EXTERNAL_STORAGE as well. We always 1217 // do this (regardless of target API version) because we can't have 1218 // an app with write permission but not read permission. 1219 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) { 1220 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n"); 1221 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \ 1222 "'requested WRITE_EXTERNAL_STORAGE'\n"); 1223 } 1224 1225 // Pre-JellyBean call log permission compatibility. 1226 if (targetSdk < 16) { 1227 if (!hasReadCallLogPermission && hasReadContactsPermission) { 1228 printf("uses-permission:'android.permission.READ_CALL_LOG'\n"); 1229 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \ 1230 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n"); 1231 } 1232 if (!hasWriteCallLogPermission && hasWriteContactsPermission) { 1233 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n"); 1234 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \ 1235 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n"); 1236 } 1237 } 1238 1239 /* The following blocks handle printing "inferred" uses-features, based 1240 * on whether related features or permissions are used by the app. 1241 * Note that the various spec*Feature variables denote whether the 1242 * relevant tag was *present* in the AndroidManfest, not that it was 1243 * present and set to true. 1244 */ 1245 // Camera-related back-compatibility logic 1246 if (!specCameraFeature) { 1247 if (reqCameraFlashFeature) { 1248 // if app requested a sub-feature (autofocus or flash) and didn't 1249 // request the base camera feature, we infer that it meant to 1250 printf("uses-feature:'android.hardware.camera'\n"); 1251 printf("uses-implied-feature:'android.hardware.camera'," \ 1252 "'requested android.hardware.camera.flash feature'\n"); 1253 } else if (reqCameraAutofocusFeature) { 1254 // if app requested a sub-feature (autofocus or flash) and didn't 1255 // request the base camera feature, we infer that it meant to 1256 printf("uses-feature:'android.hardware.camera'\n"); 1257 printf("uses-implied-feature:'android.hardware.camera'," \ 1258 "'requested android.hardware.camera.autofocus feature'\n"); 1259 } else if (hasCameraPermission) { 1260 // if app wants to use camera but didn't request the feature, we infer 1261 // that it meant to, and further that it wants autofocus 1262 // (which was the 1.0 - 1.5 behavior) 1263 printf("uses-feature:'android.hardware.camera'\n"); 1264 if (!specCameraAutofocusFeature) { 1265 printf("uses-feature:'android.hardware.camera.autofocus'\n"); 1266 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \ 1267 "'requested android.permission.CAMERA permission'\n"); 1268 } 1269 } 1270 } 1271 1272 // Location-related back-compatibility logic 1273 if (!specLocationFeature && 1274 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission || 1275 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) { 1276 // if app either takes a location-related permission or requests one of the 1277 // sub-features, we infer that it also meant to request the base location feature 1278 printf("uses-feature:'android.hardware.location'\n"); 1279 printf("uses-implied-feature:'android.hardware.location'," \ 1280 "'requested a location access permission'\n"); 1281 } 1282 if (!specGpsFeature && hasGpsPermission) { 1283 // if app takes GPS (FINE location) perm but does not request the GPS 1284 // feature, we infer that it meant to 1285 printf("uses-feature:'android.hardware.location.gps'\n"); 1286 printf("uses-implied-feature:'android.hardware.location.gps'," \ 1287 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n"); 1288 } 1289 if (!specNetworkLocFeature && hasCoarseLocPermission) { 1290 // if app takes Network location (COARSE location) perm but does not request the 1291 // network location feature, we infer that it meant to 1292 printf("uses-feature:'android.hardware.location.network'\n"); 1293 printf("uses-implied-feature:'android.hardware.location.network'," \ 1294 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n"); 1295 } 1296 1297 // Bluetooth-related compatibility logic 1298 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) { 1299 // if app takes a Bluetooth permission but does not request the Bluetooth 1300 // feature, we infer that it meant to 1301 printf("uses-feature:'android.hardware.bluetooth'\n"); 1302 printf("uses-implied-feature:'android.hardware.bluetooth'," \ 1303 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \ 1304 "permission and targetSdkVersion > 4'\n"); 1305 } 1306 1307 // Microphone-related compatibility logic 1308 if (!specMicrophoneFeature && hasRecordAudioPermission) { 1309 // if app takes the record-audio permission but does not request the microphone 1310 // feature, we infer that it meant to 1311 printf("uses-feature:'android.hardware.microphone'\n"); 1312 printf("uses-implied-feature:'android.hardware.microphone'," \ 1313 "'requested android.permission.RECORD_AUDIO permission'\n"); 1314 } 1315 1316 // WiFi-related compatibility logic 1317 if (!specWiFiFeature && hasWiFiPermission) { 1318 // if app takes one of the WiFi permissions but does not request the WiFi 1319 // feature, we infer that it meant to 1320 printf("uses-feature:'android.hardware.wifi'\n"); 1321 printf("uses-implied-feature:'android.hardware.wifi'," \ 1322 "'requested android.permission.ACCESS_WIFI_STATE, " \ 1323 "android.permission.CHANGE_WIFI_STATE, or " \ 1324 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n"); 1325 } 1326 1327 // Telephony-related compatibility logic 1328 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) { 1329 // if app takes one of the telephony permissions or requests a sub-feature but 1330 // does not request the base telephony feature, we infer that it meant to 1331 printf("uses-feature:'android.hardware.telephony'\n"); 1332 printf("uses-implied-feature:'android.hardware.telephony'," \ 1333 "'requested a telephony-related permission or feature'\n"); 1334 } 1335 1336 // Touchscreen-related back-compatibility logic 1337 if (!specTouchscreenFeature) { // not a typo! 1338 // all apps are presumed to require a touchscreen, unless they explicitly say 1339 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/> 1340 // Note that specTouchscreenFeature is true if the tag is present, regardless 1341 // of whether its value is true or false, so this is safe 1342 printf("uses-feature:'android.hardware.touchscreen'\n"); 1343 printf("uses-implied-feature:'android.hardware.touchscreen'," \ 1344 "'assumed you require a touch screen unless explicitly made optional'\n"); 1345 } 1346 if (!specMultitouchFeature && reqDistinctMultitouchFeature) { 1347 // if app takes one of the telephony permissions or requests a sub-feature but 1348 // does not request the base telephony feature, we infer that it meant to 1349 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n"); 1350 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \ 1351 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n"); 1352 } 1353 1354 // Landscape/portrait-related compatibility logic 1355 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) { 1356 // If the app has specified any activities in its manifest 1357 // that request a specific orientation, then assume that 1358 // orientation is required. 1359 if (reqScreenLandscapeFeature) { 1360 printf("uses-feature:'android.hardware.screen.landscape'\n"); 1361 printf("uses-implied-feature:'android.hardware.screen.landscape'," \ 1362 "'one or more activities have specified a landscape orientation'\n"); 1363 } 1364 if (reqScreenPortraitFeature) { 1365 printf("uses-feature:'android.hardware.screen.portrait'\n"); 1366 printf("uses-implied-feature:'android.hardware.screen.portrait'," \ 1367 "'one or more activities have specified a portrait orientation'\n"); 1368 } 1369 } 1370 1371 if (hasMainActivity) { 1372 printf("main\n"); 1373 } 1374 if (hasWidgetReceivers) { 1375 printf("app-widget\n"); 1376 } 1377 if (hasImeService) { 1378 printf("ime\n"); 1379 } 1380 if (hasWallpaperService) { 1381 printf("wallpaper\n"); 1382 } 1383 if (hasOtherActivities) { 1384 printf("other-activities\n"); 1385 } 1386 if (isSearchable) { 1387 printf("search\n"); 1388 } 1389 if (hasOtherReceivers) { 1390 printf("other-receivers\n"); 1391 } 1392 if (hasOtherServices) { 1393 printf("other-services\n"); 1394 } 1395 1396 // For modern apps, if screen size buckets haven't been specified 1397 // but the new width ranges have, then infer the buckets from them. 1398 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0 1399 && requiresSmallestWidthDp > 0) { 1400 int compatWidth = compatibleWidthLimitDp; 1401 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp; 1402 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) { 1403 smallScreen = -1; 1404 } else { 1405 smallScreen = 0; 1406 } 1407 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) { 1408 normalScreen = -1; 1409 } else { 1410 normalScreen = 0; 1411 } 1412 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) { 1413 largeScreen = -1; 1414 } else { 1415 largeScreen = 0; 1416 } 1417 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) { 1418 xlargeScreen = -1; 1419 } else { 1420 xlargeScreen = 0; 1421 } 1422 } 1423 1424 // Determine default values for any unspecified screen sizes, 1425 // based on the target SDK of the package. As of 4 (donut) 1426 // the screen size support was introduced, so all default to 1427 // enabled. 1428 if (smallScreen > 0) { 1429 smallScreen = targetSdk >= 4 ? -1 : 0; 1430 } 1431 if (normalScreen > 0) { 1432 normalScreen = -1; 1433 } 1434 if (largeScreen > 0) { 1435 largeScreen = targetSdk >= 4 ? -1 : 0; 1436 } 1437 if (xlargeScreen > 0) { 1438 // Introduced in Gingerbread. 1439 xlargeScreen = targetSdk >= 9 ? -1 : 0; 1440 } 1441 if (anyDensity > 0) { 1442 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0 1443 || compatibleWidthLimitDp > 0) ? -1 : 0; 1444 } 1445 printf("supports-screens:"); 1446 if (smallScreen != 0) printf(" 'small'"); 1447 if (normalScreen != 0) printf(" 'normal'"); 1448 if (largeScreen != 0) printf(" 'large'"); 1449 if (xlargeScreen != 0) printf(" 'xlarge'"); 1450 printf("\n"); 1451 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false"); 1452 if (requiresSmallestWidthDp > 0) { 1453 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp); 1454 } 1455 if (compatibleWidthLimitDp > 0) { 1456 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp); 1457 } 1458 if (largestWidthLimitDp > 0) { 1459 printf("largest-width-limit:'%d'\n", largestWidthLimitDp); 1460 } 1461 1462 printf("locales:"); 1463 const size_t NL = locales.size(); 1464 for (size_t i=0; i<NL; i++) { 1465 const char* localeStr = locales[i].string(); 1466 if (localeStr == NULL || strlen(localeStr) == 0) { 1467 localeStr = "--_--"; 1468 } 1469 printf(" '%s'", localeStr); 1470 } 1471 printf("\n"); 1472 1473 printf("densities:"); 1474 const size_t ND = densities.size(); 1475 for (size_t i=0; i<ND; i++) { 1476 printf(" '%d'", densities[i]); 1477 } 1478 printf("\n"); 1479 1480 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib"); 1481 if (dir != NULL) { 1482 if (dir->getFileCount() > 0) { 1483 printf("native-code:"); 1484 for (size_t i=0; i<dir->getFileCount(); i++) { 1485 printf(" '%s'", dir->getFileName(i).string()); 1486 } 1487 printf("\n"); 1488 } 1489 delete dir; 1490 } 1491 } else if (strcmp("badger", option) == 0) { 1492 printf("%s", CONSOLE_DATA); 1493 } else if (strcmp("configurations", option) == 0) { 1494 Vector<ResTable_config> configs; 1495 res.getConfigurations(&configs); 1496 const size_t N = configs.size(); 1497 for (size_t i=0; i<N; i++) { 1498 printf("%s\n", configs[i].toString().string()); 1499 } 1500 } else { 1501 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option); 1502 goto bail; 1503 } 1504 } 1505 1506 result = NO_ERROR; 1507 1508 bail: 1509 if (asset) { 1510 delete asset; 1511 } 1512 return (result != NO_ERROR); 1513 } 1514 1515 1516 /* 1517 * Handle the "add" command, which wants to add files to a new or 1518 * pre-existing archive. 1519 */ 1520 int doAdd(Bundle* bundle) 1521 { 1522 ZipFile* zip = NULL; 1523 status_t result = UNKNOWN_ERROR; 1524 const char* zipFileName; 1525 1526 if (bundle->getUpdate()) { 1527 /* avoid confusion */ 1528 fprintf(stderr, "ERROR: can't use '-u' with add\n"); 1529 goto bail; 1530 } 1531 1532 if (bundle->getFileSpecCount() < 1) { 1533 fprintf(stderr, "ERROR: must specify zip file name\n"); 1534 goto bail; 1535 } 1536 zipFileName = bundle->getFileSpecEntry(0); 1537 1538 if (bundle->getFileSpecCount() < 2) { 1539 fprintf(stderr, "NOTE: nothing to do\n"); 1540 goto bail; 1541 } 1542 1543 zip = openReadWrite(zipFileName, true); 1544 if (zip == NULL) { 1545 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName); 1546 goto bail; 1547 } 1548 1549 for (int i = 1; i < bundle->getFileSpecCount(); i++) { 1550 const char* fileName = bundle->getFileSpecEntry(i); 1551 1552 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) { 1553 printf(" '%s'... (from gzip)\n", fileName); 1554 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL); 1555 } else { 1556 if (bundle->getJunkPath()) { 1557 String8 storageName = String8(fileName).getPathLeaf(); 1558 printf(" '%s' as '%s'...\n", fileName, storageName.string()); 1559 result = zip->add(fileName, storageName.string(), 1560 bundle->getCompressionMethod(), NULL); 1561 } else { 1562 printf(" '%s'...\n", fileName); 1563 result = zip->add(fileName, bundle->getCompressionMethod(), NULL); 1564 } 1565 } 1566 if (result != NO_ERROR) { 1567 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName); 1568 if (result == NAME_NOT_FOUND) 1569 fprintf(stderr, ": file not found\n"); 1570 else if (result == ALREADY_EXISTS) 1571 fprintf(stderr, ": already exists in archive\n"); 1572 else 1573 fprintf(stderr, "\n"); 1574 goto bail; 1575 } 1576 } 1577 1578 result = NO_ERROR; 1579 1580 bail: 1581 delete zip; 1582 return (result != NO_ERROR); 1583 } 1584 1585 1586 /* 1587 * Delete files from an existing archive. 1588 */ 1589 int doRemove(Bundle* bundle) 1590 { 1591 ZipFile* zip = NULL; 1592 status_t result = UNKNOWN_ERROR; 1593 const char* zipFileName; 1594 1595 if (bundle->getFileSpecCount() < 1) { 1596 fprintf(stderr, "ERROR: must specify zip file name\n"); 1597 goto bail; 1598 } 1599 zipFileName = bundle->getFileSpecEntry(0); 1600 1601 if (bundle->getFileSpecCount() < 2) { 1602 fprintf(stderr, "NOTE: nothing to do\n"); 1603 goto bail; 1604 } 1605 1606 zip = openReadWrite(zipFileName, false); 1607 if (zip == NULL) { 1608 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n", 1609 zipFileName); 1610 goto bail; 1611 } 1612 1613 for (int i = 1; i < bundle->getFileSpecCount(); i++) { 1614 const char* fileName = bundle->getFileSpecEntry(i); 1615 ZipEntry* entry; 1616 1617 entry = zip->getEntryByName(fileName); 1618 if (entry == NULL) { 1619 printf(" '%s' NOT FOUND\n", fileName); 1620 continue; 1621 } 1622 1623 result = zip->remove(entry); 1624 1625 if (result != NO_ERROR) { 1626 fprintf(stderr, "Unable to delete '%s' from '%s'\n", 1627 bundle->getFileSpecEntry(i), zipFileName); 1628 goto bail; 1629 } 1630 } 1631 1632 /* update the archive */ 1633 zip->flush(); 1634 1635 bail: 1636 delete zip; 1637 return (result != NO_ERROR); 1638 } 1639 1640 1641 /* 1642 * Package up an asset directory and associated application files. 1643 */ 1644 int doPackage(Bundle* bundle) 1645 { 1646 const char* outputAPKFile; 1647 int retVal = 1; 1648 status_t err; 1649 sp<AaptAssets> assets; 1650 int N; 1651 FILE* fp; 1652 String8 dependencyFile; 1653 1654 // -c zz_ZZ means do pseudolocalization 1655 ResourceFilter filter; 1656 err = filter.parse(bundle->getConfigurations()); 1657 if (err != NO_ERROR) { 1658 goto bail; 1659 } 1660 if (filter.containsPseudo()) { 1661 bundle->setPseudolocalize(true); 1662 } 1663 1664 N = bundle->getFileSpecCount(); 1665 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0 1666 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) { 1667 fprintf(stderr, "ERROR: no input files\n"); 1668 goto bail; 1669 } 1670 1671 outputAPKFile = bundle->getOutputAPKFile(); 1672 1673 // Make sure the filenames provided exist and are of the appropriate type. 1674 if (outputAPKFile) { 1675 FileType type; 1676 type = getFileType(outputAPKFile); 1677 if (type != kFileTypeNonexistent && type != kFileTypeRegular) { 1678 fprintf(stderr, 1679 "ERROR: output file '%s' exists but is not regular file\n", 1680 outputAPKFile); 1681 goto bail; 1682 } 1683 } 1684 1685 // Load the assets. 1686 assets = new AaptAssets(); 1687 1688 // Set up the resource gathering in assets if we're going to generate 1689 // dependency files. Every time we encounter a resource while slurping 1690 // the tree, we'll add it to these stores so we have full resource paths 1691 // to write to a dependency file. 1692 if (bundle->getGenDependencies()) { 1693 sp<FilePathStore> resPathStore = new FilePathStore; 1694 assets->setFullResPaths(resPathStore); 1695 sp<FilePathStore> assetPathStore = new FilePathStore; 1696 assets->setFullAssetPaths(assetPathStore); 1697 } 1698 1699 err = assets->slurpFromArgs(bundle); 1700 if (err < 0) { 1701 goto bail; 1702 } 1703 1704 if (bundle->getVerbose()) { 1705 assets->print(String8()); 1706 } 1707 1708 // If they asked for any fileAs that need to be compiled, do so. 1709 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) { 1710 err = buildResources(bundle, assets); 1711 if (err != 0) { 1712 goto bail; 1713 } 1714 } 1715 1716 // At this point we've read everything and processed everything. From here 1717 // on out it's just writing output files. 1718 if (SourcePos::hasErrors()) { 1719 goto bail; 1720 } 1721 1722 // Update symbols with information about which ones are needed as Java symbols. 1723 assets->applyJavaSymbols(); 1724 if (SourcePos::hasErrors()) { 1725 goto bail; 1726 } 1727 1728 // If we've been asked to generate a dependency file, do that here 1729 if (bundle->getGenDependencies()) { 1730 // If this is the packaging step, generate the dependency file next to 1731 // the output apk (e.g. bin/resources.ap_.d) 1732 if (outputAPKFile) { 1733 dependencyFile = String8(outputAPKFile); 1734 // Add the .d extension to the dependency file. 1735 dependencyFile.append(".d"); 1736 } else { 1737 // Else if this is the R.java dependency generation step, 1738 // generate the dependency file in the R.java package subdirectory 1739 // e.g. gen/com/foo/app/R.java.d 1740 dependencyFile = String8(bundle->getRClassDir()); 1741 dependencyFile.appendPath("R.java.d"); 1742 } 1743 // Make sure we have a clean dependency file to start with 1744 fp = fopen(dependencyFile, "w"); 1745 fclose(fp); 1746 } 1747 1748 // Write out R.java constants 1749 if (!assets->havePrivateSymbols()) { 1750 if (bundle->getCustomPackage() == NULL) { 1751 // Write the R.java file into the appropriate class directory 1752 // e.g. gen/com/foo/app/R.java 1753 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true); 1754 } else { 1755 const String8 customPkg(bundle->getCustomPackage()); 1756 err = writeResourceSymbols(bundle, assets, customPkg, true); 1757 } 1758 if (err < 0) { 1759 goto bail; 1760 } 1761 // If we have library files, we're going to write our R.java file into 1762 // the appropriate class directory for those libraries as well. 1763 // e.g. gen/com/foo/app/lib/R.java 1764 if (bundle->getExtraPackages() != NULL) { 1765 // Split on colon 1766 String8 libs(bundle->getExtraPackages()); 1767 char* packageString = strtok(libs.lockBuffer(libs.length()), ":"); 1768 while (packageString != NULL) { 1769 // Write the R.java file out with the correct package name 1770 err = writeResourceSymbols(bundle, assets, String8(packageString), true); 1771 if (err < 0) { 1772 goto bail; 1773 } 1774 packageString = strtok(NULL, ":"); 1775 } 1776 libs.unlockBuffer(); 1777 } 1778 } else { 1779 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false); 1780 if (err < 0) { 1781 goto bail; 1782 } 1783 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true); 1784 if (err < 0) { 1785 goto bail; 1786 } 1787 } 1788 1789 // Write out the ProGuard file 1790 err = writeProguardFile(bundle, assets); 1791 if (err < 0) { 1792 goto bail; 1793 } 1794 1795 // Write the apk 1796 if (outputAPKFile) { 1797 err = writeAPK(bundle, assets, String8(outputAPKFile)); 1798 if (err != NO_ERROR) { 1799 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile); 1800 goto bail; 1801 } 1802 } 1803 1804 // If we've been asked to generate a dependency file, we need to finish up here. 1805 // the writeResourceSymbols and writeAPK functions have already written the target 1806 // half of the dependency file, now we need to write the prerequisites. (files that 1807 // the R.java file or .ap_ file depend on) 1808 if (bundle->getGenDependencies()) { 1809 // Now that writeResourceSymbols or writeAPK has taken care of writing 1810 // the targets to our dependency file, we'll write the prereqs 1811 fp = fopen(dependencyFile, "a+"); 1812 fprintf(fp, " : "); 1813 bool includeRaw = (outputAPKFile != NULL); 1814 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw); 1815 // Also manually add the AndroidManifeset since it's not under res/ or assets/ 1816 // and therefore was not added to our pathstores during slurping 1817 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile()); 1818 fclose(fp); 1819 } 1820 1821 retVal = 0; 1822 bail: 1823 if (SourcePos::hasErrors()) { 1824 SourcePos::printErrors(stderr); 1825 } 1826 return retVal; 1827 } 1828 1829 /* 1830 * Do PNG Crunching 1831 * PRECONDITIONS 1832 * -S flag points to a source directory containing drawable* folders 1833 * -C flag points to destination directory. The folder structure in the 1834 * source directory will be mirrored to the destination (cache) directory 1835 * 1836 * POSTCONDITIONS 1837 * Destination directory will be updated to match the PNG files in 1838 * the source directory. 1839 */ 1840 int doCrunch(Bundle* bundle) 1841 { 1842 fprintf(stdout, "Crunching PNG Files in "); 1843 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]); 1844 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir()); 1845 1846 updatePreProcessedCache(bundle); 1847 1848 return NO_ERROR; 1849 } 1850 1851 /* 1852 * Do PNG Crunching on a single flag 1853 * -i points to a single png file 1854 * -o points to a single png output file 1855 */ 1856 int doSingleCrunch(Bundle* bundle) 1857 { 1858 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile()); 1859 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile()); 1860 1861 String8 input(bundle->getSingleCrunchInputFile()); 1862 String8 output(bundle->getSingleCrunchOutputFile()); 1863 1864 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) { 1865 // we can't return the status_t as it gets truncate to the lower 8 bits. 1866 return 42; 1867 } 1868 1869 return NO_ERROR; 1870 } 1871 1872 char CONSOLE_DATA[2925] = { 1873 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1874 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1875 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1876 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 1877 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63, 1878 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83, 1879 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1880 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1881 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81, 1882 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32, 1883 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 1884 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1885 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59, 1886 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1887 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1888 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 1889 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32, 1890 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1891 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1892 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87, 1893 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32, 1894 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 1895 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 1896 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58, 1897 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1898 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1899 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81, 1900 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32, 1901 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 1902 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1903 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59, 1904 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1905 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1906 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59, 1907 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32, 1908 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1909 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1910 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81, 1911 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 1912 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 1913 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1914 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81, 1915 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1916 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1917 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109, 1918 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32, 1919 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 1920 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59, 1921 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59, 1922 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1923 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59, 1924 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46, 1925 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32, 1926 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1927 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 1928 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81, 1929 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32, 1930 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81, 1931 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 1932 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58, 1933 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32, 1934 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59, 1935 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37, 1936 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59, 1937 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 1938 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59, 1939 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96, 1940 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 1941 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32, 1942 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61, 1943 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59, 1944 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32, 1945 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1946 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119, 1947 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 1948 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10, 1949 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1950 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81, 1951 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41, 1952 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 1953 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81, 1954 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 1955 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32, 1956 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1957 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 1958 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 1959 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 1960 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 1961 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 1962 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1963 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1964 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81, 1965 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32, 1966 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1967 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1968 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 1969 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1970 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 1971 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106, 1972 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59, 1973 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1974 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1975 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81, 1976 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 1977 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 1978 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1979 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59, 1980 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1981 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1982 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81, 1983 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32, 1984 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1985 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1986 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81, 1987 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1988 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 1989 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 1990 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59, 1991 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1992 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 1993 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33, 1994 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 1995 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 1996 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 1997 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59, 1998 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 1999 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 2000 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95, 2001 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59, 2002 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2003 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2004 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45, 2005 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32, 2006 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 2007 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59, 2008 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59, 2009 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2010 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2011 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32, 2012 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32, 2013 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 2014 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2015 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61, 2016 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2017 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 2018 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 2019 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59, 2020 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2021 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2022 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32, 2023 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32, 2024 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 2025 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61, 2026 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 2027 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2028 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2029 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32, 2030 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32, 2031 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2032 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2033 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2034 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2035 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10 2036 }; 2037