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