Command.cpp revision a0b46c9c441f017a2008ca8ee2c864987465996b
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 String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree, 295 uint32_t attrRes, String8* outError) 296{ 297 ssize_t idx = indexOfAttribute(tree, attrRes); 298 if (idx < 0) { 299 return String8(); 300 } 301 Res_value value; 302 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 303 if (value.dataType == Res_value::TYPE_STRING) { 304 size_t len; 305 const uint16_t* str = tree.getAttributeStringValue(idx, &len); 306 return str ? String8(str, len) : String8(); 307 } 308 resTable->resolveReference(&value, 0); 309 if (value.dataType != Res_value::TYPE_STRING) { 310 if (outError != NULL) *outError = "attribute is not a string value"; 311 return String8(); 312 } 313 } 314 size_t len; 315 const Res_value* value2 = &value; 316 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len); 317 return str ? String8(str, len) : String8(); 318} 319 320// These are attribute resource constants for the platform, as found 321// in android.R.attr 322enum { 323 NAME_ATTR = 0x01010003, 324 VERSION_CODE_ATTR = 0x0101021b, 325 VERSION_NAME_ATTR = 0x0101021c, 326 LABEL_ATTR = 0x01010001, 327 ICON_ATTR = 0x01010002, 328 MIN_SDK_VERSION_ATTR = 0x0101020c, 329 MAX_SDK_VERSION_ATTR = 0x01010271, 330 REQ_TOUCH_SCREEN_ATTR = 0x01010227, 331 REQ_KEYBOARD_TYPE_ATTR = 0x01010228, 332 REQ_HARD_KEYBOARD_ATTR = 0x01010229, 333 REQ_NAVIGATION_ATTR = 0x0101022a, 334 REQ_FIVE_WAY_NAV_ATTR = 0x01010232, 335 TARGET_SDK_VERSION_ATTR = 0x01010270, 336 TEST_ONLY_ATTR = 0x01010272, 337 ANY_DENSITY_ATTR = 0x0101026c, 338 GL_ES_VERSION_ATTR = 0x01010281, 339 SMALL_SCREEN_ATTR = 0x01010284, 340 NORMAL_SCREEN_ATTR = 0x01010285, 341 LARGE_SCREEN_ATTR = 0x01010286, 342 XLARGE_SCREEN_ATTR = 0x010102bf, 343 REQUIRED_ATTR = 0x0101028e, 344 SCREEN_SIZE_ATTR = 0x010102ca, 345 SCREEN_DENSITY_ATTR = 0x010102cb, 346}; 347 348const char *getComponentName(String8 &pkgName, String8 &componentName) { 349 ssize_t idx = componentName.find("."); 350 String8 retStr(pkgName); 351 if (idx == 0) { 352 retStr += componentName; 353 } else if (idx < 0) { 354 retStr += "."; 355 retStr += componentName; 356 } else { 357 return componentName.string(); 358 } 359 return retStr.string(); 360} 361 362static void printCompatibleScreens(ResXMLTree& tree) { 363 size_t len; 364 ResXMLTree::event_code_t code; 365 int depth = 0; 366 bool first = true; 367 printf("compatible-screens:"); 368 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 369 if (code == ResXMLTree::END_TAG) { 370 depth--; 371 if (depth < 0) { 372 break; 373 } 374 continue; 375 } 376 if (code != ResXMLTree::START_TAG) { 377 continue; 378 } 379 depth++; 380 String8 tag(tree.getElementName(&len)); 381 if (tag == "screen") { 382 int32_t screenSize = getIntegerAttribute(tree, 383 SCREEN_SIZE_ATTR, NULL, -1); 384 int32_t screenDensity = getIntegerAttribute(tree, 385 SCREEN_DENSITY_ATTR, NULL, -1); 386 if (screenSize > 0 && screenDensity > 0) { 387 if (!first) { 388 printf(","); 389 } 390 first = false; 391 printf("'%d/%d'", screenSize, screenDensity); 392 } 393 } 394 } 395 printf("\n"); 396} 397 398/* 399 * Handle the "dump" command, to extract select data from an archive. 400 */ 401int doDump(Bundle* bundle) 402{ 403 status_t result = UNKNOWN_ERROR; 404 Asset* asset = NULL; 405 406 if (bundle->getFileSpecCount() < 1) { 407 fprintf(stderr, "ERROR: no dump option specified\n"); 408 return 1; 409 } 410 411 if (bundle->getFileSpecCount() < 2) { 412 fprintf(stderr, "ERROR: no dump file specified\n"); 413 return 1; 414 } 415 416 const char* option = bundle->getFileSpecEntry(0); 417 const char* filename = bundle->getFileSpecEntry(1); 418 419 AssetManager assets; 420 void* assetsCookie; 421 if (!assets.addAssetPath(String8(filename), &assetsCookie)) { 422 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n"); 423 return 1; 424 } 425 426 const ResTable& res = assets.getResources(false); 427 if (&res == NULL) { 428 fprintf(stderr, "ERROR: dump failed because no resource table was found\n"); 429 goto bail; 430 } 431 432 if (strcmp("resources", option) == 0) { 433#ifndef HAVE_ANDROID_OS 434 res.print(bundle->getValues()); 435#endif 436 } else if (strcmp("xmltree", option) == 0) { 437 if (bundle->getFileSpecCount() < 3) { 438 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n"); 439 goto bail; 440 } 441 442 for (int i=2; i<bundle->getFileSpecCount(); i++) { 443 const char* resname = bundle->getFileSpecEntry(i); 444 ResXMLTree tree; 445 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER); 446 if (asset == NULL) { 447 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname); 448 goto bail; 449 } 450 451 if (tree.setTo(asset->getBuffer(true), 452 asset->getLength()) != NO_ERROR) { 453 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname); 454 goto bail; 455 } 456 tree.restart(); 457 printXMLBlock(&tree); 458 tree.uninit(); 459 delete asset; 460 asset = NULL; 461 } 462 463 } else if (strcmp("xmlstrings", option) == 0) { 464 if (bundle->getFileSpecCount() < 3) { 465 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n"); 466 goto bail; 467 } 468 469 for (int i=2; i<bundle->getFileSpecCount(); i++) { 470 const char* resname = bundle->getFileSpecEntry(i); 471 ResXMLTree tree; 472 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER); 473 if (asset == NULL) { 474 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname); 475 goto bail; 476 } 477 478 if (tree.setTo(asset->getBuffer(true), 479 asset->getLength()) != NO_ERROR) { 480 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname); 481 goto bail; 482 } 483 printStringPool(&tree.getStrings()); 484 delete asset; 485 asset = NULL; 486 } 487 488 } else { 489 ResXMLTree tree; 490 asset = assets.openNonAsset("AndroidManifest.xml", 491 Asset::ACCESS_BUFFER); 492 if (asset == NULL) { 493 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n"); 494 goto bail; 495 } 496 497 if (tree.setTo(asset->getBuffer(true), 498 asset->getLength()) != NO_ERROR) { 499 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n"); 500 goto bail; 501 } 502 tree.restart(); 503 504 if (strcmp("permissions", option) == 0) { 505 size_t len; 506 ResXMLTree::event_code_t code; 507 int depth = 0; 508 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 509 if (code == ResXMLTree::END_TAG) { 510 depth--; 511 continue; 512 } 513 if (code != ResXMLTree::START_TAG) { 514 continue; 515 } 516 depth++; 517 String8 tag(tree.getElementName(&len)); 518 //printf("Depth %d tag %s\n", depth, tag.string()); 519 if (depth == 1) { 520 if (tag != "manifest") { 521 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 522 goto bail; 523 } 524 String8 pkg = getAttribute(tree, NULL, "package", NULL); 525 printf("package: %s\n", pkg.string()); 526 } else if (depth == 2 && tag == "permission") { 527 String8 error; 528 String8 name = getAttribute(tree, NAME_ATTR, &error); 529 if (error != "") { 530 fprintf(stderr, "ERROR: %s\n", error.string()); 531 goto bail; 532 } 533 printf("permission: %s\n", name.string()); 534 } else if (depth == 2 && tag == "uses-permission") { 535 String8 error; 536 String8 name = getAttribute(tree, NAME_ATTR, &error); 537 if (error != "") { 538 fprintf(stderr, "ERROR: %s\n", error.string()); 539 goto bail; 540 } 541 printf("uses-permission: %s\n", name.string()); 542 } 543 } 544 } else if (strcmp("badging", option) == 0) { 545 size_t len; 546 ResXMLTree::event_code_t code; 547 int depth = 0; 548 String8 error; 549 bool withinActivity = false; 550 bool isMainActivity = false; 551 bool isLauncherActivity = false; 552 bool isSearchable = false; 553 bool withinApplication = false; 554 bool withinReceiver = false; 555 bool withinService = false; 556 bool withinIntentFilter = false; 557 bool hasMainActivity = false; 558 bool hasOtherActivities = false; 559 bool hasOtherReceivers = false; 560 bool hasOtherServices = false; 561 bool hasWallpaperService = false; 562 bool hasImeService = false; 563 bool hasWidgetReceivers = false; 564 bool hasIntentFilter = false; 565 bool actMainActivity = false; 566 bool actWidgetReceivers = false; 567 bool actImeService = false; 568 bool actWallpaperService = false; 569 570 // This next group of variables is used to implement a group of 571 // backward-compatibility heuristics necessitated by the addition of 572 // some new uses-feature constants in 2.1 and 2.2. In most cases, the 573 // heuristic is "if an app requests a permission but doesn't explicitly 574 // request the corresponding <uses-feature>, presume it's there anyway". 575 bool specCameraFeature = false; // camera-related 576 bool specCameraAutofocusFeature = false; 577 bool reqCameraAutofocusFeature = false; 578 bool reqCameraFlashFeature = false; 579 bool hasCameraPermission = false; 580 bool specLocationFeature = false; // location-related 581 bool specNetworkLocFeature = false; 582 bool reqNetworkLocFeature = false; 583 bool specGpsFeature = false; 584 bool reqGpsFeature = false; 585 bool hasMockLocPermission = false; 586 bool hasCoarseLocPermission = false; 587 bool hasGpsPermission = false; 588 bool hasGeneralLocPermission = false; 589 bool specBluetoothFeature = false; // Bluetooth API-related 590 bool hasBluetoothPermission = false; 591 bool specMicrophoneFeature = false; // microphone-related 592 bool hasRecordAudioPermission = false; 593 bool specWiFiFeature = false; 594 bool hasWiFiPermission = false; 595 bool specTelephonyFeature = false; // telephony-related 596 bool reqTelephonySubFeature = false; 597 bool hasTelephonyPermission = false; 598 bool specTouchscreenFeature = false; // touchscreen-related 599 bool specMultitouchFeature = false; 600 bool reqDistinctMultitouchFeature = false; 601 // 2.2 also added some other features that apps can request, but that 602 // have no corresponding permission, so we cannot implement any 603 // back-compatibility heuristic for them. The below are thus unnecessary 604 // (but are retained here for documentary purposes.) 605 //bool specCompassFeature = false; 606 //bool specAccelerometerFeature = false; 607 //bool specProximityFeature = false; 608 //bool specAmbientLightFeature = false; 609 //bool specLiveWallpaperFeature = false; 610 611 int targetSdk = 0; 612 int smallScreen = 1; 613 int normalScreen = 1; 614 int largeScreen = 1; 615 int xlargeScreen = 1; 616 int anyDensity = 1; 617 String8 pkg; 618 String8 activityName; 619 String8 activityLabel; 620 String8 activityIcon; 621 String8 receiverName; 622 String8 serviceName; 623 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 624 if (code == ResXMLTree::END_TAG) { 625 depth--; 626 if (depth < 2) { 627 withinApplication = false; 628 } else if (depth < 3) { 629 if (withinActivity && isMainActivity && isLauncherActivity) { 630 const char *aName = getComponentName(pkg, activityName); 631 if (aName != NULL) { 632 printf("launchable activity name='%s'", aName); 633 } 634 printf("label='%s' icon='%s'\n", 635 activityLabel.string(), 636 activityIcon.string()); 637 } 638 if (!hasIntentFilter) { 639 hasOtherActivities |= withinActivity; 640 hasOtherReceivers |= withinReceiver; 641 hasOtherServices |= withinService; 642 } 643 withinActivity = false; 644 withinService = false; 645 withinReceiver = false; 646 hasIntentFilter = false; 647 isMainActivity = isLauncherActivity = false; 648 } else if (depth < 4) { 649 if (withinIntentFilter) { 650 if (withinActivity) { 651 hasMainActivity |= actMainActivity; 652 hasOtherActivities |= !actMainActivity; 653 } else if (withinReceiver) { 654 hasWidgetReceivers |= actWidgetReceivers; 655 hasOtherReceivers |= !actWidgetReceivers; 656 } else if (withinService) { 657 hasImeService |= actImeService; 658 hasWallpaperService |= actWallpaperService; 659 hasOtherServices |= (!actImeService && !actWallpaperService); 660 } 661 } 662 withinIntentFilter = false; 663 } 664 continue; 665 } 666 if (code != ResXMLTree::START_TAG) { 667 continue; 668 } 669 depth++; 670 String8 tag(tree.getElementName(&len)); 671 //printf("Depth %d, %s\n", depth, tag.string()); 672 if (depth == 1) { 673 if (tag != "manifest") { 674 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 675 goto bail; 676 } 677 pkg = getAttribute(tree, NULL, "package", NULL); 678 printf("package: name='%s' ", pkg.string()); 679 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error); 680 if (error != "") { 681 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string()); 682 goto bail; 683 } 684 if (versionCode > 0) { 685 printf("versionCode='%d' ", versionCode); 686 } else { 687 printf("versionCode='' "); 688 } 689 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error); 690 if (error != "") { 691 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string()); 692 goto bail; 693 } 694 printf("versionName='%s'\n", versionName.string()); 695 } else if (depth == 2) { 696 withinApplication = false; 697 if (tag == "application") { 698 withinApplication = true; 699 String8 label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); 700 if (error != "") { 701 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string()); 702 goto bail; 703 } 704 printf("application: label='%s' ", label.string()); 705 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 706 if (error != "") { 707 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string()); 708 goto bail; 709 } 710 printf("icon='%s'\n", icon.string()); 711 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0); 712 if (error != "") { 713 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string()); 714 goto bail; 715 } 716 if (testOnly != 0) { 717 printf("testOnly='%d'\n", testOnly); 718 } 719 } else if (tag == "uses-sdk") { 720 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error); 721 if (error != "") { 722 error = ""; 723 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error); 724 if (error != "") { 725 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n", 726 error.string()); 727 goto bail; 728 } 729 if (name == "Donut") targetSdk = 4; 730 printf("sdkVersion:'%s'\n", name.string()); 731 } else if (code != -1) { 732 targetSdk = code; 733 printf("sdkVersion:'%d'\n", code); 734 } 735 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1); 736 if (code != -1) { 737 printf("maxSdkVersion:'%d'\n", code); 738 } 739 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error); 740 if (error != "") { 741 error = ""; 742 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error); 743 if (error != "") { 744 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n", 745 error.string()); 746 goto bail; 747 } 748 if (name == "Donut" && targetSdk < 4) targetSdk = 4; 749 printf("targetSdkVersion:'%s'\n", name.string()); 750 } else if (code != -1) { 751 if (targetSdk < code) { 752 targetSdk = code; 753 } 754 printf("targetSdkVersion:'%d'\n", code); 755 } 756 } else if (tag == "uses-configuration") { 757 int32_t reqTouchScreen = getIntegerAttribute(tree, 758 REQ_TOUCH_SCREEN_ATTR, NULL, 0); 759 int32_t reqKeyboardType = getIntegerAttribute(tree, 760 REQ_KEYBOARD_TYPE_ATTR, NULL, 0); 761 int32_t reqHardKeyboard = getIntegerAttribute(tree, 762 REQ_HARD_KEYBOARD_ATTR, NULL, 0); 763 int32_t reqNavigation = getIntegerAttribute(tree, 764 REQ_NAVIGATION_ATTR, NULL, 0); 765 int32_t reqFiveWayNav = getIntegerAttribute(tree, 766 REQ_FIVE_WAY_NAV_ATTR, NULL, 0); 767 printf("uses-configuration:"); 768 if (reqTouchScreen != 0) { 769 printf(" reqTouchScreen='%d'", reqTouchScreen); 770 } 771 if (reqKeyboardType != 0) { 772 printf(" reqKeyboardType='%d'", reqKeyboardType); 773 } 774 if (reqHardKeyboard != 0) { 775 printf(" reqHardKeyboard='%d'", reqHardKeyboard); 776 } 777 if (reqNavigation != 0) { 778 printf(" reqNavigation='%d'", reqNavigation); 779 } 780 if (reqFiveWayNav != 0) { 781 printf(" reqFiveWayNav='%d'", reqFiveWayNav); 782 } 783 printf("\n"); 784 } else if (tag == "supports-screens") { 785 smallScreen = getIntegerAttribute(tree, 786 SMALL_SCREEN_ATTR, NULL, 1); 787 normalScreen = getIntegerAttribute(tree, 788 NORMAL_SCREEN_ATTR, NULL, 1); 789 largeScreen = getIntegerAttribute(tree, 790 LARGE_SCREEN_ATTR, NULL, 1); 791 xlargeScreen = getIntegerAttribute(tree, 792 XLARGE_SCREEN_ATTR, NULL, 1); 793 anyDensity = getIntegerAttribute(tree, 794 ANY_DENSITY_ATTR, NULL, 1); 795 } else if (tag == "uses-feature") { 796 String8 name = getAttribute(tree, NAME_ATTR, &error); 797 798 if (name != "" && error == "") { 799 int req = getIntegerAttribute(tree, 800 REQUIRED_ATTR, NULL, 1); 801 802 if (name == "android.hardware.camera") { 803 specCameraFeature = true; 804 } else if (name == "android.hardware.camera.autofocus") { 805 // these have no corresponding permission to check for, 806 // but should imply the foundational camera permission 807 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req; 808 specCameraAutofocusFeature = true; 809 } else if (req && (name == "android.hardware.camera.flash")) { 810 // these have no corresponding permission to check for, 811 // but should imply the foundational camera permission 812 reqCameraFlashFeature = true; 813 } else if (name == "android.hardware.location") { 814 specLocationFeature = true; 815 } else if (name == "android.hardware.location.network") { 816 specNetworkLocFeature = true; 817 reqNetworkLocFeature = reqNetworkLocFeature || req; 818 } else if (name == "android.hardware.location.gps") { 819 specGpsFeature = true; 820 reqGpsFeature = reqGpsFeature || req; 821 } else if (name == "android.hardware.bluetooth") { 822 specBluetoothFeature = true; 823 } else if (name == "android.hardware.touchscreen") { 824 specTouchscreenFeature = true; 825 } else if (name == "android.hardware.touchscreen.multitouch") { 826 specMultitouchFeature = true; 827 } else if (name == "android.hardware.touchscreen.multitouch.distinct") { 828 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req; 829 } else if (name == "android.hardware.microphone") { 830 specMicrophoneFeature = true; 831 } else if (name == "android.hardware.wifi") { 832 specWiFiFeature = true; 833 } else if (name == "android.hardware.telephony") { 834 specTelephonyFeature = true; 835 } else if (req && (name == "android.hardware.telephony.gsm" || 836 name == "android.hardware.telephony.cdma")) { 837 // these have no corresponding permission to check for, 838 // but should imply the foundational telephony permission 839 reqTelephonySubFeature = true; 840 } 841 printf("uses-feature%s:'%s'\n", 842 req ? "" : "-not-required", name.string()); 843 } else { 844 int vers = getIntegerAttribute(tree, 845 GL_ES_VERSION_ATTR, &error); 846 if (error == "") { 847 printf("uses-gl-es:'0x%x'\n", vers); 848 } 849 } 850 } else if (tag == "uses-permission") { 851 String8 name = getAttribute(tree, NAME_ATTR, &error); 852 if (name != "" && error == "") { 853 if (name == "android.permission.CAMERA") { 854 hasCameraPermission = true; 855 } else if (name == "android.permission.ACCESS_FINE_LOCATION") { 856 hasGpsPermission = true; 857 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") { 858 hasMockLocPermission = true; 859 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") { 860 hasCoarseLocPermission = true; 861 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" || 862 name == "android.permission.INSTALL_LOCATION_PROVIDER") { 863 hasGeneralLocPermission = true; 864 } else if (name == "android.permission.BLUETOOTH" || 865 name == "android.permission.BLUETOOTH_ADMIN") { 866 hasBluetoothPermission = true; 867 } else if (name == "android.permission.RECORD_AUDIO") { 868 hasRecordAudioPermission = true; 869 } else if (name == "android.permission.ACCESS_WIFI_STATE" || 870 name == "android.permission.CHANGE_WIFI_STATE" || 871 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") { 872 hasWiFiPermission = true; 873 } else if (name == "android.permission.CALL_PHONE" || 874 name == "android.permission.CALL_PRIVILEGED" || 875 name == "android.permission.MODIFY_PHONE_STATE" || 876 name == "android.permission.PROCESS_OUTGOING_CALLS" || 877 name == "android.permission.READ_SMS" || 878 name == "android.permission.RECEIVE_SMS" || 879 name == "android.permission.RECEIVE_MMS" || 880 name == "android.permission.RECEIVE_WAP_PUSH" || 881 name == "android.permission.SEND_SMS" || 882 name == "android.permission.WRITE_APN_SETTINGS" || 883 name == "android.permission.WRITE_SMS") { 884 hasTelephonyPermission = true; 885 } 886 printf("uses-permission:'%s'\n", name.string()); 887 } else { 888 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 889 error.string()); 890 goto bail; 891 } 892 } else if (tag == "uses-package") { 893 String8 name = getAttribute(tree, NAME_ATTR, &error); 894 if (name != "" && error == "") { 895 printf("uses-package:'%s'\n", name.string()); 896 } else { 897 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 898 error.string()); 899 goto bail; 900 } 901 } else if (tag == "original-package") { 902 String8 name = getAttribute(tree, NAME_ATTR, &error); 903 if (name != "" && error == "") { 904 printf("original-package:'%s'\n", name.string()); 905 } else { 906 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 907 error.string()); 908 goto bail; 909 } 910 } else if (tag == "uses-gl-texture") { 911 String8 name = getAttribute(tree, NAME_ATTR, &error); 912 if (name != "" && error == "") { 913 printf("uses-gl-texture:'%s'\n", name.string()); 914 } else { 915 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 916 error.string()); 917 goto bail; 918 } 919 } else if (tag == "compatible-screens") { 920 printCompatibleScreens(tree); 921 depth--; 922 } 923 } else if (depth == 3 && withinApplication) { 924 withinActivity = false; 925 withinReceiver = false; 926 withinService = false; 927 hasIntentFilter = false; 928 if(tag == "activity") { 929 withinActivity = true; 930 activityName = getAttribute(tree, NAME_ATTR, &error); 931 if (error != "") { 932 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); 933 goto bail; 934 } 935 936 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); 937 if (error != "") { 938 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string()); 939 goto bail; 940 } 941 942 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 943 if (error != "") { 944 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string()); 945 goto bail; 946 } 947 } else if (tag == "uses-library") { 948 String8 libraryName = getAttribute(tree, NAME_ATTR, &error); 949 if (error != "") { 950 fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string()); 951 goto bail; 952 } 953 int req = getIntegerAttribute(tree, 954 REQUIRED_ATTR, NULL, 1); 955 printf("uses-library%s:'%s'\n", 956 req ? "" : "-not-required", libraryName.string()); 957 } else if (tag == "receiver") { 958 withinReceiver = true; 959 receiverName = getAttribute(tree, NAME_ATTR, &error); 960 961 if (error != "") { 962 fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string()); 963 goto bail; 964 } 965 } else if (tag == "service") { 966 withinService = true; 967 serviceName = getAttribute(tree, NAME_ATTR, &error); 968 969 if (error != "") { 970 fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string()); 971 goto bail; 972 } 973 } 974 } else if ((depth == 4) && (tag == "intent-filter")) { 975 hasIntentFilter = true; 976 withinIntentFilter = true; 977 actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false; 978 } else if ((depth == 5) && withinIntentFilter){ 979 String8 action; 980 if (tag == "action") { 981 action = getAttribute(tree, NAME_ATTR, &error); 982 if (error != "") { 983 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); 984 goto bail; 985 } 986 if (withinActivity) { 987 if (action == "android.intent.action.MAIN") { 988 isMainActivity = true; 989 actMainActivity = true; 990 } 991 } else if (withinReceiver) { 992 if (action == "android.appwidget.action.APPWIDGET_UPDATE") { 993 actWidgetReceivers = true; 994 } 995 } else if (withinService) { 996 if (action == "android.view.InputMethod") { 997 actImeService = true; 998 } else if (action == "android.service.wallpaper.WallpaperService") { 999 actWallpaperService = true; 1000 } 1001 } 1002 if (action == "android.intent.action.SEARCH") { 1003 isSearchable = true; 1004 } 1005 } 1006 1007 if (tag == "category") { 1008 String8 category = getAttribute(tree, NAME_ATTR, &error); 1009 if (error != "") { 1010 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string()); 1011 goto bail; 1012 } 1013 if (withinActivity) { 1014 if (category == "android.intent.category.LAUNCHER") { 1015 isLauncherActivity = true; 1016 } 1017 } 1018 } 1019 } 1020 } 1021 1022 /* The following blocks handle printing "inferred" uses-features, based 1023 * on whether related features or permissions are used by the app. 1024 * Note that the various spec*Feature variables denote whether the 1025 * relevant tag was *present* in the AndroidManfest, not that it was 1026 * present and set to true. 1027 */ 1028 // Camera-related back-compatibility logic 1029 if (!specCameraFeature) { 1030 if (reqCameraFlashFeature || reqCameraAutofocusFeature) { 1031 // if app requested a sub-feature (autofocus or flash) and didn't 1032 // request the base camera feature, we infer that it meant to 1033 printf("uses-feature:'android.hardware.camera'\n"); 1034 } else if (hasCameraPermission) { 1035 // if app wants to use camera but didn't request the feature, we infer 1036 // that it meant to, and further that it wants autofocus 1037 // (which was the 1.0 - 1.5 behavior) 1038 printf("uses-feature:'android.hardware.camera'\n"); 1039 if (!specCameraAutofocusFeature) { 1040 printf("uses-feature:'android.hardware.camera.autofocus'\n"); 1041 } 1042 } 1043 } 1044 1045 // Location-related back-compatibility logic 1046 if (!specLocationFeature && 1047 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission || 1048 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) { 1049 // if app either takes a location-related permission or requests one of the 1050 // sub-features, we infer that it also meant to request the base location feature 1051 printf("uses-feature:'android.hardware.location'\n"); 1052 } 1053 if (!specGpsFeature && hasGpsPermission) { 1054 // if app takes GPS (FINE location) perm but does not request the GPS 1055 // feature, we infer that it meant to 1056 printf("uses-feature:'android.hardware.location.gps'\n"); 1057 } 1058 if (!specNetworkLocFeature && hasCoarseLocPermission) { 1059 // if app takes Network location (COARSE location) perm but does not request the 1060 // network location feature, we infer that it meant to 1061 printf("uses-feature:'android.hardware.location.network'\n"); 1062 } 1063 1064 // Bluetooth-related compatibility logic 1065 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) { 1066 // if app takes a Bluetooth permission but does not request the Bluetooth 1067 // feature, we infer that it meant to 1068 printf("uses-feature:'android.hardware.bluetooth'\n"); 1069 } 1070 1071 // Microphone-related compatibility logic 1072 if (!specMicrophoneFeature && hasRecordAudioPermission) { 1073 // if app takes the record-audio permission but does not request the microphone 1074 // feature, we infer that it meant to 1075 printf("uses-feature:'android.hardware.microphone'\n"); 1076 } 1077 1078 // WiFi-related compatibility logic 1079 if (!specWiFiFeature && hasWiFiPermission) { 1080 // if app takes one of the WiFi permissions but does not request the WiFi 1081 // feature, we infer that it meant to 1082 printf("uses-feature:'android.hardware.wifi'\n"); 1083 } 1084 1085 // Telephony-related compatibility logic 1086 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) { 1087 // if app takes one of the telephony permissions or requests a sub-feature but 1088 // does not request the base telephony feature, we infer that it meant to 1089 printf("uses-feature:'android.hardware.telephony'\n"); 1090 } 1091 1092 // Touchscreen-related back-compatibility logic 1093 if (!specTouchscreenFeature) { // not a typo! 1094 // all apps are presumed to require a touchscreen, unless they explicitly say 1095 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/> 1096 // Note that specTouchscreenFeature is true if the tag is present, regardless 1097 // of whether its value is true or false, so this is safe 1098 printf("uses-feature:'android.hardware.touchscreen'\n"); 1099 } 1100 if (!specMultitouchFeature && reqDistinctMultitouchFeature) { 1101 // if app takes one of the telephony permissions or requests a sub-feature but 1102 // does not request the base telephony feature, we infer that it meant to 1103 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n"); 1104 } 1105 1106 if (hasMainActivity) { 1107 printf("main\n"); 1108 } 1109 if (hasWidgetReceivers) { 1110 printf("app-widget\n"); 1111 } 1112 if (hasImeService) { 1113 printf("ime\n"); 1114 } 1115 if (hasWallpaperService) { 1116 printf("wallpaper\n"); 1117 } 1118 if (hasOtherActivities) { 1119 printf("other-activities\n"); 1120 } 1121 if (isSearchable) { 1122 printf("search\n"); 1123 } 1124 if (hasOtherReceivers) { 1125 printf("other-receivers\n"); 1126 } 1127 if (hasOtherServices) { 1128 printf("other-services\n"); 1129 } 1130 1131 // Determine default values for any unspecified screen sizes, 1132 // based on the target SDK of the package. As of 4 (donut) 1133 // the screen size support was introduced, so all default to 1134 // enabled. 1135 if (smallScreen > 0) { 1136 smallScreen = targetSdk >= 4 ? -1 : 0; 1137 } 1138 if (normalScreen > 0) { 1139 normalScreen = -1; 1140 } 1141 if (largeScreen > 0) { 1142 largeScreen = targetSdk >= 4 ? -1 : 0; 1143 } 1144 if (xlargeScreen > 0) { 1145 // Introduced in Honeycomb. 1146 xlargeScreen = targetSdk >= 10 ? -1 : 0; 1147 } 1148 if (anyDensity > 0) { 1149 anyDensity = targetSdk >= 4 ? -1 : 0; 1150 } 1151 printf("supports-screens:"); 1152 if (smallScreen != 0) printf(" 'small'"); 1153 if (normalScreen != 0) printf(" 'normal'"); 1154 if (largeScreen != 0) printf(" 'large'"); 1155 if (xlargeScreen != 0) printf(" 'xlarge'"); 1156 printf("\n"); 1157 1158 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false"); 1159 1160 printf("locales:"); 1161 Vector<String8> locales; 1162 res.getLocales(&locales); 1163 const size_t NL = locales.size(); 1164 for (size_t i=0; i<NL; i++) { 1165 const char* localeStr = locales[i].string(); 1166 if (localeStr == NULL || strlen(localeStr) == 0) { 1167 localeStr = "--_--"; 1168 } 1169 printf(" '%s'", localeStr); 1170 } 1171 printf("\n"); 1172 1173 Vector<ResTable_config> configs; 1174 res.getConfigurations(&configs); 1175 SortedVector<int> densities; 1176 const size_t NC = configs.size(); 1177 for (size_t i=0; i<NC; i++) { 1178 int dens = configs[i].density; 1179 if (dens == 0) dens = 160; 1180 densities.add(dens); 1181 } 1182 1183 printf("densities:"); 1184 const size_t ND = densities.size(); 1185 for (size_t i=0; i<ND; i++) { 1186 printf(" '%d'", densities[i]); 1187 } 1188 printf("\n"); 1189 1190 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib"); 1191 if (dir != NULL) { 1192 if (dir->getFileCount() > 0) { 1193 printf("native-code:"); 1194 for (size_t i=0; i<dir->getFileCount(); i++) { 1195 printf(" '%s'", dir->getFileName(i).string()); 1196 } 1197 printf("\n"); 1198 } 1199 delete dir; 1200 } 1201 } else if (strcmp("configurations", option) == 0) { 1202 Vector<ResTable_config> configs; 1203 res.getConfigurations(&configs); 1204 const size_t N = configs.size(); 1205 for (size_t i=0; i<N; i++) { 1206 printf("%s\n", configs[i].toString().string()); 1207 } 1208 } else { 1209 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option); 1210 goto bail; 1211 } 1212 } 1213 1214 result = NO_ERROR; 1215 1216bail: 1217 if (asset) { 1218 delete asset; 1219 } 1220 return (result != NO_ERROR); 1221} 1222 1223 1224/* 1225 * Handle the "add" command, which wants to add files to a new or 1226 * pre-existing archive. 1227 */ 1228int doAdd(Bundle* bundle) 1229{ 1230 ZipFile* zip = NULL; 1231 status_t result = UNKNOWN_ERROR; 1232 const char* zipFileName; 1233 1234 if (bundle->getUpdate()) { 1235 /* avoid confusion */ 1236 fprintf(stderr, "ERROR: can't use '-u' with add\n"); 1237 goto bail; 1238 } 1239 1240 if (bundle->getFileSpecCount() < 1) { 1241 fprintf(stderr, "ERROR: must specify zip file name\n"); 1242 goto bail; 1243 } 1244 zipFileName = bundle->getFileSpecEntry(0); 1245 1246 if (bundle->getFileSpecCount() < 2) { 1247 fprintf(stderr, "NOTE: nothing to do\n"); 1248 goto bail; 1249 } 1250 1251 zip = openReadWrite(zipFileName, true); 1252 if (zip == NULL) { 1253 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName); 1254 goto bail; 1255 } 1256 1257 for (int i = 1; i < bundle->getFileSpecCount(); i++) { 1258 const char* fileName = bundle->getFileSpecEntry(i); 1259 1260 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) { 1261 printf(" '%s'... (from gzip)\n", fileName); 1262 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL); 1263 } else { 1264 if (bundle->getJunkPath()) { 1265 String8 storageName = String8(fileName).getPathLeaf(); 1266 printf(" '%s' as '%s'...\n", fileName, storageName.string()); 1267 result = zip->add(fileName, storageName.string(), 1268 bundle->getCompressionMethod(), NULL); 1269 } else { 1270 printf(" '%s'...\n", fileName); 1271 result = zip->add(fileName, bundle->getCompressionMethod(), NULL); 1272 } 1273 } 1274 if (result != NO_ERROR) { 1275 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName); 1276 if (result == NAME_NOT_FOUND) 1277 fprintf(stderr, ": file not found\n"); 1278 else if (result == ALREADY_EXISTS) 1279 fprintf(stderr, ": already exists in archive\n"); 1280 else 1281 fprintf(stderr, "\n"); 1282 goto bail; 1283 } 1284 } 1285 1286 result = NO_ERROR; 1287 1288bail: 1289 delete zip; 1290 return (result != NO_ERROR); 1291} 1292 1293 1294/* 1295 * Delete files from an existing archive. 1296 */ 1297int doRemove(Bundle* bundle) 1298{ 1299 ZipFile* zip = NULL; 1300 status_t result = UNKNOWN_ERROR; 1301 const char* zipFileName; 1302 1303 if (bundle->getFileSpecCount() < 1) { 1304 fprintf(stderr, "ERROR: must specify zip file name\n"); 1305 goto bail; 1306 } 1307 zipFileName = bundle->getFileSpecEntry(0); 1308 1309 if (bundle->getFileSpecCount() < 2) { 1310 fprintf(stderr, "NOTE: nothing to do\n"); 1311 goto bail; 1312 } 1313 1314 zip = openReadWrite(zipFileName, false); 1315 if (zip == NULL) { 1316 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n", 1317 zipFileName); 1318 goto bail; 1319 } 1320 1321 for (int i = 1; i < bundle->getFileSpecCount(); i++) { 1322 const char* fileName = bundle->getFileSpecEntry(i); 1323 ZipEntry* entry; 1324 1325 entry = zip->getEntryByName(fileName); 1326 if (entry == NULL) { 1327 printf(" '%s' NOT FOUND\n", fileName); 1328 continue; 1329 } 1330 1331 result = zip->remove(entry); 1332 1333 if (result != NO_ERROR) { 1334 fprintf(stderr, "Unable to delete '%s' from '%s'\n", 1335 bundle->getFileSpecEntry(i), zipFileName); 1336 goto bail; 1337 } 1338 } 1339 1340 /* update the archive */ 1341 zip->flush(); 1342 1343bail: 1344 delete zip; 1345 return (result != NO_ERROR); 1346} 1347 1348 1349/* 1350 * Package up an asset directory and associated application files. 1351 */ 1352int doPackage(Bundle* bundle) 1353{ 1354 const char* outputAPKFile; 1355 int retVal = 1; 1356 status_t err; 1357 sp<AaptAssets> assets; 1358 int N; 1359 1360 // -c zz_ZZ means do pseudolocalization 1361 ResourceFilter filter; 1362 err = filter.parse(bundle->getConfigurations()); 1363 if (err != NO_ERROR) { 1364 goto bail; 1365 } 1366 if (filter.containsPseudo()) { 1367 bundle->setPseudolocalize(true); 1368 } 1369 1370 N = bundle->getFileSpecCount(); 1371 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0 1372 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) { 1373 fprintf(stderr, "ERROR: no input files\n"); 1374 goto bail; 1375 } 1376 1377 outputAPKFile = bundle->getOutputAPKFile(); 1378 1379 // Make sure the filenames provided exist and are of the appropriate type. 1380 if (outputAPKFile) { 1381 FileType type; 1382 type = getFileType(outputAPKFile); 1383 if (type != kFileTypeNonexistent && type != kFileTypeRegular) { 1384 fprintf(stderr, 1385 "ERROR: output file '%s' exists but is not regular file\n", 1386 outputAPKFile); 1387 goto bail; 1388 } 1389 } 1390 1391 // Load the assets. 1392 assets = new AaptAssets(); 1393 err = assets->slurpFromArgs(bundle); 1394 if (err < 0) { 1395 goto bail; 1396 } 1397 1398 if (bundle->getVerbose()) { 1399 assets->print(); 1400 } 1401 1402 // If they asked for any files that need to be compiled, do so. 1403 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) { 1404 err = buildResources(bundle, assets); 1405 if (err != 0) { 1406 goto bail; 1407 } 1408 } 1409 1410 // At this point we've read everything and processed everything. From here 1411 // on out it's just writing output files. 1412 if (SourcePos::hasErrors()) { 1413 goto bail; 1414 } 1415 1416 // Write out R.java constants 1417 if (assets->getPackage() == assets->getSymbolsPrivatePackage()) { 1418 if (bundle->getCustomPackage() == NULL) { 1419 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true); 1420 } else { 1421 const String8 customPkg(bundle->getCustomPackage()); 1422 err = writeResourceSymbols(bundle, assets, customPkg, true); 1423 } 1424 if (err < 0) { 1425 goto bail; 1426 } 1427 } else { 1428 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false); 1429 if (err < 0) { 1430 goto bail; 1431 } 1432 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true); 1433 if (err < 0) { 1434 goto bail; 1435 } 1436 } 1437 1438 // Write out the ProGuard file 1439 err = writeProguardFile(bundle, assets); 1440 if (err < 0) { 1441 goto bail; 1442 } 1443 1444 // Write the apk 1445 if (outputAPKFile) { 1446 err = writeAPK(bundle, assets, String8(outputAPKFile)); 1447 if (err != NO_ERROR) { 1448 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile); 1449 goto bail; 1450 } 1451 } 1452 1453 retVal = 0; 1454bail: 1455 if (SourcePos::hasErrors()) { 1456 SourcePos::printErrors(stderr); 1457 } 1458 return retVal; 1459} 1460