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