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