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