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