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