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