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