Command.cpp revision caf797c84a6a2829662872e0df93fcd61da78d51
1// 2// Copyright 2006 The Android Open Source Project 3// 4// Android Asset Packaging Tool main entry point. 5// 6#include "ApkBuilder.h" 7#include "Bundle.h" 8#include "Images.h" 9#include "Main.h" 10#include "ResourceFilter.h" 11#include "ResourceTable.h" 12#include "XMLNode.h" 13 14#include <utils/Errors.h> 15#include <utils/KeyedVector.h> 16#include <utils/List.h> 17#include <utils/Log.h> 18#include <utils/SortedVector.h> 19#include <utils/threads.h> 20#include <utils/Vector.h> 21 22#include <errno.h> 23#include <fcntl.h> 24 25using namespace android; 26 27#ifndef AAPT_VERSION 28 #define AAPT_VERSION "" 29#endif 30 31/* 32 * Show version info. All the cool kids do it. 33 */ 34int doVersion(Bundle* bundle) 35{ 36 if (bundle->getFileSpecCount() != 0) { 37 printf("(ignoring extra arguments)\n"); 38 } 39 printf("Android Asset Packaging Tool, v0.2-" AAPT_VERSION "\n"); 40 41 return 0; 42} 43 44 45/* 46 * Open the file read only. The call fails if the file doesn't exist. 47 * 48 * Returns NULL on failure. 49 */ 50ZipFile* openReadOnly(const char* fileName) 51{ 52 ZipFile* zip; 53 status_t result; 54 55 zip = new ZipFile; 56 result = zip->open(fileName, ZipFile::kOpenReadOnly); 57 if (result != NO_ERROR) { 58 if (result == NAME_NOT_FOUND) { 59 fprintf(stderr, "ERROR: '%s' not found\n", fileName); 60 } else if (result == PERMISSION_DENIED) { 61 fprintf(stderr, "ERROR: '%s' access denied\n", fileName); 62 } else { 63 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n", 64 fileName); 65 } 66 delete zip; 67 return NULL; 68 } 69 70 return zip; 71} 72 73/* 74 * Open the file read-write. The file will be created if it doesn't 75 * already exist and "okayToCreate" is set. 76 * 77 * Returns NULL on failure. 78 */ 79ZipFile* openReadWrite(const char* fileName, bool okayToCreate) 80{ 81 ZipFile* zip = NULL; 82 status_t result; 83 int flags; 84 85 flags = ZipFile::kOpenReadWrite; 86 if (okayToCreate) { 87 flags |= ZipFile::kOpenCreate; 88 } 89 90 zip = new ZipFile; 91 result = zip->open(fileName, flags); 92 if (result != NO_ERROR) { 93 delete zip; 94 zip = NULL; 95 goto bail; 96 } 97 98bail: 99 return zip; 100} 101 102 103/* 104 * Return a short string describing the compression method. 105 */ 106const char* compressionName(int method) 107{ 108 if (method == ZipEntry::kCompressStored) { 109 return "Stored"; 110 } else if (method == ZipEntry::kCompressDeflated) { 111 return "Deflated"; 112 } else { 113 return "Unknown"; 114 } 115} 116 117/* 118 * Return the percent reduction in size (0% == no compression). 119 */ 120int calcPercent(long uncompressedLen, long compressedLen) 121{ 122 if (!uncompressedLen) { 123 return 0; 124 } else { 125 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5); 126 } 127} 128 129/* 130 * Handle the "list" command, which can be a simple file dump or 131 * a verbose listing. 132 * 133 * The verbose listing closely matches the output of the Info-ZIP "unzip" 134 * command. 135 */ 136int doList(Bundle* bundle) 137{ 138 int result = 1; 139 ZipFile* zip = NULL; 140 const ZipEntry* entry; 141 long totalUncLen, totalCompLen; 142 const char* zipFileName; 143 144 if (bundle->getFileSpecCount() != 1) { 145 fprintf(stderr, "ERROR: specify zip file name (only)\n"); 146 goto bail; 147 } 148 zipFileName = bundle->getFileSpecEntry(0); 149 150 zip = openReadOnly(zipFileName); 151 if (zip == NULL) { 152 goto bail; 153 } 154 155 int count, i; 156 157 if (bundle->getVerbose()) { 158 printf("Archive: %s\n", zipFileName); 159 printf( 160 " Length Method Size Ratio Offset Date Time CRC-32 Name\n"); 161 printf( 162 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n"); 163 } 164 165 totalUncLen = totalCompLen = 0; 166 167 count = zip->getNumEntries(); 168 for (i = 0; i < count; i++) { 169 entry = zip->getEntryByIndex(i); 170 if (bundle->getVerbose()) { 171 char dateBuf[32]; 172 time_t when; 173 174 when = entry->getModWhen(); 175 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M", 176 localtime(&when)); 177 178 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n", 179 (long) entry->getUncompressedLen(), 180 compressionName(entry->getCompressionMethod()), 181 (long) entry->getCompressedLen(), 182 calcPercent(entry->getUncompressedLen(), 183 entry->getCompressedLen()), 184 (size_t) entry->getLFHOffset(), 185 dateBuf, 186 entry->getCRC32(), 187 entry->getFileName()); 188 } else { 189 printf("%s\n", entry->getFileName()); 190 } 191 192 totalUncLen += entry->getUncompressedLen(); 193 totalCompLen += entry->getCompressedLen(); 194 } 195 196 if (bundle->getVerbose()) { 197 printf( 198 "-------- ------- --- -------\n"); 199 printf("%8ld %7ld %2d%% %d files\n", 200 totalUncLen, 201 totalCompLen, 202 calcPercent(totalUncLen, totalCompLen), 203 zip->getNumEntries()); 204 } 205 206 if (bundle->getAndroidList()) { 207 AssetManager assets; 208 if (!assets.addAssetPath(String8(zipFileName), NULL)) { 209 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n"); 210 goto bail; 211 } 212 213 const ResTable& res = assets.getResources(false); 214 if (&res == NULL) { 215 printf("\nNo resource table found.\n"); 216 } else { 217#ifndef HAVE_ANDROID_OS 218 printf("\nResource table:\n"); 219 res.print(false); 220#endif 221 } 222 223 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml", 224 Asset::ACCESS_BUFFER); 225 if (manifestAsset == NULL) { 226 printf("\nNo AndroidManifest.xml found.\n"); 227 } else { 228 printf("\nAndroid manifest:\n"); 229 ResXMLTree tree; 230 tree.setTo(manifestAsset->getBuffer(true), 231 manifestAsset->getLength()); 232 printXMLBlock(&tree); 233 } 234 delete manifestAsset; 235 } 236 237 result = 0; 238 239bail: 240 delete zip; 241 return result; 242} 243 244static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes) 245{ 246 size_t N = tree.getAttributeCount(); 247 for (size_t i=0; i<N; i++) { 248 if (tree.getAttributeNameResID(i) == attrRes) { 249 return (ssize_t)i; 250 } 251 } 252 return -1; 253} 254 255String8 getAttribute(const ResXMLTree& tree, const char* ns, 256 const char* attr, String8* outError) 257{ 258 ssize_t idx = tree.indexOfAttribute(ns, attr); 259 if (idx < 0) { 260 return String8(); 261 } 262 Res_value value; 263 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 264 if (value.dataType != Res_value::TYPE_STRING) { 265 if (outError != NULL) { 266 *outError = "attribute is not a string value"; 267 } 268 return String8(); 269 } 270 } 271 size_t len; 272 const uint16_t* str = tree.getAttributeStringValue(idx, &len); 273 return str ? String8(str, len) : String8(); 274} 275 276static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError) 277{ 278 ssize_t idx = indexOfAttribute(tree, attrRes); 279 if (idx < 0) { 280 return String8(); 281 } 282 Res_value value; 283 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 284 if (value.dataType != Res_value::TYPE_STRING) { 285 if (outError != NULL) { 286 *outError = "attribute is not a string value"; 287 } 288 return String8(); 289 } 290 } 291 size_t len; 292 const uint16_t* str = tree.getAttributeStringValue(idx, &len); 293 return str ? String8(str, len) : String8(); 294} 295 296static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes, 297 String8* outError, int32_t defValue = -1) 298{ 299 ssize_t idx = indexOfAttribute(tree, attrRes); 300 if (idx < 0) { 301 return defValue; 302 } 303 Res_value value; 304 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 305 if (value.dataType < Res_value::TYPE_FIRST_INT 306 || value.dataType > Res_value::TYPE_LAST_INT) { 307 if (outError != NULL) { 308 *outError = "attribute is not an integer value"; 309 } 310 return defValue; 311 } 312 } 313 return value.data; 314} 315 316static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree, 317 uint32_t attrRes, String8* outError, int32_t defValue = -1) 318{ 319 ssize_t idx = indexOfAttribute(tree, attrRes); 320 if (idx < 0) { 321 return defValue; 322 } 323 Res_value value; 324 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 325 if (value.dataType == Res_value::TYPE_REFERENCE) { 326 resTable->resolveReference(&value, 0); 327 } 328 if (value.dataType < Res_value::TYPE_FIRST_INT 329 || value.dataType > Res_value::TYPE_LAST_INT) { 330 if (outError != NULL) { 331 *outError = "attribute is not an integer value"; 332 } 333 return defValue; 334 } 335 } 336 return value.data; 337} 338 339static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree, 340 uint32_t attrRes, String8* outError) 341{ 342 ssize_t idx = indexOfAttribute(tree, attrRes); 343 if (idx < 0) { 344 return String8(); 345 } 346 Res_value value; 347 if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 348 if (value.dataType == Res_value::TYPE_STRING) { 349 size_t len; 350 const uint16_t* str = tree.getAttributeStringValue(idx, &len); 351 return str ? String8(str, len) : String8(); 352 } 353 resTable->resolveReference(&value, 0); 354 if (value.dataType != Res_value::TYPE_STRING) { 355 if (outError != NULL) { 356 *outError = "attribute is not a string value"; 357 } 358 return String8(); 359 } 360 } 361 size_t len; 362 const Res_value* value2 = &value; 363 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len); 364 return str ? String8(str, len) : String8(); 365} 366 367static void getResolvedResourceAttribute(Res_value* value, const ResTable* resTable, 368 const ResXMLTree& tree, uint32_t attrRes, String8* outError) 369{ 370 ssize_t idx = indexOfAttribute(tree, attrRes); 371 if (idx < 0) { 372 if (outError != NULL) { 373 *outError = "attribute could not be found"; 374 } 375 return; 376 } 377 if (tree.getAttributeValue(idx, value) != NO_ERROR) { 378 if (value->dataType == Res_value::TYPE_REFERENCE) { 379 resTable->resolveReference(value, 0); 380 } 381 // The attribute was found and was resolved if need be. 382 return; 383 } 384 if (outError != NULL) { 385 *outError = "error getting resolved resource attribute"; 386 } 387} 388 389static void printResolvedResourceAttribute(const ResTable* resTable, const ResXMLTree& tree, 390 uint32_t attrRes, String8 attrLabel, String8* outError) 391{ 392 Res_value value; 393 getResolvedResourceAttribute(&value, resTable, tree, attrRes, outError); 394 if (*outError != "") { 395 *outError = "error print resolved resource attribute"; 396 return; 397 } 398 if (value.dataType == Res_value::TYPE_STRING) { 399 String8 result = getResolvedAttribute(resTable, tree, attrRes, outError); 400 printf("%s='%s'", attrLabel.string(), 401 ResTable::normalizeForOutput(result.string()).string()); 402 } else if (Res_value::TYPE_FIRST_INT <= value.dataType && 403 value.dataType <= Res_value::TYPE_LAST_INT) { 404 printf("%s='%d'", attrLabel.string(), value.data); 405 } else { 406 printf("%s='0x%x'", attrLabel.string(), (int)value.data); 407 } 408} 409 410// These are attribute resource constants for the platform, as found 411// in android.R.attr 412enum { 413 LABEL_ATTR = 0x01010001, 414 ICON_ATTR = 0x01010002, 415 NAME_ATTR = 0x01010003, 416 PERMISSION_ATTR = 0x01010006, 417 EXPORTED_ATTR = 0x01010010, 418 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b, 419 RESOURCE_ATTR = 0x01010025, 420 DEBUGGABLE_ATTR = 0x0101000f, 421 VALUE_ATTR = 0x01010024, 422 VERSION_CODE_ATTR = 0x0101021b, 423 VERSION_NAME_ATTR = 0x0101021c, 424 SCREEN_ORIENTATION_ATTR = 0x0101001e, 425 MIN_SDK_VERSION_ATTR = 0x0101020c, 426 MAX_SDK_VERSION_ATTR = 0x01010271, 427 REQ_TOUCH_SCREEN_ATTR = 0x01010227, 428 REQ_KEYBOARD_TYPE_ATTR = 0x01010228, 429 REQ_HARD_KEYBOARD_ATTR = 0x01010229, 430 REQ_NAVIGATION_ATTR = 0x0101022a, 431 REQ_FIVE_WAY_NAV_ATTR = 0x01010232, 432 TARGET_SDK_VERSION_ATTR = 0x01010270, 433 TEST_ONLY_ATTR = 0x01010272, 434 ANY_DENSITY_ATTR = 0x0101026c, 435 GL_ES_VERSION_ATTR = 0x01010281, 436 SMALL_SCREEN_ATTR = 0x01010284, 437 NORMAL_SCREEN_ATTR = 0x01010285, 438 LARGE_SCREEN_ATTR = 0x01010286, 439 XLARGE_SCREEN_ATTR = 0x010102bf, 440 REQUIRED_ATTR = 0x0101028e, 441 INSTALL_LOCATION_ATTR = 0x010102b7, 442 SCREEN_SIZE_ATTR = 0x010102ca, 443 SCREEN_DENSITY_ATTR = 0x010102cb, 444 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364, 445 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365, 446 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366, 447 PUBLIC_KEY_ATTR = 0x010103a6, 448 CATEGORY_ATTR = 0x010103e8, 449 BANNER_ATTR = 0x10103f2, 450}; 451 452String8 getComponentName(String8 &pkgName, String8 &componentName) { 453 ssize_t idx = componentName.find("."); 454 String8 retStr(pkgName); 455 if (idx == 0) { 456 retStr += componentName; 457 } else if (idx < 0) { 458 retStr += "."; 459 retStr += componentName; 460 } else { 461 return componentName; 462 } 463 return retStr; 464} 465 466static void printCompatibleScreens(ResXMLTree& tree, String8* outError) { 467 size_t len; 468 ResXMLTree::event_code_t code; 469 int depth = 0; 470 bool first = true; 471 printf("compatible-screens:"); 472 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 473 if (code == ResXMLTree::END_TAG) { 474 depth--; 475 if (depth < 0) { 476 break; 477 } 478 continue; 479 } 480 if (code != ResXMLTree::START_TAG) { 481 continue; 482 } 483 depth++; 484 const char16_t* ctag16 = tree.getElementName(&len); 485 if (ctag16 == NULL) { 486 *outError = "failed to get XML element name (bad string pool)"; 487 return; 488 } 489 String8 tag(ctag16); 490 if (tag == "screen") { 491 int32_t screenSize = getIntegerAttribute(tree, 492 SCREEN_SIZE_ATTR, NULL, -1); 493 int32_t screenDensity = getIntegerAttribute(tree, 494 SCREEN_DENSITY_ATTR, NULL, -1); 495 if (screenSize > 0 && screenDensity > 0) { 496 if (!first) { 497 printf(","); 498 } 499 first = false; 500 printf("'%d/%d'", screenSize, screenDensity); 501 } 502 } 503 } 504 printf("\n"); 505} 506 507static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) { 508 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string()); 509 if (maxSdkVersion != -1) { 510 printf(" maxSdkVersion='%d'", maxSdkVersion); 511 } 512 printf("\n"); 513 514 if (optional) { 515 printf("optional-permission: name='%s'", 516 ResTable::normalizeForOutput(name.string()).string()); 517 if (maxSdkVersion != -1) { 518 printf(" maxSdkVersion='%d'", maxSdkVersion); 519 } 520 printf("\n"); 521 } 522} 523 524static void printUsesImpliedPermission(const String8& name, const String8& reason) { 525 printf("uses-implied-permission: name='%s' reason='%s'\n", 526 ResTable::normalizeForOutput(name.string()).string(), 527 ResTable::normalizeForOutput(reason.string()).string()); 528} 529 530Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost, 531 String8 *outError = NULL) 532{ 533 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER); 534 if (aidAsset == NULL) { 535 if (outError != NULL) *outError = "xml resource does not exist"; 536 return Vector<String8>(); 537 } 538 539 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service"); 540 541 bool withinApduService = false; 542 Vector<String8> categories; 543 544 String8 error; 545 ResXMLTree tree; 546 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength()); 547 548 size_t len; 549 int depth = 0; 550 ResXMLTree::event_code_t code; 551 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 552 if (code == ResXMLTree::END_TAG) { 553 depth--; 554 const char16_t* ctag16 = tree.getElementName(&len); 555 if (ctag16 == NULL) { 556 *outError = "failed to get XML element name (bad string pool)"; 557 return Vector<String8>(); 558 } 559 String8 tag(ctag16); 560 561 if (depth == 0 && tag == serviceTagName) { 562 withinApduService = false; 563 } 564 565 } else if (code == ResXMLTree::START_TAG) { 566 depth++; 567 const char16_t* ctag16 = tree.getElementName(&len); 568 if (ctag16 == NULL) { 569 *outError = "failed to get XML element name (bad string pool)"; 570 return Vector<String8>(); 571 } 572 String8 tag(ctag16); 573 574 if (depth == 1) { 575 if (tag == serviceTagName) { 576 withinApduService = true; 577 } 578 } else if (depth == 2 && withinApduService) { 579 if (tag == "aid-group") { 580 String8 category = getAttribute(tree, CATEGORY_ATTR, &error); 581 if (error != "") { 582 if (outError != NULL) *outError = error; 583 return Vector<String8>(); 584 } 585 586 categories.add(category); 587 } 588 } 589 } 590 } 591 aidAsset->close(); 592 return categories; 593} 594 595static void printComponentPresence(const char* componentName) { 596 printf("provides-component:'%s'\n", componentName); 597} 598 599/** 600 * Represents a feature that has been automatically added due to 601 * a pre-requisite or some other reason. 602 */ 603struct ImpliedFeature { 604 /** 605 * Name of the implied feature. 606 */ 607 String8 name; 608 609 /** 610 * List of human-readable reasons for why this feature was implied. 611 */ 612 SortedVector<String8> reasons; 613}; 614 615/** 616 * Represents a <feature-group> tag in the AndroidManifest.xml 617 */ 618struct FeatureGroup { 619 FeatureGroup() : openGLESVersion(-1) {} 620 621 /** 622 * Human readable label 623 */ 624 String8 label; 625 626 /** 627 * Explicit features defined in the group 628 */ 629 KeyedVector<String8, bool> features; 630 631 /** 632 * OpenGL ES version required 633 */ 634 int openGLESVersion; 635}; 636 637static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures, 638 const char* name, const char* reason) { 639 String8 name8(name); 640 ssize_t idx = impliedFeatures->indexOfKey(name8); 641 if (idx < 0) { 642 idx = impliedFeatures->add(name8, ImpliedFeature()); 643 impliedFeatures->editValueAt(idx).name = name8; 644 } 645 impliedFeatures->editValueAt(idx).reasons.add(String8(reason)); 646} 647 648static void printFeatureGroup(const FeatureGroup& grp, 649 const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) { 650 printf("feature-group: label='%s'\n", grp.label.string()); 651 652 if (grp.openGLESVersion > 0) { 653 printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion); 654 } 655 656 const size_t numFeatures = grp.features.size(); 657 for (size_t i = 0; i < numFeatures; i++) { 658 if (!grp.features[i]) { 659 continue; 660 } 661 662 const String8& featureName = grp.features.keyAt(i); 663 printf(" uses-feature: name='%s'\n", 664 ResTable::normalizeForOutput(featureName.string()).string()); 665 } 666 667 const size_t numImpliedFeatures = 668 (impliedFeatures != NULL) ? impliedFeatures->size() : 0; 669 for (size_t i = 0; i < numImpliedFeatures; i++) { 670 const ImpliedFeature& impliedFeature = impliedFeatures->valueAt(i); 671 if (grp.features.indexOfKey(impliedFeature.name) >= 0) { 672 // The feature is explicitly set, no need to use implied 673 // definition. 674 continue; 675 } 676 677 String8 printableFeatureName(ResTable::normalizeForOutput( 678 impliedFeature.name.string())); 679 printf(" uses-feature: name='%s'\n", printableFeatureName.string()); 680 printf(" uses-implied-feature: name='%s' reason='", 681 printableFeatureName.string()); 682 const size_t numReasons = impliedFeature.reasons.size(); 683 for (size_t j = 0; j < numReasons; j++) { 684 printf("%s", impliedFeature.reasons[j].string()); 685 if (j + 2 < numReasons) { 686 printf(", "); 687 } else if (j + 1 < numReasons) { 688 printf(", and "); 689 } 690 } 691 printf("'\n"); 692 } 693} 694 695static void addParentFeatures(FeatureGroup* grp, const String8& name) { 696 if (name == "android.hardware.camera.autofocus" || 697 name == "android.hardware.camera.flash") { 698 grp->features.add(String8("android.hardware.camera"), true); 699 } else if (name == "android.hardware.location.gps" || 700 name == "android.hardware.location.network") { 701 grp->features.add(String8("android.hardware.location"), true); 702 } else if (name == "android.hardware.touchscreen.multitouch") { 703 grp->features.add(String8("android.hardware.touchscreen"), true); 704 } else if (name == "android.hardware.touchscreen.multitouch.distinct") { 705 grp->features.add(String8("android.hardware.touchscreen.multitouch"), true); 706 grp->features.add(String8("android.hardware.touchscreen"), true); 707 } else if (name == "android.hardware.opengles.aep") { 708 const int openGLESVersion31 = 0x00030001; 709 if (openGLESVersion31 > grp->openGLESVersion) { 710 grp->openGLESVersion = openGLESVersion31; 711 } 712 } 713} 714 715/* 716 * Handle the "dump" command, to extract select data from an archive. 717 */ 718extern char CONSOLE_DATA[2925]; // see EOF 719int doDump(Bundle* bundle) 720{ 721 status_t result = UNKNOWN_ERROR; 722 723 if (bundle->getFileSpecCount() < 1) { 724 fprintf(stderr, "ERROR: no dump option specified\n"); 725 return 1; 726 } 727 728 if (bundle->getFileSpecCount() < 2) { 729 fprintf(stderr, "ERROR: no dump file specified\n"); 730 return 1; 731 } 732 733 const char* option = bundle->getFileSpecEntry(0); 734 const char* filename = bundle->getFileSpecEntry(1); 735 736 AssetManager assets; 737 int32_t assetsCookie; 738 if (!assets.addAssetPath(String8(filename), &assetsCookie)) { 739 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n"); 740 return 1; 741 } 742 743 // Make a dummy config for retrieving resources... we need to supply 744 // non-default values for some configs so that we can retrieve resources 745 // in the app that don't have a default. The most important of these is 746 // the API version because key resources like icons will have an implicit 747 // version if they are using newer config types like density. 748 ResTable_config config; 749 memset(&config, 0, sizeof(ResTable_config)); 750 config.language[0] = 'e'; 751 config.language[1] = 'n'; 752 config.country[0] = 'U'; 753 config.country[1] = 'S'; 754 config.orientation = ResTable_config::ORIENTATION_PORT; 755 config.density = ResTable_config::DENSITY_MEDIUM; 756 config.sdkVersion = 10000; // Very high. 757 config.screenWidthDp = 320; 758 config.screenHeightDp = 480; 759 config.smallestScreenWidthDp = 320; 760 config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL; 761 assets.setConfiguration(config); 762 763 const ResTable& res = assets.getResources(false); 764 if (&res == NULL) { 765 fprintf(stderr, "ERROR: dump failed because no resource table was found\n"); 766 return 1; 767 } else if (res.getError() != NO_ERROR) { 768 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n"); 769 return 1; 770 } 771 772 // The dynamicRefTable can be null if there are no resources for this asset cookie. 773 // This fine. 774 const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie); 775 776 Asset* asset = NULL; 777 778 if (strcmp("resources", option) == 0) { 779#ifndef HAVE_ANDROID_OS 780 res.print(bundle->getValues()); 781#endif 782 783 } else if (strcmp("strings", option) == 0) { 784 const ResStringPool* pool = res.getTableStringBlock(0); 785 printStringPool(pool); 786 787 } else if (strcmp("xmltree", option) == 0) { 788 if (bundle->getFileSpecCount() < 3) { 789 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n"); 790 goto bail; 791 } 792 793 for (int i=2; i<bundle->getFileSpecCount(); i++) { 794 const char* resname = bundle->getFileSpecEntry(i); 795 ResXMLTree tree(dynamicRefTable); 796 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER); 797 if (asset == NULL) { 798 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname); 799 goto bail; 800 } 801 802 if (tree.setTo(asset->getBuffer(true), 803 asset->getLength()) != NO_ERROR) { 804 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname); 805 goto bail; 806 } 807 tree.restart(); 808 printXMLBlock(&tree); 809 tree.uninit(); 810 delete asset; 811 asset = NULL; 812 } 813 814 } else if (strcmp("xmlstrings", option) == 0) { 815 if (bundle->getFileSpecCount() < 3) { 816 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n"); 817 goto bail; 818 } 819 820 for (int i=2; i<bundle->getFileSpecCount(); i++) { 821 const char* resname = bundle->getFileSpecEntry(i); 822 asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER); 823 if (asset == NULL) { 824 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname); 825 goto bail; 826 } 827 828 ResXMLTree tree(dynamicRefTable); 829 if (tree.setTo(asset->getBuffer(true), 830 asset->getLength()) != NO_ERROR) { 831 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname); 832 goto bail; 833 } 834 printStringPool(&tree.getStrings()); 835 delete asset; 836 asset = NULL; 837 } 838 839 } else { 840 asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER); 841 if (asset == NULL) { 842 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n"); 843 goto bail; 844 } 845 846 ResXMLTree tree(dynamicRefTable); 847 if (tree.setTo(asset->getBuffer(true), 848 asset->getLength()) != NO_ERROR) { 849 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n"); 850 goto bail; 851 } 852 tree.restart(); 853 854 if (strcmp("permissions", option) == 0) { 855 size_t len; 856 ResXMLTree::event_code_t code; 857 int depth = 0; 858 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 859 if (code == ResXMLTree::END_TAG) { 860 depth--; 861 continue; 862 } 863 if (code != ResXMLTree::START_TAG) { 864 continue; 865 } 866 depth++; 867 const char16_t* ctag16 = tree.getElementName(&len); 868 if (ctag16 == NULL) { 869 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n"); 870 goto bail; 871 } 872 String8 tag(ctag16); 873 //printf("Depth %d tag %s\n", depth, tag.string()); 874 if (depth == 1) { 875 if (tag != "manifest") { 876 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 877 goto bail; 878 } 879 String8 pkg = getAttribute(tree, NULL, "package", NULL); 880 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string()); 881 } else if (depth == 2 && tag == "permission") { 882 String8 error; 883 String8 name = getAttribute(tree, NAME_ATTR, &error); 884 if (error != "") { 885 fprintf(stderr, "ERROR: %s\n", error.string()); 886 goto bail; 887 } 888 printf("permission: %s\n", 889 ResTable::normalizeForOutput(name.string()).string()); 890 } else if (depth == 2 && tag == "uses-permission") { 891 String8 error; 892 String8 name = getAttribute(tree, NAME_ATTR, &error); 893 if (error != "") { 894 fprintf(stderr, "ERROR: %s\n", error.string()); 895 goto bail; 896 } 897 printUsesPermission(name, 898 getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0, 899 getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1)); 900 } 901 } 902 } else if (strcmp("badging", option) == 0) { 903 Vector<String8> locales; 904 res.getLocales(&locales); 905 906 Vector<ResTable_config> configs; 907 res.getConfigurations(&configs); 908 SortedVector<int> densities; 909 const size_t NC = configs.size(); 910 for (size_t i=0; i<NC; i++) { 911 int dens = configs[i].density; 912 if (dens == 0) { 913 dens = 160; 914 } 915 densities.add(dens); 916 } 917 918 size_t len; 919 ResXMLTree::event_code_t code; 920 int depth = 0; 921 String8 error; 922 bool withinActivity = false; 923 bool isMainActivity = false; 924 bool isLauncherActivity = false; 925 bool isLeanbackLauncherActivity = false; 926 bool isSearchable = false; 927 bool withinApplication = false; 928 bool withinSupportsInput = false; 929 bool withinFeatureGroup = false; 930 bool withinReceiver = false; 931 bool withinService = false; 932 bool withinProvider = false; 933 bool withinIntentFilter = false; 934 bool hasMainActivity = false; 935 bool hasOtherActivities = false; 936 bool hasOtherReceivers = false; 937 bool hasOtherServices = false; 938 bool hasIntentFilter = false; 939 940 bool hasWallpaperService = false; 941 bool hasImeService = false; 942 bool hasAccessibilityService = false; 943 bool hasPrintService = false; 944 bool hasWidgetReceivers = false; 945 bool hasDeviceAdminReceiver = false; 946 bool hasPaymentService = false; 947 bool hasDocumentsProvider = false; 948 bool hasCameraActivity = false; 949 bool hasCameraSecureActivity = false; 950 bool hasLauncher = false; 951 bool hasNotificationListenerService = false; 952 bool hasDreamService = false; 953 954 bool actMainActivity = false; 955 bool actWidgetReceivers = false; 956 bool actDeviceAdminEnabled = false; 957 bool actImeService = false; 958 bool actWallpaperService = false; 959 bool actAccessibilityService = false; 960 bool actPrintService = false; 961 bool actHostApduService = false; 962 bool actOffHostApduService = false; 963 bool actDocumentsProvider = false; 964 bool actNotificationListenerService = false; 965 bool actDreamService = false; 966 bool actCamera = false; 967 bool actCameraSecure = false; 968 bool catLauncher = false; 969 bool hasMetaHostPaymentCategory = false; 970 bool hasMetaOffHostPaymentCategory = false; 971 972 // These permissions are required by services implementing services 973 // the system binds to (IME, Accessibility, PrintServices, etc.) 974 bool hasBindDeviceAdminPermission = false; 975 bool hasBindInputMethodPermission = false; 976 bool hasBindAccessibilityServicePermission = false; 977 bool hasBindPrintServicePermission = false; 978 bool hasBindNfcServicePermission = false; 979 bool hasRequiredSafAttributes = false; 980 bool hasBindNotificationListenerServicePermission = false; 981 bool hasBindDreamServicePermission = false; 982 983 // These two implement the implicit permissions that are granted 984 // to pre-1.6 applications. 985 bool hasWriteExternalStoragePermission = false; 986 bool hasReadPhoneStatePermission = false; 987 988 // If an app requests write storage, they will also get read storage. 989 bool hasReadExternalStoragePermission = false; 990 991 // Implement transition to read and write call log. 992 bool hasReadContactsPermission = false; 993 bool hasWriteContactsPermission = false; 994 bool hasReadCallLogPermission = false; 995 bool hasWriteCallLogPermission = false; 996 997 // If an app declares itself as multiArch, we report the 998 // native libraries differently. 999 bool hasMultiArch = false; 1000 1001 // This next group of variables is used to implement a group of 1002 // backward-compatibility heuristics necessitated by the addition of 1003 // some new uses-feature constants in 2.1 and 2.2. In most cases, the 1004 // heuristic is "if an app requests a permission but doesn't explicitly 1005 // request the corresponding <uses-feature>, presume it's there anyway". 1006 1007 // 2.2 also added some other features that apps can request, but that 1008 // have no corresponding permission, so we cannot implement any 1009 // back-compatibility heuristic for them. The below are thus unnecessary 1010 // (but are retained here for documentary purposes.) 1011 //bool specCompassFeature = false; 1012 //bool specAccelerometerFeature = false; 1013 //bool specProximityFeature = false; 1014 //bool specAmbientLightFeature = false; 1015 //bool specLiveWallpaperFeature = false; 1016 1017 int targetSdk = 0; 1018 int smallScreen = 1; 1019 int normalScreen = 1; 1020 int largeScreen = 1; 1021 int xlargeScreen = 1; 1022 int anyDensity = 1; 1023 int requiresSmallestWidthDp = 0; 1024 int compatibleWidthLimitDp = 0; 1025 int largestWidthLimitDp = 0; 1026 String8 pkg; 1027 String8 activityName; 1028 String8 activityLabel; 1029 String8 activityIcon; 1030 String8 activityBanner; 1031 String8 receiverName; 1032 String8 serviceName; 1033 Vector<String8> supportedInput; 1034 1035 FeatureGroup commonFeatures; 1036 Vector<FeatureGroup> featureGroups; 1037 KeyedVector<String8, ImpliedFeature> impliedFeatures; 1038 1039 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 1040 if (code == ResXMLTree::END_TAG) { 1041 depth--; 1042 if (depth < 2) { 1043 if (withinSupportsInput && !supportedInput.isEmpty()) { 1044 printf("supports-input: '"); 1045 const size_t N = supportedInput.size(); 1046 for (size_t i=0; i<N; i++) { 1047 printf("%s", ResTable::normalizeForOutput( 1048 supportedInput[i].string()).string()); 1049 if (i != N - 1) { 1050 printf("' '"); 1051 } else { 1052 printf("'\n"); 1053 } 1054 } 1055 supportedInput.clear(); 1056 } 1057 withinApplication = false; 1058 withinSupportsInput = false; 1059 withinFeatureGroup = false; 1060 } else if (depth < 3) { 1061 if (withinActivity && isMainActivity) { 1062 String8 aName(getComponentName(pkg, activityName)); 1063 if (isLauncherActivity) { 1064 printf("launchable-activity:"); 1065 if (aName.length() > 0) { 1066 printf(" name='%s' ", 1067 ResTable::normalizeForOutput(aName.string()).string()); 1068 } 1069 printf(" label='%s' icon='%s'\n", 1070 ResTable::normalizeForOutput(activityLabel.string()).string(), 1071 ResTable::normalizeForOutput(activityIcon.string()).string()); 1072 } 1073 if (isLeanbackLauncherActivity) { 1074 printf("leanback-launchable-activity:"); 1075 if (aName.length() > 0) { 1076 printf(" name='%s' ", 1077 ResTable::normalizeForOutput(aName.string()).string()); 1078 } 1079 printf(" label='%s' icon='%s' banner='%s'\n", 1080 ResTable::normalizeForOutput(activityLabel.string()).string(), 1081 ResTable::normalizeForOutput(activityIcon.string()).string(), 1082 ResTable::normalizeForOutput(activityBanner.string()).string()); 1083 } 1084 } 1085 if (!hasIntentFilter) { 1086 hasOtherActivities |= withinActivity; 1087 hasOtherReceivers |= withinReceiver; 1088 hasOtherServices |= withinService; 1089 } else { 1090 if (withinService) { 1091 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory && 1092 hasBindNfcServicePermission); 1093 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory && 1094 hasBindNfcServicePermission); 1095 } 1096 } 1097 withinActivity = false; 1098 withinService = false; 1099 withinReceiver = false; 1100 withinProvider = false; 1101 hasIntentFilter = false; 1102 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false; 1103 } else if (depth < 4) { 1104 if (withinIntentFilter) { 1105 if (withinActivity) { 1106 hasMainActivity |= actMainActivity; 1107 hasLauncher |= catLauncher; 1108 hasCameraActivity |= actCamera; 1109 hasCameraSecureActivity |= actCameraSecure; 1110 hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure; 1111 } else if (withinReceiver) { 1112 hasWidgetReceivers |= actWidgetReceivers; 1113 hasDeviceAdminReceiver |= (actDeviceAdminEnabled && 1114 hasBindDeviceAdminPermission); 1115 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled); 1116 } else if (withinService) { 1117 hasImeService |= actImeService; 1118 hasWallpaperService |= actWallpaperService; 1119 hasAccessibilityService |= (actAccessibilityService && 1120 hasBindAccessibilityServicePermission); 1121 hasPrintService |= (actPrintService && hasBindPrintServicePermission); 1122 hasNotificationListenerService |= actNotificationListenerService && 1123 hasBindNotificationListenerServicePermission; 1124 hasDreamService |= actDreamService && hasBindDreamServicePermission; 1125 hasOtherServices |= (!actImeService && !actWallpaperService && 1126 !actAccessibilityService && !actPrintService && 1127 !actHostApduService && !actOffHostApduService && 1128 !actNotificationListenerService); 1129 } else if (withinProvider) { 1130 hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes; 1131 } 1132 } 1133 withinIntentFilter = false; 1134 } 1135 continue; 1136 } 1137 if (code != ResXMLTree::START_TAG) { 1138 continue; 1139 } 1140 depth++; 1141 1142 const char16_t* ctag16 = tree.getElementName(&len); 1143 if (ctag16 == NULL) { 1144 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n"); 1145 goto bail; 1146 } 1147 String8 tag(ctag16); 1148 //printf("Depth %d, %s\n", depth, tag.string()); 1149 if (depth == 1) { 1150 if (tag != "manifest") { 1151 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 1152 goto bail; 1153 } 1154 pkg = getAttribute(tree, NULL, "package", NULL); 1155 printf("package: name='%s' ", 1156 ResTable::normalizeForOutput(pkg.string()).string()); 1157 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error); 1158 if (error != "") { 1159 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string()); 1160 goto bail; 1161 } 1162 if (versionCode > 0) { 1163 printf("versionCode='%d' ", versionCode); 1164 } else { 1165 printf("versionCode='' "); 1166 } 1167 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error); 1168 if (error != "") { 1169 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string()); 1170 goto bail; 1171 } 1172 printf("versionName='%s'", 1173 ResTable::normalizeForOutput(versionName.string()).string()); 1174 1175 String8 splitName = getAttribute(tree, NULL, "split", NULL); 1176 if (!splitName.isEmpty()) { 1177 printf(" split='%s'", ResTable::normalizeForOutput( 1178 splitName.string()).string()); 1179 } 1180 printf("\n"); 1181 1182 int32_t installLocation = getResolvedIntegerAttribute(&res, tree, 1183 INSTALL_LOCATION_ATTR, &error, -1); 1184 if (error != "") { 1185 fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n", 1186 error.string()); 1187 goto bail; 1188 } 1189 1190 if (installLocation >= 0) { 1191 printf("install-location:'"); 1192 switch (installLocation) { 1193 case 0: 1194 printf("auto"); 1195 break; 1196 case 1: 1197 printf("internalOnly"); 1198 break; 1199 case 2: 1200 printf("preferExternal"); 1201 break; 1202 default: 1203 fprintf(stderr, "Invalid installLocation %d\n", installLocation); 1204 goto bail; 1205 } 1206 printf("'\n"); 1207 } 1208 } else if (depth == 2) { 1209 withinApplication = false; 1210 if (tag == "application") { 1211 withinApplication = true; 1212 1213 String8 label; 1214 const size_t NL = locales.size(); 1215 for (size_t i=0; i<NL; i++) { 1216 const char* localeStr = locales[i].string(); 1217 assets.setLocale(localeStr != NULL ? localeStr : ""); 1218 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); 1219 if (llabel != "") { 1220 if (localeStr == NULL || strlen(localeStr) == 0) { 1221 label = llabel; 1222 printf("application-label:'%s'\n", 1223 ResTable::normalizeForOutput(llabel.string()).string()); 1224 } else { 1225 if (label == "") { 1226 label = llabel; 1227 } 1228 printf("application-label-%s:'%s'\n", localeStr, 1229 ResTable::normalizeForOutput(llabel.string()).string()); 1230 } 1231 } 1232 } 1233 1234 ResTable_config tmpConfig = config; 1235 const size_t ND = densities.size(); 1236 for (size_t i=0; i<ND; i++) { 1237 tmpConfig.density = densities[i]; 1238 assets.setConfiguration(tmpConfig); 1239 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 1240 if (icon != "") { 1241 printf("application-icon-%d:'%s'\n", densities[i], 1242 ResTable::normalizeForOutput(icon.string()).string()); 1243 } 1244 } 1245 assets.setConfiguration(config); 1246 1247 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 1248 if (error != "") { 1249 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string()); 1250 goto bail; 1251 } 1252 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0); 1253 if (error != "") { 1254 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string()); 1255 goto bail; 1256 } 1257 printf("application: label='%s' ", 1258 ResTable::normalizeForOutput(label.string()).string()); 1259 printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string()); 1260 if (testOnly != 0) { 1261 printf("testOnly='%d'\n", testOnly); 1262 } 1263 1264 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0); 1265 if (error != "") { 1266 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string()); 1267 goto bail; 1268 } 1269 if (debuggable != 0) { 1270 printf("application-debuggable\n"); 1271 } 1272 1273 // We must search by name because the multiArch flag hasn't been API 1274 // frozen yet. 1275 int32_t multiArchIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, 1276 "multiArch"); 1277 if (multiArchIndex >= 0) { 1278 Res_value value; 1279 if (tree.getAttributeValue(multiArchIndex, &value) != NO_ERROR) { 1280 if (value.dataType >= Res_value::TYPE_FIRST_INT && 1281 value.dataType <= Res_value::TYPE_LAST_INT) { 1282 hasMultiArch = value.data; 1283 } 1284 } 1285 } 1286 } else if (tag == "uses-sdk") { 1287 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error); 1288 if (error != "") { 1289 error = ""; 1290 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error); 1291 if (error != "") { 1292 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n", 1293 error.string()); 1294 goto bail; 1295 } 1296 if (name == "Donut") targetSdk = 4; 1297 printf("sdkVersion:'%s'\n", 1298 ResTable::normalizeForOutput(name.string()).string()); 1299 } else if (code != -1) { 1300 targetSdk = code; 1301 printf("sdkVersion:'%d'\n", code); 1302 } 1303 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1); 1304 if (code != -1) { 1305 printf("maxSdkVersion:'%d'\n", code); 1306 } 1307 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error); 1308 if (error != "") { 1309 error = ""; 1310 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error); 1311 if (error != "") { 1312 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n", 1313 error.string()); 1314 goto bail; 1315 } 1316 if (name == "Donut" && targetSdk < 4) targetSdk = 4; 1317 printf("targetSdkVersion:'%s'\n", 1318 ResTable::normalizeForOutput(name.string()).string()); 1319 } else if (code != -1) { 1320 if (targetSdk < code) { 1321 targetSdk = code; 1322 } 1323 printf("targetSdkVersion:'%d'\n", code); 1324 } 1325 } else if (tag == "uses-configuration") { 1326 int32_t reqTouchScreen = getIntegerAttribute(tree, 1327 REQ_TOUCH_SCREEN_ATTR, NULL, 0); 1328 int32_t reqKeyboardType = getIntegerAttribute(tree, 1329 REQ_KEYBOARD_TYPE_ATTR, NULL, 0); 1330 int32_t reqHardKeyboard = getIntegerAttribute(tree, 1331 REQ_HARD_KEYBOARD_ATTR, NULL, 0); 1332 int32_t reqNavigation = getIntegerAttribute(tree, 1333 REQ_NAVIGATION_ATTR, NULL, 0); 1334 int32_t reqFiveWayNav = getIntegerAttribute(tree, 1335 REQ_FIVE_WAY_NAV_ATTR, NULL, 0); 1336 printf("uses-configuration:"); 1337 if (reqTouchScreen != 0) { 1338 printf(" reqTouchScreen='%d'", reqTouchScreen); 1339 } 1340 if (reqKeyboardType != 0) { 1341 printf(" reqKeyboardType='%d'", reqKeyboardType); 1342 } 1343 if (reqHardKeyboard != 0) { 1344 printf(" reqHardKeyboard='%d'", reqHardKeyboard); 1345 } 1346 if (reqNavigation != 0) { 1347 printf(" reqNavigation='%d'", reqNavigation); 1348 } 1349 if (reqFiveWayNav != 0) { 1350 printf(" reqFiveWayNav='%d'", reqFiveWayNav); 1351 } 1352 printf("\n"); 1353 } else if (tag == "supports-input") { 1354 withinSupportsInput = true; 1355 } else if (tag == "supports-screens") { 1356 smallScreen = getIntegerAttribute(tree, 1357 SMALL_SCREEN_ATTR, NULL, 1); 1358 normalScreen = getIntegerAttribute(tree, 1359 NORMAL_SCREEN_ATTR, NULL, 1); 1360 largeScreen = getIntegerAttribute(tree, 1361 LARGE_SCREEN_ATTR, NULL, 1); 1362 xlargeScreen = getIntegerAttribute(tree, 1363 XLARGE_SCREEN_ATTR, NULL, 1); 1364 anyDensity = getIntegerAttribute(tree, 1365 ANY_DENSITY_ATTR, NULL, 1); 1366 requiresSmallestWidthDp = getIntegerAttribute(tree, 1367 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0); 1368 compatibleWidthLimitDp = getIntegerAttribute(tree, 1369 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0); 1370 largestWidthLimitDp = getIntegerAttribute(tree, 1371 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0); 1372 } else if (tag == "feature-group") { 1373 withinFeatureGroup = true; 1374 FeatureGroup group; 1375 group.label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); 1376 if (error != "") { 1377 fprintf(stderr, "ERROR getting 'android:label' attribute:" 1378 " %s\n", error.string()); 1379 goto bail; 1380 } 1381 featureGroups.add(group); 1382 1383 } else if (tag == "uses-feature") { 1384 String8 name = getAttribute(tree, NAME_ATTR, &error); 1385 if (name != "" && error == "") { 1386 int req = getIntegerAttribute(tree, 1387 REQUIRED_ATTR, NULL, 1); 1388 1389 commonFeatures.features.add(name, req); 1390 if (req) { 1391 addParentFeatures(&commonFeatures, name); 1392 } 1393 } else { 1394 int vers = getIntegerAttribute(tree, 1395 GL_ES_VERSION_ATTR, &error); 1396 if (error == "") { 1397 if (vers > commonFeatures.openGLESVersion) { 1398 commonFeatures.openGLESVersion = vers; 1399 } 1400 } 1401 } 1402 } else if (tag == "uses-permission") { 1403 String8 name = getAttribute(tree, NAME_ATTR, &error); 1404 if (name != "" && error == "") { 1405 if (name == "android.permission.CAMERA") { 1406 addImpliedFeature(&impliedFeatures, "android.hardware.camera", 1407 String8::format("requested %s permission", name.string()) 1408 .string()); 1409 } else if (name == "android.permission.ACCESS_FINE_LOCATION") { 1410 addImpliedFeature(&impliedFeatures, "android.hardware.location.gps", 1411 String8::format("requested %s permission", name.string()) 1412 .string()); 1413 addImpliedFeature(&impliedFeatures, "android.hardware.location", 1414 String8::format("requested %s permission", name.string()) 1415 .string()); 1416 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") { 1417 addImpliedFeature(&impliedFeatures, "android.hardware.location", 1418 String8::format("requested %s permission", name.string()) 1419 .string()); 1420 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") { 1421 addImpliedFeature(&impliedFeatures, "android.hardware.location.network", 1422 String8::format("requested %s permission", name.string()) 1423 .string()); 1424 addImpliedFeature(&impliedFeatures, "android.hardware.location", 1425 String8::format("requested %s permission", name.string()) 1426 .string()); 1427 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" || 1428 name == "android.permission.INSTALL_LOCATION_PROVIDER") { 1429 addImpliedFeature(&impliedFeatures, "android.hardware.location", 1430 String8::format("requested %s permission", name.string()) 1431 .string()); 1432 } else if (name == "android.permission.BLUETOOTH" || 1433 name == "android.permission.BLUETOOTH_ADMIN") { 1434 if (targetSdk > 4) { 1435 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth", 1436 String8::format("requested %s permission", name.string()) 1437 .string()); 1438 addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth", 1439 "targetSdkVersion > 4"); 1440 } 1441 } else if (name == "android.permission.RECORD_AUDIO") { 1442 addImpliedFeature(&impliedFeatures, "android.hardware.microphone", 1443 String8::format("requested %s permission", name.string()) 1444 .string()); 1445 } else if (name == "android.permission.ACCESS_WIFI_STATE" || 1446 name == "android.permission.CHANGE_WIFI_STATE" || 1447 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") { 1448 addImpliedFeature(&impliedFeatures, "android.hardware.wifi", 1449 String8::format("requested %s permission", name.string()) 1450 .string()); 1451 } else if (name == "android.permission.CALL_PHONE" || 1452 name == "android.permission.CALL_PRIVILEGED" || 1453 name == "android.permission.MODIFY_PHONE_STATE" || 1454 name == "android.permission.PROCESS_OUTGOING_CALLS" || 1455 name == "android.permission.READ_SMS" || 1456 name == "android.permission.RECEIVE_SMS" || 1457 name == "android.permission.RECEIVE_MMS" || 1458 name == "android.permission.RECEIVE_WAP_PUSH" || 1459 name == "android.permission.SEND_SMS" || 1460 name == "android.permission.WRITE_APN_SETTINGS" || 1461 name == "android.permission.WRITE_SMS") { 1462 addImpliedFeature(&impliedFeatures, "android.hardware.telephony", 1463 String8("requested a telephony permission").string()); 1464 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") { 1465 hasWriteExternalStoragePermission = true; 1466 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") { 1467 hasReadExternalStoragePermission = true; 1468 } else if (name == "android.permission.READ_PHONE_STATE") { 1469 hasReadPhoneStatePermission = true; 1470 } else if (name == "android.permission.READ_CONTACTS") { 1471 hasReadContactsPermission = true; 1472 } else if (name == "android.permission.WRITE_CONTACTS") { 1473 hasWriteContactsPermission = true; 1474 } else if (name == "android.permission.READ_CALL_LOG") { 1475 hasReadCallLogPermission = true; 1476 } else if (name == "android.permission.WRITE_CALL_LOG") { 1477 hasWriteCallLogPermission = true; 1478 } 1479 1480 printUsesPermission(name, 1481 getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0, 1482 getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1)); 1483 } else { 1484 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1485 error.string()); 1486 goto bail; 1487 } 1488 } else if (tag == "uses-package") { 1489 String8 name = getAttribute(tree, NAME_ATTR, &error); 1490 if (name != "" && error == "") { 1491 printf("uses-package:'%s'\n", 1492 ResTable::normalizeForOutput(name.string()).string()); 1493 } else { 1494 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1495 error.string()); 1496 goto bail; 1497 } 1498 } else if (tag == "original-package") { 1499 String8 name = getAttribute(tree, NAME_ATTR, &error); 1500 if (name != "" && error == "") { 1501 printf("original-package:'%s'\n", 1502 ResTable::normalizeForOutput(name.string()).string()); 1503 } else { 1504 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1505 error.string()); 1506 goto bail; 1507 } 1508 } else if (tag == "supports-gl-texture") { 1509 String8 name = getAttribute(tree, NAME_ATTR, &error); 1510 if (name != "" && error == "") { 1511 printf("supports-gl-texture:'%s'\n", 1512 ResTable::normalizeForOutput(name.string()).string()); 1513 } else { 1514 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1515 error.string()); 1516 goto bail; 1517 } 1518 } else if (tag == "compatible-screens") { 1519 printCompatibleScreens(tree, &error); 1520 if (error != "") { 1521 fprintf(stderr, "ERROR getting compatible screens: %s\n", 1522 error.string()); 1523 goto bail; 1524 } 1525 depth--; 1526 } else if (tag == "package-verifier") { 1527 String8 name = getAttribute(tree, NAME_ATTR, &error); 1528 if (name != "" && error == "") { 1529 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error); 1530 if (publicKey != "" && error == "") { 1531 printf("package-verifier: name='%s' publicKey='%s'\n", 1532 ResTable::normalizeForOutput(name.string()).string(), 1533 ResTable::normalizeForOutput(publicKey.string()).string()); 1534 } 1535 } 1536 } 1537 } else if (depth == 3) { 1538 withinActivity = false; 1539 withinReceiver = false; 1540 withinService = false; 1541 withinProvider = false; 1542 hasIntentFilter = false; 1543 hasMetaHostPaymentCategory = false; 1544 hasMetaOffHostPaymentCategory = false; 1545 hasBindDeviceAdminPermission = false; 1546 hasBindInputMethodPermission = false; 1547 hasBindAccessibilityServicePermission = false; 1548 hasBindPrintServicePermission = false; 1549 hasBindNfcServicePermission = false; 1550 hasRequiredSafAttributes = false; 1551 hasBindNotificationListenerServicePermission = false; 1552 hasBindDreamServicePermission = false; 1553 if (withinApplication) { 1554 if(tag == "activity") { 1555 withinActivity = true; 1556 activityName = getAttribute(tree, NAME_ATTR, &error); 1557 if (error != "") { 1558 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1559 error.string()); 1560 goto bail; 1561 } 1562 1563 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); 1564 if (error != "") { 1565 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", 1566 error.string()); 1567 goto bail; 1568 } 1569 1570 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); 1571 if (error != "") { 1572 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", 1573 error.string()); 1574 goto bail; 1575 } 1576 1577 activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error); 1578 if (error != "") { 1579 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n", 1580 error.string()); 1581 goto bail; 1582 } 1583 1584 int32_t orien = getResolvedIntegerAttribute(&res, tree, 1585 SCREEN_ORIENTATION_ATTR, &error); 1586 if (error == "") { 1587 if (orien == 0 || orien == 6 || orien == 8) { 1588 // Requests landscape, sensorLandscape, or reverseLandscape. 1589 addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape", 1590 "one or more activities have specified a landscape orientation"); 1591 } else if (orien == 1 || orien == 7 || orien == 9) { 1592 // Requests portrait, sensorPortrait, or reversePortrait. 1593 addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait", 1594 "one or more activities have specified a portrait orientation"); 1595 } 1596 } 1597 } else if (tag == "uses-library") { 1598 String8 libraryName = getAttribute(tree, NAME_ATTR, &error); 1599 if (error != "") { 1600 fprintf(stderr, 1601 "ERROR getting 'android:name' attribute for uses-library" 1602 " %s\n", error.string()); 1603 goto bail; 1604 } 1605 int req = getIntegerAttribute(tree, 1606 REQUIRED_ATTR, NULL, 1); 1607 printf("uses-library%s:'%s'\n", 1608 req ? "" : "-not-required", ResTable::normalizeForOutput( 1609 libraryName.string()).string()); 1610 } else if (tag == "receiver") { 1611 withinReceiver = true; 1612 receiverName = getAttribute(tree, NAME_ATTR, &error); 1613 1614 if (error != "") { 1615 fprintf(stderr, 1616 "ERROR getting 'android:name' attribute for receiver:" 1617 " %s\n", error.string()); 1618 goto bail; 1619 } 1620 1621 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error); 1622 if (error == "") { 1623 if (permission == "android.permission.BIND_DEVICE_ADMIN") { 1624 hasBindDeviceAdminPermission = true; 1625 } 1626 } else { 1627 fprintf(stderr, "ERROR getting 'android:permission' attribute for" 1628 " receiver '%s': %s\n", receiverName.string(), error.string()); 1629 } 1630 } else if (tag == "service") { 1631 withinService = true; 1632 serviceName = getAttribute(tree, NAME_ATTR, &error); 1633 1634 if (error != "") { 1635 fprintf(stderr, "ERROR getting 'android:name' attribute for " 1636 "service:%s\n", error.string()); 1637 goto bail; 1638 } 1639 1640 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error); 1641 if (error == "") { 1642 if (permission == "android.permission.BIND_INPUT_METHOD") { 1643 hasBindInputMethodPermission = true; 1644 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") { 1645 hasBindAccessibilityServicePermission = true; 1646 } else if (permission == "android.permission.BIND_PRINT_SERVICE") { 1647 hasBindPrintServicePermission = true; 1648 } else if (permission == "android.permission.BIND_NFC_SERVICE") { 1649 hasBindNfcServicePermission = true; 1650 } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") { 1651 hasBindNotificationListenerServicePermission = true; 1652 } else if (permission == "android.permission.BIND_DREAM_SERVICE") { 1653 hasBindDreamServicePermission = true; 1654 } 1655 } else { 1656 fprintf(stderr, "ERROR getting 'android:permission' attribute for" 1657 " service '%s': %s\n", serviceName.string(), error.string()); 1658 } 1659 } else if (tag == "provider") { 1660 withinProvider = true; 1661 1662 bool exported = getResolvedIntegerAttribute(&res, tree, EXPORTED_ATTR, &error); 1663 if (error != "") { 1664 fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:" 1665 " %s\n", error.string()); 1666 goto bail; 1667 } 1668 1669 bool grantUriPermissions = getResolvedIntegerAttribute(&res, tree, 1670 GRANT_URI_PERMISSIONS_ATTR, &error); 1671 if (error != "") { 1672 fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:" 1673 " %s\n", error.string()); 1674 goto bail; 1675 } 1676 1677 String8 permission = getResolvedAttribute(&res, tree, PERMISSION_ATTR, &error); 1678 if (error != "") { 1679 fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:" 1680 " %s\n", error.string()); 1681 goto bail; 1682 } 1683 1684 hasRequiredSafAttributes |= exported && grantUriPermissions && 1685 permission == "android.permission.MANAGE_DOCUMENTS"; 1686 1687 } else if (bundle->getIncludeMetaData() && tag == "meta-data") { 1688 String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error); 1689 if (error != "") { 1690 fprintf(stderr, "ERROR getting 'android:name' attribute for " 1691 "meta-data:%s\n", error.string()); 1692 goto bail; 1693 } 1694 printf("meta-data: name='%s' ", 1695 ResTable::normalizeForOutput(metaDataName.string()).string()); 1696 printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"), 1697 &error); 1698 if (error != "") { 1699 // Try looking for a RESOURCE_ATTR 1700 error = ""; 1701 printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR, 1702 String8("resource"), &error); 1703 if (error != "") { 1704 fprintf(stderr, "ERROR getting 'android:value' or " 1705 "'android:resource' attribute for " 1706 "meta-data:%s\n", error.string()); 1707 goto bail; 1708 } 1709 } 1710 printf("\n"); 1711 } else if (withinSupportsInput && tag == "input-type") { 1712 String8 name = getAttribute(tree, NAME_ATTR, &error); 1713 if (name != "" && error == "") { 1714 supportedInput.add(name); 1715 } else { 1716 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1717 error.string()); 1718 goto bail; 1719 } 1720 } 1721 } else if (withinFeatureGroup && tag == "uses-feature") { 1722 FeatureGroup& top = featureGroups.editTop(); 1723 1724 String8 name = getResolvedAttribute(&res, tree, NAME_ATTR, &error); 1725 if (name != "" && error == "") { 1726 top.features.add(name, true); 1727 addParentFeatures(&top, name); 1728 } else { 1729 int vers = getIntegerAttribute(tree, GL_ES_VERSION_ATTR, &error); 1730 if (error == "") { 1731 if (vers > top.openGLESVersion) { 1732 top.openGLESVersion = vers; 1733 } 1734 } 1735 } 1736 } 1737 } else if (depth == 4) { 1738 if (tag == "intent-filter") { 1739 hasIntentFilter = true; 1740 withinIntentFilter = true; 1741 actMainActivity = false; 1742 actWidgetReceivers = false; 1743 actImeService = false; 1744 actWallpaperService = false; 1745 actAccessibilityService = false; 1746 actPrintService = false; 1747 actDeviceAdminEnabled = false; 1748 actHostApduService = false; 1749 actOffHostApduService = false; 1750 actDocumentsProvider = false; 1751 actNotificationListenerService = false; 1752 actDreamService = false; 1753 actCamera = false; 1754 actCameraSecure = false; 1755 catLauncher = false; 1756 } else if (withinService && tag == "meta-data") { 1757 String8 name = getAttribute(tree, NAME_ATTR, &error); 1758 if (error != "") { 1759 fprintf(stderr, "ERROR getting 'android:name' attribute for" 1760 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string()); 1761 goto bail; 1762 } 1763 1764 if (name == "android.nfc.cardemulation.host_apdu_service" || 1765 name == "android.nfc.cardemulation.off_host_apdu_service") { 1766 bool offHost = true; 1767 if (name == "android.nfc.cardemulation.host_apdu_service") { 1768 offHost = false; 1769 } 1770 1771 String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error); 1772 if (error != "") { 1773 fprintf(stderr, "ERROR getting 'android:resource' attribute for" 1774 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string()); 1775 goto bail; 1776 } 1777 1778 Vector<String8> categories = getNfcAidCategories(assets, xmlPath, 1779 offHost, &error); 1780 if (error != "") { 1781 fprintf(stderr, "ERROR getting AID category for service '%s'\n", 1782 serviceName.string()); 1783 goto bail; 1784 } 1785 1786 const size_t catLen = categories.size(); 1787 for (size_t i = 0; i < catLen; i++) { 1788 bool paymentCategory = (categories[i] == "payment"); 1789 if (offHost) { 1790 hasMetaOffHostPaymentCategory |= paymentCategory; 1791 } else { 1792 hasMetaHostPaymentCategory |= paymentCategory; 1793 } 1794 } 1795 } 1796 } 1797 } else if ((depth == 5) && withinIntentFilter) { 1798 String8 action; 1799 if (tag == "action") { 1800 action = getAttribute(tree, NAME_ATTR, &error); 1801 if (error != "") { 1802 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", 1803 error.string()); 1804 goto bail; 1805 } 1806 1807 if (withinActivity) { 1808 if (action == "android.intent.action.MAIN") { 1809 isMainActivity = true; 1810 actMainActivity = true; 1811 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" || 1812 action == "android.media.action.VIDEO_CAMERA") { 1813 actCamera = true; 1814 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") { 1815 actCameraSecure = true; 1816 } 1817 } else if (withinReceiver) { 1818 if (action == "android.appwidget.action.APPWIDGET_UPDATE") { 1819 actWidgetReceivers = true; 1820 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") { 1821 actDeviceAdminEnabled = true; 1822 } 1823 } else if (withinService) { 1824 if (action == "android.view.InputMethod") { 1825 actImeService = true; 1826 } else if (action == "android.service.wallpaper.WallpaperService") { 1827 actWallpaperService = true; 1828 } else if (action == "android.accessibilityservice.AccessibilityService") { 1829 actAccessibilityService = true; 1830 } else if (action == "android.printservice.PrintService") { 1831 actPrintService = true; 1832 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") { 1833 actHostApduService = true; 1834 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") { 1835 actOffHostApduService = true; 1836 } else if (action == "android.service.notification.NotificationListenerService") { 1837 actNotificationListenerService = true; 1838 } else if (action == "android.service.dreams.DreamService") { 1839 actDreamService = true; 1840 } 1841 } else if (withinProvider) { 1842 if (action == "android.content.action.DOCUMENTS_PROVIDER") { 1843 actDocumentsProvider = true; 1844 } 1845 } 1846 if (action == "android.intent.action.SEARCH") { 1847 isSearchable = true; 1848 } 1849 } 1850 1851 if (tag == "category") { 1852 String8 category = getAttribute(tree, NAME_ATTR, &error); 1853 if (error != "") { 1854 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", 1855 error.string()); 1856 goto bail; 1857 } 1858 if (withinActivity) { 1859 if (category == "android.intent.category.LAUNCHER") { 1860 isLauncherActivity = true; 1861 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") { 1862 isLeanbackLauncherActivity = true; 1863 } else if (category == "android.intent.category.HOME") { 1864 catLauncher = true; 1865 } 1866 } 1867 } 1868 } 1869 } 1870 1871 // Pre-1.6 implicitly granted permission compatibility logic 1872 if (targetSdk < 4) { 1873 if (!hasWriteExternalStoragePermission) { 1874 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE")); 1875 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"), 1876 String8("targetSdkVersion < 4")); 1877 hasWriteExternalStoragePermission = true; 1878 } 1879 if (!hasReadPhoneStatePermission) { 1880 printUsesPermission(String8("android.permission.READ_PHONE_STATE")); 1881 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"), 1882 String8("targetSdkVersion < 4")); 1883 } 1884 } 1885 1886 // If the application has requested WRITE_EXTERNAL_STORAGE, we will 1887 // force them to always take READ_EXTERNAL_STORAGE as well. We always 1888 // do this (regardless of target API version) because we can't have 1889 // an app with write permission but not read permission. 1890 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) { 1891 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE")); 1892 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"), 1893 String8("requested WRITE_EXTERNAL_STORAGE")); 1894 } 1895 1896 // Pre-JellyBean call log permission compatibility. 1897 if (targetSdk < 16) { 1898 if (!hasReadCallLogPermission && hasReadContactsPermission) { 1899 printUsesPermission(String8("android.permission.READ_CALL_LOG")); 1900 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"), 1901 String8("targetSdkVersion < 16 and requested READ_CONTACTS")); 1902 } 1903 if (!hasWriteCallLogPermission && hasWriteContactsPermission) { 1904 printUsesPermission(String8("android.permission.WRITE_CALL_LOG")); 1905 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"), 1906 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS")); 1907 } 1908 } 1909 1910 addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen", 1911 "default feature for all apps"); 1912 1913 const size_t numFeatureGroups = featureGroups.size(); 1914 if (numFeatureGroups == 0) { 1915 // If no <feature-group> tags were defined, apply auto-implied features. 1916 printFeatureGroup(commonFeatures, &impliedFeatures); 1917 1918 } else { 1919 // <feature-group> tags are defined, so we ignore implied features and 1920 for (size_t i = 0; i < numFeatureGroups; i++) { 1921 FeatureGroup& grp = featureGroups.editItemAt(i); 1922 1923 if (commonFeatures.openGLESVersion > grp.openGLESVersion) { 1924 grp.openGLESVersion = commonFeatures.openGLESVersion; 1925 } 1926 1927 // Merge the features defined in the top level (not inside a <feature-group>) 1928 // with this feature group. 1929 const size_t numCommonFeatures = commonFeatures.features.size(); 1930 for (size_t j = 0; j < numCommonFeatures; j++) { 1931 if (grp.features.indexOfKey(commonFeatures.features.keyAt(j)) < 0) { 1932 grp.features.add(commonFeatures.features.keyAt(j), 1933 commonFeatures.features[j]); 1934 } 1935 } 1936 1937 if (!grp.features.isEmpty()) { 1938 printFeatureGroup(grp); 1939 } 1940 } 1941 } 1942 1943 1944 if (hasWidgetReceivers) { 1945 printComponentPresence("app-widget"); 1946 } 1947 if (hasDeviceAdminReceiver) { 1948 printComponentPresence("device-admin"); 1949 } 1950 if (hasImeService) { 1951 printComponentPresence("ime"); 1952 } 1953 if (hasWallpaperService) { 1954 printComponentPresence("wallpaper"); 1955 } 1956 if (hasAccessibilityService) { 1957 printComponentPresence("accessibility"); 1958 } 1959 if (hasPrintService) { 1960 printComponentPresence("print-service"); 1961 } 1962 if (hasPaymentService) { 1963 printComponentPresence("payment"); 1964 } 1965 if (isSearchable) { 1966 printComponentPresence("search"); 1967 } 1968 if (hasDocumentsProvider) { 1969 printComponentPresence("document-provider"); 1970 } 1971 if (hasLauncher) { 1972 printComponentPresence("launcher"); 1973 } 1974 if (hasNotificationListenerService) { 1975 printComponentPresence("notification-listener"); 1976 } 1977 if (hasDreamService) { 1978 printComponentPresence("dream"); 1979 } 1980 if (hasCameraActivity) { 1981 printComponentPresence("camera"); 1982 } 1983 if (hasCameraSecureActivity) { 1984 printComponentPresence("camera-secure"); 1985 } 1986 1987 if (hasMainActivity) { 1988 printf("main\n"); 1989 } 1990 if (hasOtherActivities) { 1991 printf("other-activities\n"); 1992 } 1993 if (hasOtherReceivers) { 1994 printf("other-receivers\n"); 1995 } 1996 if (hasOtherServices) { 1997 printf("other-services\n"); 1998 } 1999 2000 // For modern apps, if screen size buckets haven't been specified 2001 // but the new width ranges have, then infer the buckets from them. 2002 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0 2003 && requiresSmallestWidthDp > 0) { 2004 int compatWidth = compatibleWidthLimitDp; 2005 if (compatWidth <= 0) { 2006 compatWidth = requiresSmallestWidthDp; 2007 } 2008 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) { 2009 smallScreen = -1; 2010 } else { 2011 smallScreen = 0; 2012 } 2013 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) { 2014 normalScreen = -1; 2015 } else { 2016 normalScreen = 0; 2017 } 2018 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) { 2019 largeScreen = -1; 2020 } else { 2021 largeScreen = 0; 2022 } 2023 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) { 2024 xlargeScreen = -1; 2025 } else { 2026 xlargeScreen = 0; 2027 } 2028 } 2029 2030 // Determine default values for any unspecified screen sizes, 2031 // based on the target SDK of the package. As of 4 (donut) 2032 // the screen size support was introduced, so all default to 2033 // enabled. 2034 if (smallScreen > 0) { 2035 smallScreen = targetSdk >= 4 ? -1 : 0; 2036 } 2037 if (normalScreen > 0) { 2038 normalScreen = -1; 2039 } 2040 if (largeScreen > 0) { 2041 largeScreen = targetSdk >= 4 ? -1 : 0; 2042 } 2043 if (xlargeScreen > 0) { 2044 // Introduced in Gingerbread. 2045 xlargeScreen = targetSdk >= 9 ? -1 : 0; 2046 } 2047 if (anyDensity > 0) { 2048 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0 2049 || compatibleWidthLimitDp > 0) ? -1 : 0; 2050 } 2051 printf("supports-screens:"); 2052 if (smallScreen != 0) { 2053 printf(" 'small'"); 2054 } 2055 if (normalScreen != 0) { 2056 printf(" 'normal'"); 2057 } 2058 if (largeScreen != 0) { 2059 printf(" 'large'"); 2060 } 2061 if (xlargeScreen != 0) { 2062 printf(" 'xlarge'"); 2063 } 2064 printf("\n"); 2065 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false"); 2066 if (requiresSmallestWidthDp > 0) { 2067 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp); 2068 } 2069 if (compatibleWidthLimitDp > 0) { 2070 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp); 2071 } 2072 if (largestWidthLimitDp > 0) { 2073 printf("largest-width-limit:'%d'\n", largestWidthLimitDp); 2074 } 2075 2076 printf("locales:"); 2077 const size_t NL = locales.size(); 2078 for (size_t i=0; i<NL; i++) { 2079 const char* localeStr = locales[i].string(); 2080 if (localeStr == NULL || strlen(localeStr) == 0) { 2081 localeStr = "--_--"; 2082 } 2083 printf(" '%s'", localeStr); 2084 } 2085 printf("\n"); 2086 2087 printf("densities:"); 2088 const size_t ND = densities.size(); 2089 for (size_t i=0; i<ND; i++) { 2090 printf(" '%d'", densities[i]); 2091 } 2092 printf("\n"); 2093 2094 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib"); 2095 if (dir != NULL) { 2096 if (dir->getFileCount() > 0) { 2097 SortedVector<String8> architectures; 2098 for (size_t i=0; i<dir->getFileCount(); i++) { 2099 architectures.add(ResTable::normalizeForOutput( 2100 dir->getFileName(i).string())); 2101 } 2102 2103 bool outputAltNativeCode = false; 2104 // A multiArch package is one that contains 64-bit and 2105 // 32-bit versions of native code and expects 3rd-party 2106 // apps to load these native code libraries. Since most 2107 // 64-bit systems also support 32-bit apps, the apps 2108 // loading this multiArch package's code may be either 2109 // 32-bit or 64-bit. 2110 if (hasMultiArch) { 2111 // If this is a multiArch package, report the 64-bit 2112 // version only. Then as a separate entry, report the 2113 // rest. 2114 // 2115 // If we report the 32-bit architecture, this APK will 2116 // be installed on a 32-bit device, causing a large waste 2117 // of bandwidth and disk space. This assumes that 2118 // the developer of the multiArch package has also 2119 // made a version that is 32-bit only. 2120 String8 intel64("x86_64"); 2121 String8 arm64("arm64-v8a"); 2122 ssize_t index = architectures.indexOf(intel64); 2123 if (index < 0) { 2124 index = architectures.indexOf(arm64); 2125 } 2126 2127 if (index >= 0) { 2128 printf("native-code: '%s'\n", architectures[index].string()); 2129 architectures.removeAt(index); 2130 outputAltNativeCode = true; 2131 } 2132 } 2133 2134 const size_t archCount = architectures.size(); 2135 if (archCount > 0) { 2136 if (outputAltNativeCode) { 2137 printf("alt-"); 2138 } 2139 printf("native-code:"); 2140 for (size_t i = 0; i < archCount; i++) { 2141 printf(" '%s'", architectures[i].string()); 2142 } 2143 printf("\n"); 2144 } 2145 } 2146 delete dir; 2147 } 2148 } else if (strcmp("badger", option) == 0) { 2149 printf("%s", CONSOLE_DATA); 2150 } else if (strcmp("configurations", option) == 0) { 2151 Vector<ResTable_config> configs; 2152 res.getConfigurations(&configs); 2153 const size_t N = configs.size(); 2154 for (size_t i=0; i<N; i++) { 2155 printf("%s\n", configs[i].toString().string()); 2156 } 2157 } else { 2158 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option); 2159 goto bail; 2160 } 2161 } 2162 2163 result = NO_ERROR; 2164 2165bail: 2166 if (asset) { 2167 delete asset; 2168 } 2169 return (result != NO_ERROR); 2170} 2171 2172 2173/* 2174 * Handle the "add" command, which wants to add files to a new or 2175 * pre-existing archive. 2176 */ 2177int doAdd(Bundle* bundle) 2178{ 2179 ZipFile* zip = NULL; 2180 status_t result = UNKNOWN_ERROR; 2181 const char* zipFileName; 2182 2183 if (bundle->getUpdate()) { 2184 /* avoid confusion */ 2185 fprintf(stderr, "ERROR: can't use '-u' with add\n"); 2186 goto bail; 2187 } 2188 2189 if (bundle->getFileSpecCount() < 1) { 2190 fprintf(stderr, "ERROR: must specify zip file name\n"); 2191 goto bail; 2192 } 2193 zipFileName = bundle->getFileSpecEntry(0); 2194 2195 if (bundle->getFileSpecCount() < 2) { 2196 fprintf(stderr, "NOTE: nothing to do\n"); 2197 goto bail; 2198 } 2199 2200 zip = openReadWrite(zipFileName, true); 2201 if (zip == NULL) { 2202 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName); 2203 goto bail; 2204 } 2205 2206 for (int i = 1; i < bundle->getFileSpecCount(); i++) { 2207 const char* fileName = bundle->getFileSpecEntry(i); 2208 2209 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) { 2210 printf(" '%s'... (from gzip)\n", fileName); 2211 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL); 2212 } else { 2213 if (bundle->getJunkPath()) { 2214 String8 storageName = String8(fileName).getPathLeaf(); 2215 printf(" '%s' as '%s'...\n", fileName, 2216 ResTable::normalizeForOutput(storageName.string()).string()); 2217 result = zip->add(fileName, storageName.string(), 2218 bundle->getCompressionMethod(), NULL); 2219 } else { 2220 printf(" '%s'...\n", fileName); 2221 result = zip->add(fileName, bundle->getCompressionMethod(), NULL); 2222 } 2223 } 2224 if (result != NO_ERROR) { 2225 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName); 2226 if (result == NAME_NOT_FOUND) { 2227 fprintf(stderr, ": file not found\n"); 2228 } else if (result == ALREADY_EXISTS) { 2229 fprintf(stderr, ": already exists in archive\n"); 2230 } else { 2231 fprintf(stderr, "\n"); 2232 } 2233 goto bail; 2234 } 2235 } 2236 2237 result = NO_ERROR; 2238 2239bail: 2240 delete zip; 2241 return (result != NO_ERROR); 2242} 2243 2244 2245/* 2246 * Delete files from an existing archive. 2247 */ 2248int doRemove(Bundle* bundle) 2249{ 2250 ZipFile* zip = NULL; 2251 status_t result = UNKNOWN_ERROR; 2252 const char* zipFileName; 2253 2254 if (bundle->getFileSpecCount() < 1) { 2255 fprintf(stderr, "ERROR: must specify zip file name\n"); 2256 goto bail; 2257 } 2258 zipFileName = bundle->getFileSpecEntry(0); 2259 2260 if (bundle->getFileSpecCount() < 2) { 2261 fprintf(stderr, "NOTE: nothing to do\n"); 2262 goto bail; 2263 } 2264 2265 zip = openReadWrite(zipFileName, false); 2266 if (zip == NULL) { 2267 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n", 2268 zipFileName); 2269 goto bail; 2270 } 2271 2272 for (int i = 1; i < bundle->getFileSpecCount(); i++) { 2273 const char* fileName = bundle->getFileSpecEntry(i); 2274 ZipEntry* entry; 2275 2276 entry = zip->getEntryByName(fileName); 2277 if (entry == NULL) { 2278 printf(" '%s' NOT FOUND\n", fileName); 2279 continue; 2280 } 2281 2282 result = zip->remove(entry); 2283 2284 if (result != NO_ERROR) { 2285 fprintf(stderr, "Unable to delete '%s' from '%s'\n", 2286 bundle->getFileSpecEntry(i), zipFileName); 2287 goto bail; 2288 } 2289 } 2290 2291 /* update the archive */ 2292 zip->flush(); 2293 2294bail: 2295 delete zip; 2296 return (result != NO_ERROR); 2297} 2298 2299static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) { 2300 const size_t numDirs = dir->getDirs().size(); 2301 for (size_t i = 0; i < numDirs; i++) { 2302 bool ignore = ignoreConfig; 2303 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i); 2304 const char* dirStr = subDir->getLeaf().string(); 2305 if (!ignore && strstr(dirStr, "mipmap") == dirStr) { 2306 ignore = true; 2307 } 2308 status_t err = addResourcesToBuilder(subDir, builder, ignore); 2309 if (err != NO_ERROR) { 2310 return err; 2311 } 2312 } 2313 2314 const size_t numFiles = dir->getFiles().size(); 2315 for (size_t i = 0; i < numFiles; i++) { 2316 sp<AaptGroup> gp = dir->getFiles().valueAt(i); 2317 const size_t numConfigs = gp->getFiles().size(); 2318 for (size_t j = 0; j < numConfigs; j++) { 2319 status_t err = NO_ERROR; 2320 if (ignoreConfig) { 2321 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j)); 2322 } else { 2323 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j)); 2324 } 2325 if (err != NO_ERROR) { 2326 fprintf(stderr, "Failed to add %s (%s) to builder.\n", 2327 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string()); 2328 return err; 2329 } 2330 } 2331 } 2332 return NO_ERROR; 2333} 2334 2335static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) { 2336 if (split->isBase()) { 2337 return original; 2338 } 2339 2340 String8 ext(original.getPathExtension()); 2341 if (ext == String8(".apk")) { 2342 return String8::format("%s_%s%s", 2343 original.getBasePath().string(), 2344 split->getDirectorySafeName().string(), 2345 ext.string()); 2346 } 2347 2348 return String8::format("%s_%s", original.string(), 2349 split->getDirectorySafeName().string()); 2350} 2351 2352/* 2353 * Package up an asset directory and associated application files. 2354 */ 2355int doPackage(Bundle* bundle) 2356{ 2357 const char* outputAPKFile; 2358 int retVal = 1; 2359 status_t err; 2360 sp<AaptAssets> assets; 2361 int N; 2362 FILE* fp; 2363 String8 dependencyFile; 2364 sp<ApkBuilder> builder; 2365 2366 // -c en_XA or/and ar_XB means do pseudolocalization 2367 sp<WeakResourceFilter> configFilter = new WeakResourceFilter(); 2368 err = configFilter->parse(bundle->getConfigurations()); 2369 if (err != NO_ERROR) { 2370 goto bail; 2371 } 2372 if (configFilter->containsPseudo()) { 2373 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED); 2374 } 2375 if (configFilter->containsPseudoBidi()) { 2376 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI); 2377 } 2378 2379 N = bundle->getFileSpecCount(); 2380 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0 2381 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) { 2382 fprintf(stderr, "ERROR: no input files\n"); 2383 goto bail; 2384 } 2385 2386 outputAPKFile = bundle->getOutputAPKFile(); 2387 2388 // Make sure the filenames provided exist and are of the appropriate type. 2389 if (outputAPKFile) { 2390 FileType type; 2391 type = getFileType(outputAPKFile); 2392 if (type != kFileTypeNonexistent && type != kFileTypeRegular) { 2393 fprintf(stderr, 2394 "ERROR: output file '%s' exists but is not regular file\n", 2395 outputAPKFile); 2396 goto bail; 2397 } 2398 } 2399 2400 // Load the assets. 2401 assets = new AaptAssets(); 2402 2403 // Set up the resource gathering in assets if we're going to generate 2404 // dependency files. Every time we encounter a resource while slurping 2405 // the tree, we'll add it to these stores so we have full resource paths 2406 // to write to a dependency file. 2407 if (bundle->getGenDependencies()) { 2408 sp<FilePathStore> resPathStore = new FilePathStore; 2409 assets->setFullResPaths(resPathStore); 2410 sp<FilePathStore> assetPathStore = new FilePathStore; 2411 assets->setFullAssetPaths(assetPathStore); 2412 } 2413 2414 err = assets->slurpFromArgs(bundle); 2415 if (err < 0) { 2416 goto bail; 2417 } 2418 2419 if (bundle->getVerbose()) { 2420 assets->print(String8()); 2421 } 2422 2423 // Create the ApkBuilder, which will collect the compiled files 2424 // to write to the final APK (or sets of APKs if we are building 2425 // a Split APK. 2426 builder = new ApkBuilder(configFilter); 2427 2428 // If we are generating a Split APK, find out which configurations to split on. 2429 if (bundle->getSplitConfigurations().size() > 0) { 2430 const Vector<String8>& splitStrs = bundle->getSplitConfigurations(); 2431 const size_t numSplits = splitStrs.size(); 2432 for (size_t i = 0; i < numSplits; i++) { 2433 std::set<ConfigDescription> configs; 2434 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) { 2435 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string()); 2436 goto bail; 2437 } 2438 2439 err = builder->createSplitForConfigs(configs); 2440 if (err != NO_ERROR) { 2441 goto bail; 2442 } 2443 } 2444 } 2445 2446 // If they asked for any fileAs that need to be compiled, do so. 2447 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) { 2448 err = buildResources(bundle, assets, builder); 2449 if (err != 0) { 2450 goto bail; 2451 } 2452 } 2453 2454 // At this point we've read everything and processed everything. From here 2455 // on out it's just writing output files. 2456 if (SourcePos::hasErrors()) { 2457 goto bail; 2458 } 2459 2460 // Update symbols with information about which ones are needed as Java symbols. 2461 assets->applyJavaSymbols(); 2462 if (SourcePos::hasErrors()) { 2463 goto bail; 2464 } 2465 2466 // If we've been asked to generate a dependency file, do that here 2467 if (bundle->getGenDependencies()) { 2468 // If this is the packaging step, generate the dependency file next to 2469 // the output apk (e.g. bin/resources.ap_.d) 2470 if (outputAPKFile) { 2471 dependencyFile = String8(outputAPKFile); 2472 // Add the .d extension to the dependency file. 2473 dependencyFile.append(".d"); 2474 } else { 2475 // Else if this is the R.java dependency generation step, 2476 // generate the dependency file in the R.java package subdirectory 2477 // e.g. gen/com/foo/app/R.java.d 2478 dependencyFile = String8(bundle->getRClassDir()); 2479 dependencyFile.appendPath("R.java.d"); 2480 } 2481 // Make sure we have a clean dependency file to start with 2482 fp = fopen(dependencyFile, "w"); 2483 fclose(fp); 2484 } 2485 2486 // Write out R.java constants 2487 if (!assets->havePrivateSymbols()) { 2488 if (bundle->getCustomPackage() == NULL) { 2489 // Write the R.java file into the appropriate class directory 2490 // e.g. gen/com/foo/app/R.java 2491 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true); 2492 } else { 2493 const String8 customPkg(bundle->getCustomPackage()); 2494 err = writeResourceSymbols(bundle, assets, customPkg, true); 2495 } 2496 if (err < 0) { 2497 goto bail; 2498 } 2499 // If we have library files, we're going to write our R.java file into 2500 // the appropriate class directory for those libraries as well. 2501 // e.g. gen/com/foo/app/lib/R.java 2502 if (bundle->getExtraPackages() != NULL) { 2503 // Split on colon 2504 String8 libs(bundle->getExtraPackages()); 2505 char* packageString = strtok(libs.lockBuffer(libs.length()), ":"); 2506 while (packageString != NULL) { 2507 // Write the R.java file out with the correct package name 2508 err = writeResourceSymbols(bundle, assets, String8(packageString), true); 2509 if (err < 0) { 2510 goto bail; 2511 } 2512 packageString = strtok(NULL, ":"); 2513 } 2514 libs.unlockBuffer(); 2515 } 2516 } else { 2517 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false); 2518 if (err < 0) { 2519 goto bail; 2520 } 2521 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true); 2522 if (err < 0) { 2523 goto bail; 2524 } 2525 } 2526 2527 // Write out the ProGuard file 2528 err = writeProguardFile(bundle, assets); 2529 if (err < 0) { 2530 goto bail; 2531 } 2532 2533 // Write the apk 2534 if (outputAPKFile) { 2535 // Gather all resources and add them to the APK Builder. The builder will then 2536 // figure out which Split they belong in. 2537 err = addResourcesToBuilder(assets, builder); 2538 if (err != NO_ERROR) { 2539 goto bail; 2540 } 2541 2542 const Vector<sp<ApkSplit> >& splits = builder->getSplits(); 2543 const size_t numSplits = splits.size(); 2544 for (size_t i = 0; i < numSplits; i++) { 2545 const sp<ApkSplit>& split = splits[i]; 2546 String8 outputPath = buildApkName(String8(outputAPKFile), split); 2547 err = writeAPK(bundle, outputPath, split); 2548 if (err != NO_ERROR) { 2549 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string()); 2550 goto bail; 2551 } 2552 } 2553 } 2554 2555 // If we've been asked to generate a dependency file, we need to finish up here. 2556 // the writeResourceSymbols and writeAPK functions have already written the target 2557 // half of the dependency file, now we need to write the prerequisites. (files that 2558 // the R.java file or .ap_ file depend on) 2559 if (bundle->getGenDependencies()) { 2560 // Now that writeResourceSymbols or writeAPK has taken care of writing 2561 // the targets to our dependency file, we'll write the prereqs 2562 fp = fopen(dependencyFile, "a+"); 2563 fprintf(fp, " : "); 2564 bool includeRaw = (outputAPKFile != NULL); 2565 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw); 2566 // Also manually add the AndroidManifeset since it's not under res/ or assets/ 2567 // and therefore was not added to our pathstores during slurping 2568 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile()); 2569 fclose(fp); 2570 } 2571 2572 retVal = 0; 2573bail: 2574 if (SourcePos::hasErrors()) { 2575 SourcePos::printErrors(stderr); 2576 } 2577 return retVal; 2578} 2579 2580/* 2581 * Do PNG Crunching 2582 * PRECONDITIONS 2583 * -S flag points to a source directory containing drawable* folders 2584 * -C flag points to destination directory. The folder structure in the 2585 * source directory will be mirrored to the destination (cache) directory 2586 * 2587 * POSTCONDITIONS 2588 * Destination directory will be updated to match the PNG files in 2589 * the source directory. 2590 */ 2591int doCrunch(Bundle* bundle) 2592{ 2593 fprintf(stdout, "Crunching PNG Files in "); 2594 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]); 2595 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir()); 2596 2597 updatePreProcessedCache(bundle); 2598 2599 return NO_ERROR; 2600} 2601 2602/* 2603 * Do PNG Crunching on a single flag 2604 * -i points to a single png file 2605 * -o points to a single png output file 2606 */ 2607int doSingleCrunch(Bundle* bundle) 2608{ 2609 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile()); 2610 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile()); 2611 2612 String8 input(bundle->getSingleCrunchInputFile()); 2613 String8 output(bundle->getSingleCrunchOutputFile()); 2614 2615 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) { 2616 // we can't return the status_t as it gets truncate to the lower 8 bits. 2617 return 42; 2618 } 2619 2620 return NO_ERROR; 2621} 2622 2623char CONSOLE_DATA[2925] = { 2624 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2625 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2626 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2627 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 2628 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63, 2629 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83, 2630 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2631 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2632 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81, 2633 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32, 2634 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 2635 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2636 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59, 2637 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2638 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2639 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 2640 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32, 2641 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2642 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2643 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87, 2644 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32, 2645 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 2646 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 2647 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58, 2648 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2649 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2650 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81, 2651 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32, 2652 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 2653 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2654 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59, 2655 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2656 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2657 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59, 2658 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32, 2659 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2660 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2661 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81, 2662 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 2663 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 2664 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2665 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81, 2666 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2667 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2668 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109, 2669 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32, 2670 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 2671 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59, 2672 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59, 2673 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2674 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59, 2675 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46, 2676 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32, 2677 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2678 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 2679 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81, 2680 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32, 2681 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81, 2682 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2683 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58, 2684 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32, 2685 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59, 2686 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37, 2687 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59, 2688 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 2689 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59, 2690 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96, 2691 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 2692 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32, 2693 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61, 2694 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59, 2695 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32, 2696 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2697 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119, 2698 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2699 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10, 2700 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2701 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81, 2702 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41, 2703 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 2704 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81, 2705 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 2706 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32, 2707 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2708 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 2709 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 2710 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 2711 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 2712 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 2713 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2714 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2715 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81, 2716 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32, 2717 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2718 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2719 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2720 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2721 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 2722 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106, 2723 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59, 2724 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2725 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2726 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81, 2727 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 2728 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 2729 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2730 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59, 2731 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2732 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2733 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81, 2734 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32, 2735 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2736 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2737 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2738 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2739 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 2740 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 2741 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59, 2742 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2743 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2744 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33, 2745 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 2746 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 2747 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 2748 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59, 2749 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 2750 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 2751 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95, 2752 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59, 2753 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2754 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2755 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45, 2756 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32, 2757 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 2758 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59, 2759 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59, 2760 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2761 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2762 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32, 2763 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32, 2764 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 2765 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2766 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61, 2767 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2768 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 2769 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 2770 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59, 2771 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2772 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2773 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32, 2774 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32, 2775 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 2776 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61, 2777 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 2778 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2779 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2780 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32, 2781 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32, 2782 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2783 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2784 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2785 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2786 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10 2787 }; 2788