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