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