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