Command.cpp revision 75c498426a17b47d445711bb9e36794b05876a56
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.h> 12#include <utils/ZipFile.h> 13 14#include <fcntl.h> 15#include <errno.h> 16 17using namespace android; 18 19/* 20 * Show version info. All the cool kids do it. 21 */ 22int doVersion(Bundle* bundle) 23{ 24 if (bundle->getFileSpecCount() != 0) 25 printf("(ignoring extra arguments)\n"); 26 printf("Android Asset Packaging Tool, v0.2\n"); 27 28 return 0; 29} 30 31 32/* 33 * Open the file read only. The call fails if the file doesn't exist. 34 * 35 * Returns NULL on failure. 36 */ 37ZipFile* openReadOnly(const char* fileName) 38{ 39 ZipFile* zip; 40 status_t result; 41 42 zip = new ZipFile; 43 result = zip->open(fileName, ZipFile::kOpenReadOnly); 44 if (result != NO_ERROR) { 45 if (result == NAME_NOT_FOUND) 46 fprintf(stderr, "ERROR: '%s' not found\n", fileName); 47 else if (result == PERMISSION_DENIED) 48 fprintf(stderr, "ERROR: '%s' access denied\n", fileName); 49 else 50 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n", 51 fileName); 52 delete zip; 53 return NULL; 54 } 55 56 return zip; 57} 58 59/* 60 * Open the file read-write. The file will be created if it doesn't 61 * already exist and "okayToCreate" is set. 62 * 63 * Returns NULL on failure. 64 */ 65ZipFile* openReadWrite(const char* fileName, bool okayToCreate) 66{ 67 ZipFile* zip = NULL; 68 status_t result; 69 int flags; 70 71 flags = ZipFile::kOpenReadWrite; 72 if (okayToCreate) 73 flags |= ZipFile::kOpenCreate; 74 75 zip = new ZipFile; 76 result = zip->open(fileName, flags); 77 if (result != NO_ERROR) { 78 delete zip; 79 zip = NULL; 80 goto bail; 81 } 82 83bail: 84 return zip; 85} 86 87 88/* 89 * Return a short string describing the compression method. 90 */ 91const char* compressionName(int method) 92{ 93 if (method == ZipEntry::kCompressStored) 94 return "Stored"; 95 else if (method == ZipEntry::kCompressDeflated) 96 return "Deflated"; 97 else 98 return "Unknown"; 99} 100 101/* 102 * Return the percent reduction in size (0% == no compression). 103 */ 104int calcPercent(long uncompressedLen, long compressedLen) 105{ 106 if (!uncompressedLen) 107 return 0; 108 else 109 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5); 110} 111 112/* 113 * Handle the "list" command, which can be a simple file dump or 114 * a verbose listing. 115 * 116 * The verbose listing closely matches the output of the Info-ZIP "unzip" 117 * command. 118 */ 119int doList(Bundle* bundle) 120{ 121 int result = 1; 122 ZipFile* zip = NULL; 123 const ZipEntry* entry; 124 long totalUncLen, totalCompLen; 125 const char* zipFileName; 126 127 if (bundle->getFileSpecCount() != 1) { 128 fprintf(stderr, "ERROR: specify zip file name (only)\n"); 129 goto bail; 130 } 131 zipFileName = bundle->getFileSpecEntry(0); 132 133 zip = openReadOnly(zipFileName); 134 if (zip == NULL) 135 goto bail; 136 137 int count, i; 138 139 if (bundle->getVerbose()) { 140 printf("Archive: %s\n", zipFileName); 141 printf( 142 " Length Method Size Ratio Date Time CRC-32 Name\n"); 143 printf( 144 "-------- ------ ------- ----- ---- ---- ------ ----\n"); 145 } 146 147 totalUncLen = totalCompLen = 0; 148 149 count = zip->getNumEntries(); 150 for (i = 0; i < count; i++) { 151 entry = zip->getEntryByIndex(i); 152 if (bundle->getVerbose()) { 153 char dateBuf[32]; 154 time_t when; 155 156 when = entry->getModWhen(); 157 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M", 158 localtime(&when)); 159 160 printf("%8ld %-7.7s %7ld %3d%% %s %08lx %s\n", 161 (long) entry->getUncompressedLen(), 162 compressionName(entry->getCompressionMethod()), 163 (long) entry->getCompressedLen(), 164 calcPercent(entry->getUncompressedLen(), 165 entry->getCompressedLen()), 166 dateBuf, 167 entry->getCRC32(), 168 entry->getFileName()); 169 } else { 170 printf("%s\n", entry->getFileName()); 171 } 172 173 totalUncLen += entry->getUncompressedLen(); 174 totalCompLen += entry->getCompressedLen(); 175 } 176 177 if (bundle->getVerbose()) { 178 printf( 179 "-------- ------- --- -------\n"); 180 printf("%8ld %7ld %2d%% %d files\n", 181 totalUncLen, 182 totalCompLen, 183 calcPercent(totalUncLen, totalCompLen), 184 zip->getNumEntries()); 185 } 186 187 if (bundle->getAndroidList()) { 188 AssetManager assets; 189 if (!assets.addAssetPath(String8(zipFileName), NULL)) { 190 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n"); 191 goto bail; 192 } 193 194 const ResTable& res = assets.getResources(false); 195 if (&res == NULL) { 196 printf("\nNo resource table found.\n"); 197 } else { 198 printf("\nResource table:\n"); 199 res.print(false); 200 } 201 202 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml", 203 Asset::ACCESS_BUFFER); 204 if (manifestAsset == NULL) { 205 printf("\nNo AndroidManifest.xml found.\n"); 206 } else { 207 printf("\nAndroid manifest:\n"); 208 ResXMLTree tree; 209 tree.setTo(manifestAsset->getBuffer(true), 210 manifestAsset->getLength()); 211 printXMLBlock(&tree); 212 } 213 delete manifestAsset; 214 } 215 216 result = 0; 217 218bail: 219 delete zip; 220 return result; 221} 222 223static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes) 224{ 225 size_t N = tree.getAttributeCount(); 226 for (size_t i=0; i<N; i++) { 227 if (tree.getAttributeNameResID(i) == attrRes) { 228 return (ssize_t)i; 229 } 230 } 231 return -1; 232} 233 234static String8 getAttribute(const ResXMLTree& tree, const char* ns, 235 const char* attr, String8* outError) 236{ 237 ssize_t idx = tree.indexOfAttribute(ns, attr); 238 if (idx < 0) { 239 return String8(); 240 } 241 Res_value value; 242 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 243 if (value.dataType != Res_value::TYPE_STRING) { 244 if (outError != NULL) *outError = "attribute is not a string value"; 245 return String8(); 246 } 247 } 248 size_t len; 249 const uint16_t* str = tree.getAttributeStringValue(idx, &len); 250 return str ? String8(str, len) : String8(); 251} 252 253static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError) 254{ 255 ssize_t idx = indexOfAttribute(tree, attrRes); 256 if (idx < 0) { 257 return String8(); 258 } 259 Res_value value; 260 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 261 if (value.dataType != Res_value::TYPE_STRING) { 262 if (outError != NULL) *outError = "attribute is not a string value"; 263 return String8(); 264 } 265 } 266 size_t len; 267 const uint16_t* str = tree.getAttributeStringValue(idx, &len); 268 return str ? String8(str, len) : String8(); 269} 270 271static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes, 272 String8* outError, int32_t defValue = -1) 273{ 274 ssize_t idx = indexOfAttribute(tree, attrRes); 275 if (idx < 0) { 276 return defValue; 277 } 278 Res_value value; 279 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 280 if (value.dataType < Res_value::TYPE_FIRST_INT 281 || value.dataType > Res_value::TYPE_LAST_INT) { 282 if (outError != NULL) *outError = "attribute is not an integer value"; 283 return defValue; 284 } 285 } 286 return value.data; 287} 288 289static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree, 290 uint32_t attrRes, String8* outError) 291{ 292 ssize_t idx = indexOfAttribute(tree, attrRes); 293 if (idx < 0) { 294 return String8(); 295 } 296 Res_value value; 297 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 298 if (value.dataType == Res_value::TYPE_STRING) { 299 size_t len; 300 const uint16_t* str = tree.getAttributeStringValue(idx, &len); 301 return str ? String8(str, len) : String8(); 302 } 303 resTable->resolveReference(&value, 0); 304 if (value.dataType != Res_value::TYPE_STRING) { 305 if (outError != NULL) *outError = "attribute is not a string value"; 306 return String8(); 307 } 308 } 309 size_t len; 310 const Res_value* value2 = &value; 311 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len); 312 return str ? String8(str, len) : String8(); 313} 314 315// These are attribute resource constants for the platform, as found 316// in android.R.attr 317enum { 318 NAME_ATTR = 0x01010003, 319 VERSION_CODE_ATTR = 0x0101021b, 320 VERSION_NAME_ATTR = 0x0101021c, 321 LABEL_ATTR = 0x01010001, 322 ICON_ATTR = 0x01010002, 323 MIN_SDK_VERSION_ATTR = 0x0101020c, 324 MAX_SDK_VERSION_ATTR = 0x01010271, 325 REQ_TOUCH_SCREEN_ATTR = 0x01010227, 326 REQ_KEYBOARD_TYPE_ATTR = 0x01010228, 327 REQ_HARD_KEYBOARD_ATTR = 0x01010229, 328 REQ_NAVIGATION_ATTR = 0x0101022a, 329 REQ_FIVE_WAY_NAV_ATTR = 0x01010232, 330 TARGET_SDK_VERSION_ATTR = 0x01010270, 331 TEST_ONLY_ATTR = 0x01010272, 332 DENSITY_ATTR = 0x0101026c, 333 SMALL_SCREEN_ATTR = 0x01010284, 334 NORMAL_SCREEN_ATTR = 0x01010285, 335 LARGE_SCREEN_ATTR = 0x01010286, 336}; 337 338const char *getComponentName(String8 &pkgName, String8 &componentName) { 339 ssize_t idx = componentName.find("."); 340 String8 retStr(pkgName); 341 if (idx == 0) { 342 retStr += componentName; 343 } else if (idx < 0) { 344 retStr += "."; 345 retStr += componentName; 346 } else { 347 return componentName.string(); 348 } 349 return retStr.string(); 350} 351 352/* 353 * Handle the "dump" command, to extract select data from an archive. 354 */ 355int doDump(Bundle* bundle) 356{ 357 status_t result = UNKNOWN_ERROR; 358 Asset* asset = NULL; 359 360 if (bundle->getFileSpecCount() < 1) { 361 fprintf(stderr, "ERROR: no dump option specified\n"); 362 return 1; 363 } 364 365 if (bundle->getFileSpecCount() < 2) { 366 fprintf(stderr, "ERROR: no dump file specified\n"); 367 return 1; 368 } 369 370 const char* option = bundle->getFileSpecEntry(0); 371 const char* filename = bundle->getFileSpecEntry(1); 372 373 AssetManager assets; 374 void* assetsCookie; 375 if (!assets.addAssetPath(String8(filename), &assetsCookie)) { 376 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n"); 377 return 1; 378 } 379 380 const ResTable& res = assets.getResources(false); 381 if (&res == NULL) { 382 fprintf(stderr, "ERROR: dump failed because no resource table was found\n"); 383 goto bail; 384 } 385 386 if (strcmp("resources", option) == 0) { 387 res.print(bundle->getValues()); 388 389 } else if (strcmp("xmltree", option) == 0) { 390 if (bundle->getFileSpecCount() < 3) { 391 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n"); 392 goto bail; 393 } 394 395 for (int i=2; i<bundle->getFileSpecCount(); i++) { 396 const char* resname = bundle->getFileSpecEntry(i); 397 ResXMLTree tree; 398 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER); 399 if (asset == NULL) { 400 fprintf(stderr, "ERROR: dump failed because resource %p found\n", resname); 401 goto bail; 402 } 403 404 if (tree.setTo(asset->getBuffer(true), 405 asset->getLength()) != NO_ERROR) { 406 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname); 407 goto bail; 408 } 409 tree.restart(); 410 printXMLBlock(&tree); 411 delete asset; 412 asset = NULL; 413 } 414 415 } else if (strcmp("xmlstrings", option) == 0) { 416 if (bundle->getFileSpecCount() < 3) { 417 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n"); 418 goto bail; 419 } 420 421 for (int i=2; i<bundle->getFileSpecCount(); i++) { 422 const char* resname = bundle->getFileSpecEntry(i); 423 ResXMLTree tree; 424 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER); 425 if (asset == NULL) { 426 fprintf(stderr, "ERROR: dump failed because resource %p found\n", resname); 427 goto bail; 428 } 429 430 if (tree.setTo(asset->getBuffer(true), 431 asset->getLength()) != NO_ERROR) { 432 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname); 433 goto bail; 434 } 435 printStringPool(&tree.getStrings()); 436 delete asset; 437 asset = NULL; 438 } 439 440 } else { 441 ResXMLTree tree; 442 asset = assets.openNonAsset("AndroidManifest.xml", 443 Asset::ACCESS_BUFFER); 444 if (asset == NULL) { 445 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n"); 446 goto bail; 447 } 448 449 if (tree.setTo(asset->getBuffer(true), 450 asset->getLength()) != NO_ERROR) { 451 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n"); 452 goto bail; 453 } 454 tree.restart(); 455 456 if (strcmp("permissions", option) == 0) { 457 size_t len; 458 ResXMLTree::event_code_t code; 459 int depth = 0; 460 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 461 if (code == ResXMLTree::END_TAG) { 462 depth--; 463 continue; 464 } 465 if (code != ResXMLTree::START_TAG) { 466 continue; 467 } 468 depth++; 469 String8 tag(tree.getElementName(&len)); 470 //printf("Depth %d tag %s\n", depth, tag.string()); 471 if (depth == 1) { 472 if (tag != "manifest") { 473 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 474 goto bail; 475 } 476 String8 pkg = getAttribute(tree, NULL, "package", NULL); 477 printf("package: %s\n", pkg.string()); 478 } else if (depth == 2 && tag == "permission") { 479 String8 error; 480 String8 name = getAttribute(tree, NAME_ATTR, &error); 481 if (error != "") { 482 fprintf(stderr, "ERROR: %s\n", error.string()); 483 goto bail; 484 } 485 printf("permission: %s\n", name.string()); 486 } else if (depth == 2 && tag == "uses-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("uses-permission: %s\n", name.string()); 494 } 495 } 496 } else if (strcmp("badging", option) == 0) { 497 size_t len; 498 ResXMLTree::event_code_t code; 499 int depth = 0; 500 String8 error; 501 bool withinActivity = false; 502 bool isMainActivity = false; 503 bool isLauncherActivity = false; 504 bool withinApplication = false; 505 bool withinReceiver = false; 506 int targetSdk = 0; 507 int smallScreen = 1; 508 int normalScreen = 1; 509 int largeScreen = 1; 510 String8 pkg; 511 String8 activityName; 512 String8 activityLabel; 513 String8 activityIcon; 514 String8 receiverName; 515 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 516 if (code == ResXMLTree::END_TAG) { 517 depth--; 518 continue; 519 } 520 if (code != ResXMLTree::START_TAG) { 521 continue; 522 } 523 depth++; 524 String8 tag(tree.getElementName(&len)); 525 //printf("Depth %d tag %s\n", depth, tag.string()); 526 if (depth == 1) { 527 if (tag != "manifest") { 528 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 529 goto bail; 530 } 531 pkg = getAttribute(tree, NULL, "package", NULL); 532 printf("package: name='%s' ", pkg.string()); 533 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error); 534 if (error != "") { 535 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string()); 536 goto bail; 537 } 538 if (versionCode > 0) { 539 printf("versionCode='%d' ", versionCode); 540 } else { 541 printf("versionCode='' "); 542 } 543 String8 versionName = getAttribute(tree, VERSION_NAME_ATTR, &error); 544 if (error != "") { 545 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string()); 546 goto bail; 547 } 548 printf("versionName='%s'\n", versionName.string()); 549 } else if (depth == 2) { 550 withinApplication = false; 551 if (tag == "application") { 552 withinApplication = true; 553 String8 label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); 554 if (error != "") { 555 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string()); 556 goto bail; 557 } 558 printf("application: label='%s' ", label.string()); 559 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 560 if (error != "") { 561 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string()); 562 goto bail; 563 } 564 printf("icon='%s'\n", icon.string()); 565 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0); 566 if (error != "") { 567 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string()); 568 goto bail; 569 } 570 if (testOnly != 0) { 571 printf("testOnly='%d'\n", testOnly); 572 } 573 } else if (tag == "uses-sdk") { 574 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error); 575 if (error != "") { 576 error = ""; 577 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error); 578 if (error != "") { 579 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n", 580 error.string()); 581 goto bail; 582 } 583 if (name == "Donut") targetSdk = 4; 584 printf("sdkVersion:'%s'\n", name.string()); 585 } else if (code != -1) { 586 targetSdk = code; 587 printf("sdkVersion:'%d'\n", code); 588 } 589 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1); 590 if (code != -1) { 591 printf("maxSdkVersion:'%d'\n", code); 592 } 593 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error); 594 if (error != "") { 595 error = ""; 596 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error); 597 if (error != "") { 598 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n", 599 error.string()); 600 goto bail; 601 } 602 if (name == "Donut" && targetSdk < 4) targetSdk = 4; 603 printf("targetSdkVersion:'%s'\n", name.string()); 604 } else if (code != -1) { 605 if (targetSdk < code) { 606 targetSdk = code; 607 } 608 printf("targetSdkVersion:'%d'\n", code); 609 } 610 } else if (tag == "uses-configuration") { 611 int32_t reqTouchScreen = getIntegerAttribute(tree, 612 REQ_TOUCH_SCREEN_ATTR, NULL, 0); 613 int32_t reqKeyboardType = getIntegerAttribute(tree, 614 REQ_KEYBOARD_TYPE_ATTR, NULL, 0); 615 int32_t reqHardKeyboard = getIntegerAttribute(tree, 616 REQ_HARD_KEYBOARD_ATTR, NULL, 0); 617 int32_t reqNavigation = getIntegerAttribute(tree, 618 REQ_NAVIGATION_ATTR, NULL, 0); 619 int32_t reqFiveWayNav = getIntegerAttribute(tree, 620 REQ_FIVE_WAY_NAV_ATTR, NULL, 0); 621 printf("uses-configuation:"); 622 if (reqTouchScreen != 0) { 623 printf(" reqTouchScreen='%d'", reqTouchScreen); 624 } 625 if (reqKeyboardType != 0) { 626 printf(" reqKeyboardType='%d'", reqKeyboardType); 627 } 628 if (reqHardKeyboard != 0) { 629 printf(" reqHardKeyboard='%d'", reqHardKeyboard); 630 } 631 if (reqNavigation != 0) { 632 printf(" reqNavigation='%d'", reqNavigation); 633 } 634 if (reqFiveWayNav != 0) { 635 printf(" reqFiveWayNav='%d'", reqFiveWayNav); 636 } 637 printf("\n"); 638 } else if (tag == "supports-density") { 639 int32_t dens = getIntegerAttribute(tree, DENSITY_ATTR, &error); 640 if (error != "") { 641 fprintf(stderr, "ERROR getting 'android:density' attribute: %s\n", 642 error.string()); 643 goto bail; 644 } 645 printf("supports-density:'%d'\n", dens); 646 } else if (tag == "supports-screens") { 647 smallScreen = getIntegerAttribute(tree, 648 SMALL_SCREEN_ATTR, NULL, 1); 649 normalScreen = getIntegerAttribute(tree, 650 NORMAL_SCREEN_ATTR, NULL, 1); 651 largeScreen = getIntegerAttribute(tree, 652 LARGE_SCREEN_ATTR, NULL, 1); 653 } 654 } else if (depth == 3 && withinApplication) { 655 withinActivity = false; 656 withinReceiver = false; 657 if(tag == "activity") { 658 withinActivity = true; 659 activityName = getAttribute(tree, NAME_ATTR, &error); 660 if (error != "") { 661 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); 662 goto bail; 663 } 664 665 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); 666 if (error != "") { 667 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string()); 668 goto bail; 669 } 670 671 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 672 if (error != "") { 673 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string()); 674 goto bail; 675 } 676 } else if (tag == "uses-library") { 677 String8 libraryName = getAttribute(tree, NAME_ATTR, &error); 678 if (error != "") { 679 fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string()); 680 goto bail; 681 } 682 printf("uses-library:'%s'\n", libraryName.string()); 683 } else if (tag == "receiver") { 684 withinReceiver = true; 685 receiverName = getAttribute(tree, NAME_ATTR, &error); 686 687 if (error != "") { 688 fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string()); 689 goto bail; 690 } 691 } 692 } else if (depth == 5) { 693 if (withinActivity) { 694 if (tag == "action") { 695 //printf("LOG: action tag\n"); 696 String8 action = getAttribute(tree, NAME_ATTR, &error); 697 if (error != "") { 698 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); 699 goto bail; 700 } 701 if (action == "android.intent.action.MAIN") { 702 isMainActivity = true; 703 //printf("LOG: isMainActivity==true\n"); 704 } 705 } else if (tag == "category") { 706 String8 category = getAttribute(tree, NAME_ATTR, &error); 707 if (error != "") { 708 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string()); 709 goto bail; 710 } 711 if (category == "android.intent.category.LAUNCHER") { 712 isLauncherActivity = true; 713 //printf("LOG: isLauncherActivity==true\n"); 714 } 715 } 716 } else if (withinReceiver) { 717 if (tag == "action") { 718 String8 action = getAttribute(tree, NAME_ATTR, &error); 719 if (error != "") { 720 fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string()); 721 goto bail; 722 } 723 if (action == "android.appwidget.action.APPWIDGET_UPDATE") { 724 const char *rName = getComponentName(pkg, receiverName); 725 if (rName != NULL) { 726 printf("gadget-receiver:'%s/%s'\n", pkg.string(), rName); 727 } 728 } 729 } 730 } 731 } 732 733 if (depth < 2) { 734 withinApplication = false; 735 } 736 if (depth < 3) { 737 //if (withinActivity) printf("LOG: withinActivity==false\n"); 738 withinActivity = false; 739 withinReceiver = false; 740 } 741 742 if (depth < 5) { 743 //if (isMainActivity) printf("LOG: isMainActivity==false\n"); 744 //if (isLauncherActivity) printf("LOG: isLauncherActivity==false\n"); 745 isMainActivity = false; 746 isLauncherActivity = false; 747 } 748 749 if (withinActivity && isMainActivity && isLauncherActivity) { 750 printf("launchable activity:"); 751 const char *aName = getComponentName(pkg, activityName); 752 if (aName != NULL) { 753 printf(" name='%s'", aName); 754 } 755 printf("label='%s' icon='%s'\n", 756 activityLabel.string(), 757 activityIcon.string()); 758 } 759 } 760 761 // Determine default values for any unspecified screen sizes, 762 // based on the target SDK of the package. As of 4 (donut) 763 // the screen size support was introduced, so all default to 764 // enabled. 765 if (smallScreen > 0) { 766 smallScreen = targetSdk >= 4 ? -1 : 0; 767 } 768 if (normalScreen > 0) { 769 normalScreen = -1; 770 } 771 if (largeScreen > 0) { 772 largeScreen = targetSdk >= 4 ? -1 : 0; 773 } 774 printf("supports-screens:"); 775 if (smallScreen != 0) printf(" 'small'"); 776 if (normalScreen != 0) printf(" 'normal'"); 777 if (largeScreen != 0) printf(" 'large'"); 778 printf("\n"); 779 780 printf("locales:"); 781 Vector<String8> locales; 782 res.getLocales(&locales); 783 const size_t NL = locales.size(); 784 for (size_t i=0; i<NL; i++) { 785 const char* localeStr = locales[i].string(); 786 if (localeStr == NULL || strlen(localeStr) == 0) { 787 localeStr = "--_--"; 788 } 789 printf(" '%s'", localeStr); 790 } 791 printf("\n"); 792 793 Vector<ResTable_config> configs; 794 res.getConfigurations(&configs); 795 SortedVector<int> densities; 796 const size_t NC = configs.size(); 797 for (size_t i=0; i<NC; i++) { 798 int dens = configs[i].density; 799 if (dens == 0) dens = 160; 800 densities.add(dens); 801 } 802 803 printf("densities:"); 804 const size_t ND = densities.size(); 805 for (size_t i=0; i<ND; i++) { 806 printf(" '%d'", densities[i]); 807 } 808 printf("\n"); 809 810 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib"); 811 if (dir != NULL) { 812 if (dir->getFileCount() > 0) { 813 printf("native-code:"); 814 for (size_t i=0; i<dir->getFileCount(); i++) { 815 printf(" '%s'", dir->getFileName(i).string()); 816 } 817 printf("\n"); 818 } 819 delete dir; 820 } 821 } else if (strcmp("configurations", option) == 0) { 822 Vector<ResTable_config> configs; 823 res.getConfigurations(&configs); 824 const size_t N = configs.size(); 825 for (size_t i=0; i<N; i++) { 826 printf("%s\n", configs[i].toString().string()); 827 } 828 } else { 829 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option); 830 goto bail; 831 } 832 } 833 834 result = NO_ERROR; 835 836bail: 837 if (asset) { 838 delete asset; 839 } 840 return (result != NO_ERROR); 841} 842 843 844/* 845 * Handle the "add" command, which wants to add files to a new or 846 * pre-existing archive. 847 */ 848int doAdd(Bundle* bundle) 849{ 850 ZipFile* zip = NULL; 851 status_t result = UNKNOWN_ERROR; 852 const char* zipFileName; 853 854 if (bundle->getUpdate()) { 855 /* avoid confusion */ 856 fprintf(stderr, "ERROR: can't use '-u' with add\n"); 857 goto bail; 858 } 859 860 if (bundle->getFileSpecCount() < 1) { 861 fprintf(stderr, "ERROR: must specify zip file name\n"); 862 goto bail; 863 } 864 zipFileName = bundle->getFileSpecEntry(0); 865 866 if (bundle->getFileSpecCount() < 2) { 867 fprintf(stderr, "NOTE: nothing to do\n"); 868 goto bail; 869 } 870 871 zip = openReadWrite(zipFileName, true); 872 if (zip == NULL) { 873 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName); 874 goto bail; 875 } 876 877 for (int i = 1; i < bundle->getFileSpecCount(); i++) { 878 const char* fileName = bundle->getFileSpecEntry(i); 879 880 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) { 881 printf(" '%s'... (from gzip)\n", fileName); 882 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL); 883 } else { 884 printf(" '%s'...\n", fileName); 885 result = zip->add(fileName, bundle->getCompressionMethod(), NULL); 886 } 887 if (result != NO_ERROR) { 888 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName); 889 if (result == NAME_NOT_FOUND) 890 fprintf(stderr, ": file not found\n"); 891 else if (result == ALREADY_EXISTS) 892 fprintf(stderr, ": already exists in archive\n"); 893 else 894 fprintf(stderr, "\n"); 895 goto bail; 896 } 897 } 898 899 result = NO_ERROR; 900 901bail: 902 delete zip; 903 return (result != NO_ERROR); 904} 905 906 907/* 908 * Delete files from an existing archive. 909 */ 910int doRemove(Bundle* bundle) 911{ 912 ZipFile* zip = NULL; 913 status_t result = UNKNOWN_ERROR; 914 const char* zipFileName; 915 916 if (bundle->getFileSpecCount() < 1) { 917 fprintf(stderr, "ERROR: must specify zip file name\n"); 918 goto bail; 919 } 920 zipFileName = bundle->getFileSpecEntry(0); 921 922 if (bundle->getFileSpecCount() < 2) { 923 fprintf(stderr, "NOTE: nothing to do\n"); 924 goto bail; 925 } 926 927 zip = openReadWrite(zipFileName, false); 928 if (zip == NULL) { 929 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n", 930 zipFileName); 931 goto bail; 932 } 933 934 for (int i = 1; i < bundle->getFileSpecCount(); i++) { 935 const char* fileName = bundle->getFileSpecEntry(i); 936 ZipEntry* entry; 937 938 entry = zip->getEntryByName(fileName); 939 if (entry == NULL) { 940 printf(" '%s' NOT FOUND\n", fileName); 941 continue; 942 } 943 944 result = zip->remove(entry); 945 946 if (result != NO_ERROR) { 947 fprintf(stderr, "Unable to delete '%s' from '%s'\n", 948 bundle->getFileSpecEntry(i), zipFileName); 949 goto bail; 950 } 951 } 952 953 /* update the archive */ 954 zip->flush(); 955 956bail: 957 delete zip; 958 return (result != NO_ERROR); 959} 960 961 962/* 963 * Package up an asset directory and associated application files. 964 */ 965int doPackage(Bundle* bundle) 966{ 967 const char* outputAPKFile; 968 int retVal = 1; 969 status_t err; 970 sp<AaptAssets> assets; 971 int N; 972 973 // -c zz_ZZ means do pseudolocalization 974 ResourceFilter filter; 975 err = filter.parse(bundle->getConfigurations()); 976 if (err != NO_ERROR) { 977 goto bail; 978 } 979 if (filter.containsPseudo()) { 980 bundle->setPseudolocalize(true); 981 } 982 983 N = bundle->getFileSpecCount(); 984 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0 985 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) { 986 fprintf(stderr, "ERROR: no input files\n"); 987 goto bail; 988 } 989 990 outputAPKFile = bundle->getOutputAPKFile(); 991 992 // Make sure the filenames provided exist and are of the appropriate type. 993 if (outputAPKFile) { 994 FileType type; 995 type = getFileType(outputAPKFile); 996 if (type != kFileTypeNonexistent && type != kFileTypeRegular) { 997 fprintf(stderr, 998 "ERROR: output file '%s' exists but is not regular file\n", 999 outputAPKFile); 1000 goto bail; 1001 } 1002 } 1003 1004 // Load the assets. 1005 assets = new AaptAssets(); 1006 err = assets->slurpFromArgs(bundle); 1007 if (err < 0) { 1008 goto bail; 1009 } 1010 1011 if (bundle->getVerbose()) { 1012 assets->print(); 1013 } 1014 1015 // If they asked for any files that need to be compiled, do so. 1016 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) { 1017 err = buildResources(bundle, assets); 1018 if (err != 0) { 1019 goto bail; 1020 } 1021 } 1022 1023 // At this point we've read everything and processed everything. From here 1024 // on out it's just writing output files. 1025 if (SourcePos::hasErrors()) { 1026 goto bail; 1027 } 1028 1029 // Write out R.java constants 1030 if (assets->getPackage() == assets->getSymbolsPrivatePackage()) { 1031 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true); 1032 if (err < 0) { 1033 goto bail; 1034 } 1035 } else { 1036 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false); 1037 if (err < 0) { 1038 goto bail; 1039 } 1040 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true); 1041 if (err < 0) { 1042 goto bail; 1043 } 1044 } 1045 1046 // Write the apk 1047 if (outputAPKFile) { 1048 err = writeAPK(bundle, assets, String8(outputAPKFile)); 1049 if (err != NO_ERROR) { 1050 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile); 1051 goto bail; 1052 } 1053 } 1054 1055 retVal = 0; 1056bail: 1057 if (SourcePos::hasErrors()) { 1058 SourcePos::printErrors(stderr); 1059 } 1060 return retVal; 1061} 1062