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