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