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