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