2 // Copyright 2006 The Android Open Source Project
4 // Android Asset Packaging Tool main entry point.
8 #include "ResourceFilter.h"
9 #include "ResourceTable.h"
13 #include <utils/Log.h>
14 #include <utils/threads.h>
15 #include <utils/List.h>
16 #include <utils/Errors.h>
21 using namespace android;
24 * Show version info. All the cool kids do it.
26 int doVersion(Bundle* bundle)
28 if (bundle->getFileSpecCount() != 0)
29 printf("(ignoring extra arguments)\n");
30 printf("Android Asset Packaging Tool, v0.2\n");
37 * Open the file read only. The call fails if the file doesn't exist.
39 * Returns NULL on failure.
41 ZipFile* openReadOnly(const char* fileName)
47 result = zip->open(fileName, ZipFile::kOpenReadOnly);
48 if (result != NO_ERROR) {
49 if (result == NAME_NOT_FOUND)
50 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
51 else if (result == PERMISSION_DENIED)
52 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
54 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
64 * Open the file read-write. The file will be created if it doesn't
65 * already exist and "okayToCreate" is set.
67 * Returns NULL on failure.
69 ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
75 flags = ZipFile::kOpenReadWrite;
77 flags |= ZipFile::kOpenCreate;
80 result = zip->open(fileName, flags);
81 if (result != NO_ERROR) {
93 * Return a short string describing the compression method.
95 const char* compressionName(int method)
97 if (method == ZipEntry::kCompressStored)
99 else if (method == ZipEntry::kCompressDeflated)
106 * Return the percent reduction in size (0% == no compression).
108 int calcPercent(long uncompressedLen, long compressedLen)
110 if (!uncompressedLen)
113 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
117 * Handle the "list" command, which can be a simple file dump or
120 * The verbose listing closely matches the output of the Info-ZIP "unzip"
123 int doList(Bundle* bundle)
127 const ZipEntry* entry;
128 long totalUncLen, totalCompLen;
129 const char* zipFileName;
131 if (bundle->getFileSpecCount() != 1) {
132 fprintf(stderr, "ERROR: specify zip file name (only)\n");
135 zipFileName = bundle->getFileSpecEntry(0);
137 zip = openReadOnly(zipFileName);
143 if (bundle->getVerbose()) {
144 printf("Archive: %s\n", zipFileName);
146 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
148 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
151 totalUncLen = totalCompLen = 0;
153 count = zip->getNumEntries();
154 for (i = 0; i < count; i++) {
155 entry = zip->getEntryByIndex(i);
156 if (bundle->getVerbose()) {
160 when = entry->getModWhen();
161 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
164 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
165 (long) entry->getUncompressedLen(),
166 compressionName(entry->getCompressionMethod()),
167 (long) entry->getCompressedLen(),
168 calcPercent(entry->getUncompressedLen(),
169 entry->getCompressedLen()),
170 (size_t) entry->getLFHOffset(),
173 entry->getFileName());
175 printf("%s\n", entry->getFileName());
178 totalUncLen += entry->getUncompressedLen();
179 totalCompLen += entry->getCompressedLen();
182 if (bundle->getVerbose()) {
184 "-------- ------- --- -------\n");
185 printf("%8ld %7ld %2d%% %d files\n",
188 calcPercent(totalUncLen, totalCompLen),
189 zip->getNumEntries());
192 if (bundle->getAndroidList()) {
194 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
195 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
199 const ResTable& res = assets.getResources(false);
201 printf("\nNo resource table found.\n");
203 #ifndef HAVE_ANDROID_OS
204 printf("\nResource table:\n");
209 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
210 Asset::ACCESS_BUFFER);
211 if (manifestAsset == NULL) {
212 printf("\nNo AndroidManifest.xml found.\n");
214 printf("\nAndroid manifest:\n");
216 tree.setTo(manifestAsset->getBuffer(true),
217 manifestAsset->getLength());
218 printXMLBlock(&tree);
220 delete manifestAsset;
230 static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
232 size_t N = tree.getAttributeCount();
233 for (size_t i=0; i<N; i++) {
234 if (tree.getAttributeNameResID(i) == attrRes) {
241 String8 getAttribute(const ResXMLTree& tree, const char* ns,
242 const char* attr, String8* outError)
244 ssize_t idx = tree.indexOfAttribute(ns, attr);
249 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
250 if (value.dataType != Res_value::TYPE_STRING) {
251 if (outError != NULL) *outError = "attribute is not a string value";
256 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
257 return str ? String8(str, len) : String8();
260 static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
262 ssize_t idx = indexOfAttribute(tree, attrRes);
267 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
268 if (value.dataType != Res_value::TYPE_STRING) {
269 if (outError != NULL) *outError = "attribute is not a string value";
274 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
275 return str ? String8(str, len) : String8();
278 static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
279 String8* outError, int32_t defValue = -1)
281 ssize_t idx = indexOfAttribute(tree, attrRes);
286 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
287 if (value.dataType < Res_value::TYPE_FIRST_INT
288 || value.dataType > Res_value::TYPE_LAST_INT) {
289 if (outError != NULL) *outError = "attribute is not an integer value";
296 static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
297 uint32_t attrRes, String8* outError, int32_t defValue = -1)
299 ssize_t idx = indexOfAttribute(tree, attrRes);
304 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
305 if (value.dataType == Res_value::TYPE_REFERENCE) {
306 resTable->resolveReference(&value, 0);
308 if (value.dataType < Res_value::TYPE_FIRST_INT
309 || value.dataType > Res_value::TYPE_LAST_INT) {
310 if (outError != NULL) *outError = "attribute is not an integer value";
317 static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
318 uint32_t attrRes, String8* outError)
320 ssize_t idx = indexOfAttribute(tree, attrRes);
325 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
326 if (value.dataType == Res_value::TYPE_STRING) {
328 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
329 return str ? String8(str, len) : String8();
331 resTable->resolveReference(&value, 0);
332 if (value.dataType != Res_value::TYPE_STRING) {
333 if (outError != NULL) *outError = "attribute is not a string value";
338 const Res_value* value2 = &value;
339 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
340 return str ? String8(str, len) : String8();
343 // These are attribute resource constants for the platform, as found
346 LABEL_ATTR = 0x01010001,
347 ICON_ATTR = 0x01010002,
348 NAME_ATTR = 0x01010003,
349 PERMISSION_ATTR = 0x01010006,
350 RESOURCE_ATTR = 0x01010025,
351 DEBUGGABLE_ATTR = 0x0101000f,
352 VERSION_CODE_ATTR = 0x0101021b,
353 VERSION_NAME_ATTR = 0x0101021c,
354 SCREEN_ORIENTATION_ATTR = 0x0101001e,
355 MIN_SDK_VERSION_ATTR = 0x0101020c,
356 MAX_SDK_VERSION_ATTR = 0x01010271,
357 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
358 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
359 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
360 REQ_NAVIGATION_ATTR = 0x0101022a,
361 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
362 TARGET_SDK_VERSION_ATTR = 0x01010270,
363 TEST_ONLY_ATTR = 0x01010272,
364 ANY_DENSITY_ATTR = 0x0101026c,
365 GL_ES_VERSION_ATTR = 0x01010281,
366 SMALL_SCREEN_ATTR = 0x01010284,
367 NORMAL_SCREEN_ATTR = 0x01010285,
368 LARGE_SCREEN_ATTR = 0x01010286,
369 XLARGE_SCREEN_ATTR = 0x010102bf,
370 REQUIRED_ATTR = 0x0101028e,
371 SCREEN_SIZE_ATTR = 0x010102ca,
372 SCREEN_DENSITY_ATTR = 0x010102cb,
373 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
374 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
375 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
376 PUBLIC_KEY_ATTR = 0x010103a6,
377 CATEGORY_ATTR = 0x010103e8,
378 BANNER_ATTR = 0x10103f2,
381 const char *getComponentName(String8 &pkgName, String8 &componentName) {
382 ssize_t idx = componentName.find(".");
383 String8 retStr(pkgName);
385 retStr += componentName;
386 } else if (idx < 0) {
388 retStr += componentName;
390 return componentName.string();
392 return retStr.string();
395 static void printCompatibleScreens(ResXMLTree& tree) {
397 ResXMLTree::event_code_t code;
400 printf("compatible-screens:");
401 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
402 if (code == ResXMLTree::END_TAG) {
409 if (code != ResXMLTree::START_TAG) {
413 String8 tag(tree.getElementName(&len));
414 if (tag == "screen") {
415 int32_t screenSize = getIntegerAttribute(tree,
416 SCREEN_SIZE_ATTR, NULL, -1);
417 int32_t screenDensity = getIntegerAttribute(tree,
418 SCREEN_DENSITY_ATTR, NULL, -1);
419 if (screenSize > 0 && screenDensity > 0) {
424 printf("'%d/%d'", screenSize, screenDensity);
431 Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
432 String8 *outError = NULL)
434 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
435 if (aidAsset == NULL) {
436 if (outError != NULL) *outError = "xml resource does not exist";
437 return Vector<String8>();
440 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
442 bool withinApduService = false;
443 Vector<String8> categories;
447 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
451 ResXMLTree::event_code_t code;
452 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
453 if (code == ResXMLTree::END_TAG) {
455 String8 tag(tree.getElementName(&len));
457 if (depth == 0 && tag == serviceTagName) {
458 withinApduService = false;
461 } else if (code == ResXMLTree::START_TAG) {
463 String8 tag(tree.getElementName(&len));
466 if (tag == serviceTagName) {
467 withinApduService = true;
469 } else if (depth == 2 && withinApduService) {
470 if (tag == "aid-group") {
471 String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
473 if (outError != NULL) *outError = error;
474 return Vector<String8>();
477 categories.add(category);
487 * Handle the "dump" command, to extract select data from an archive.
489 extern char CONSOLE_DATA[2925]; // see EOF
490 int doDump(Bundle* bundle)
492 status_t result = UNKNOWN_ERROR;
495 if (bundle->getFileSpecCount() < 1) {
496 fprintf(stderr, "ERROR: no dump option specified\n");
500 if (bundle->getFileSpecCount() < 2) {
501 fprintf(stderr, "ERROR: no dump file specified\n");
505 const char* option = bundle->getFileSpecEntry(0);
506 const char* filename = bundle->getFileSpecEntry(1);
509 int32_t assetsCookie;
510 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
511 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
515 // Make a dummy config for retrieving resources... we need to supply
516 // non-default values for some configs so that we can retrieve resources
517 // in the app that don't have a default. The most important of these is
518 // the API version because key resources like icons will have an implicit
519 // version if they are using newer config types like density.
520 ResTable_config config;
521 config.language[0] = 'e';
522 config.language[1] = 'n';
523 config.country[0] = 'U';
524 config.country[1] = 'S';
525 config.orientation = ResTable_config::ORIENTATION_PORT;
526 config.density = ResTable_config::DENSITY_MEDIUM;
527 config.sdkVersion = 10000; // Very high.
528 config.screenWidthDp = 320;
529 config.screenHeightDp = 480;
530 config.smallestScreenWidthDp = 320;
531 assets.setConfiguration(config);
533 const ResTable& res = assets.getResources(false);
535 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
539 if (strcmp("resources", option) == 0) {
540 #ifndef HAVE_ANDROID_OS
541 res.print(bundle->getValues());
544 } else if (strcmp("strings", option) == 0) {
545 const ResStringPool* pool = res.getTableStringBlock(0);
546 printStringPool(pool);
548 } else if (strcmp("xmltree", option) == 0) {
549 if (bundle->getFileSpecCount() < 3) {
550 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
554 for (int i=2; i<bundle->getFileSpecCount(); i++) {
555 const char* resname = bundle->getFileSpecEntry(i);
557 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
559 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
563 if (tree.setTo(asset->getBuffer(true),
564 asset->getLength()) != NO_ERROR) {
565 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
569 printXMLBlock(&tree);
575 } else if (strcmp("xmlstrings", option) == 0) {
576 if (bundle->getFileSpecCount() < 3) {
577 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
581 for (int i=2; i<bundle->getFileSpecCount(); i++) {
582 const char* resname = bundle->getFileSpecEntry(i);
584 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
586 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
590 if (tree.setTo(asset->getBuffer(true),
591 asset->getLength()) != NO_ERROR) {
592 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
595 printStringPool(&tree.getStrings());
602 asset = assets.openNonAsset("AndroidManifest.xml",
603 Asset::ACCESS_BUFFER);
605 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
609 if (tree.setTo(asset->getBuffer(true),
610 asset->getLength()) != NO_ERROR) {
611 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
616 if (strcmp("permissions", option) == 0) {
618 ResXMLTree::event_code_t code;
620 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
621 if (code == ResXMLTree::END_TAG) {
625 if (code != ResXMLTree::START_TAG) {
629 String8 tag(tree.getElementName(&len));
630 //printf("Depth %d tag %s\n", depth, tag.string());
632 if (tag != "manifest") {
633 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
636 String8 pkg = getAttribute(tree, NULL, "package", NULL);
637 printf("package: %s\n", pkg.string());
638 } else if (depth == 2 && tag == "permission") {
640 String8 name = getAttribute(tree, NAME_ATTR, &error);
642 fprintf(stderr, "ERROR: %s\n", error.string());
645 printf("permission: %s\n", name.string());
646 } else if (depth == 2 && tag == "uses-permission") {
648 String8 name = getAttribute(tree, NAME_ATTR, &error);
650 fprintf(stderr, "ERROR: %s\n", error.string());
653 printf("uses-permission: %s\n", name.string());
654 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
656 printf("optional-permission: %s\n", name.string());
660 } else if (strcmp("badging", option) == 0) {
661 Vector<String8> locales;
662 res.getLocales(&locales);
664 Vector<ResTable_config> configs;
665 res.getConfigurations(&configs);
666 SortedVector<int> densities;
667 const size_t NC = configs.size();
668 for (size_t i=0; i<NC; i++) {
669 int dens = configs[i].density;
670 if (dens == 0) dens = 160;
675 ResXMLTree::event_code_t code;
678 bool withinActivity = false;
679 bool isMainActivity = false;
680 bool isLauncherActivity = false;
681 bool isLeanbackLauncherActivity = false;
682 bool isSearchable = false;
683 bool withinApplication = false;
684 bool withinSupportsInput = false;
685 bool withinReceiver = false;
686 bool withinService = false;
687 bool withinIntentFilter = false;
688 bool hasMainActivity = false;
689 bool hasOtherActivities = false;
690 bool hasOtherReceivers = false;
691 bool hasOtherServices = false;
692 bool hasWallpaperService = false;
693 bool hasImeService = false;
694 bool hasAccessibilityService = false;
695 bool hasPrintService = false;
696 bool hasWidgetReceivers = false;
697 bool hasDeviceAdminReceiver = false;
698 bool hasIntentFilter = false;
699 bool hasPaymentService = false;
700 bool actMainActivity = false;
701 bool actWidgetReceivers = false;
702 bool actDeviceAdminEnabled = false;
703 bool actImeService = false;
704 bool actWallpaperService = false;
705 bool actAccessibilityService = false;
706 bool actPrintService = false;
707 bool actHostApduService = false;
708 bool actOffHostApduService = false;
709 bool hasMetaHostPaymentCategory = false;
710 bool hasMetaOffHostPaymentCategory = false;
712 // These permissions are required by services implementing services
713 // the system binds to (IME, Accessibility, PrintServices, etc.)
714 bool hasBindDeviceAdminPermission = false;
715 bool hasBindInputMethodPermission = false;
716 bool hasBindAccessibilityServicePermission = false;
717 bool hasBindPrintServicePermission = false;
718 bool hasBindNfcServicePermission = false;
720 // These two implement the implicit permissions that are granted
721 // to pre-1.6 applications.
722 bool hasWriteExternalStoragePermission = false;
723 bool hasReadPhoneStatePermission = false;
725 // If an app requests write storage, they will also get read storage.
726 bool hasReadExternalStoragePermission = false;
728 // Implement transition to read and write call log.
729 bool hasReadContactsPermission = false;
730 bool hasWriteContactsPermission = false;
731 bool hasReadCallLogPermission = false;
732 bool hasWriteCallLogPermission = false;
734 // This next group of variables is used to implement a group of
735 // backward-compatibility heuristics necessitated by the addition of
736 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
737 // heuristic is "if an app requests a permission but doesn't explicitly
738 // request the corresponding <uses-feature>, presume it's there anyway".
739 bool specCameraFeature = false; // camera-related
740 bool specCameraAutofocusFeature = false;
741 bool reqCameraAutofocusFeature = false;
742 bool reqCameraFlashFeature = false;
743 bool hasCameraPermission = false;
744 bool specLocationFeature = false; // location-related
745 bool specNetworkLocFeature = false;
746 bool reqNetworkLocFeature = false;
747 bool specGpsFeature = false;
748 bool reqGpsFeature = false;
749 bool hasMockLocPermission = false;
750 bool hasCoarseLocPermission = false;
751 bool hasGpsPermission = false;
752 bool hasGeneralLocPermission = false;
753 bool specBluetoothFeature = false; // Bluetooth API-related
754 bool hasBluetoothPermission = false;
755 bool specMicrophoneFeature = false; // microphone-related
756 bool hasRecordAudioPermission = false;
757 bool specWiFiFeature = false;
758 bool hasWiFiPermission = false;
759 bool specTelephonyFeature = false; // telephony-related
760 bool reqTelephonySubFeature = false;
761 bool hasTelephonyPermission = false;
762 bool specTouchscreenFeature = false; // touchscreen-related
763 bool specMultitouchFeature = false;
764 bool reqDistinctMultitouchFeature = false;
765 bool specScreenPortraitFeature = false;
766 bool specScreenLandscapeFeature = false;
767 bool reqScreenPortraitFeature = false;
768 bool reqScreenLandscapeFeature = false;
769 // 2.2 also added some other features that apps can request, but that
770 // have no corresponding permission, so we cannot implement any
771 // back-compatibility heuristic for them. The below are thus unnecessary
772 // (but are retained here for documentary purposes.)
773 //bool specCompassFeature = false;
774 //bool specAccelerometerFeature = false;
775 //bool specProximityFeature = false;
776 //bool specAmbientLightFeature = false;
777 //bool specLiveWallpaperFeature = false;
781 int normalScreen = 1;
783 int xlargeScreen = 1;
785 int requiresSmallestWidthDp = 0;
786 int compatibleWidthLimitDp = 0;
787 int largestWidthLimitDp = 0;
789 String8 activityName;
790 String8 activityLabel;
791 String8 activityIcon;
792 String8 activityBanner;
793 String8 receiverName;
795 Vector<String8> supportedInput;
796 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
797 if (code == ResXMLTree::END_TAG) {
800 if (withinSupportsInput && !supportedInput.isEmpty()) {
801 printf("supports-input: '");
802 const size_t N = supportedInput.size();
803 for (size_t i=0; i<N; i++) {
804 printf("%s", supportedInput[i].string());
811 supportedInput.clear();
813 withinApplication = false;
814 withinSupportsInput = false;
815 } else if (depth < 3) {
816 if (withinActivity && isMainActivity) {
817 const char *aName = getComponentName(pkg, activityName);
818 if (isLauncherActivity) {
819 printf("launchable-activity:");
821 printf(" name='%s' ", aName);
823 printf(" label='%s' icon='%s'\n",
824 activityLabel.string(),
825 activityIcon.string());
827 if (isLeanbackLauncherActivity) {
828 printf("leanback-launchable-activity:");
830 printf(" name='%s' ", aName);
832 printf(" label='%s' icon='%s' banner='%s'\n",
833 activityLabel.string(),
834 activityIcon.string(),
835 activityBanner.string());
838 if (!hasIntentFilter) {
839 hasOtherActivities |= withinActivity;
840 hasOtherReceivers |= withinReceiver;
841 hasOtherServices |= withinService;
844 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
845 hasBindNfcServicePermission);
846 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
847 hasBindNfcServicePermission);
850 withinActivity = false;
851 withinService = false;
852 withinReceiver = false;
853 hasIntentFilter = false;
854 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
855 } else if (depth < 4) {
856 if (withinIntentFilter) {
857 if (withinActivity) {
858 hasMainActivity |= actMainActivity;
859 hasOtherActivities |= !actMainActivity;
860 } else if (withinReceiver) {
861 hasWidgetReceivers |= actWidgetReceivers;
862 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
863 hasBindDeviceAdminPermission);
864 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
865 } else if (withinService) {
866 hasImeService |= actImeService;
867 hasWallpaperService |= actWallpaperService;
868 hasAccessibilityService |= (actAccessibilityService &&
869 hasBindAccessibilityServicePermission);
870 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
871 hasOtherServices |= (!actImeService && !actWallpaperService &&
872 !actAccessibilityService && !actPrintService &&
873 !actHostApduService && !actOffHostApduService);
876 withinIntentFilter = false;
880 if (code != ResXMLTree::START_TAG) {
884 String8 tag(tree.getElementName(&len));
885 //printf("Depth %d, %s\n", depth, tag.string());
887 if (tag != "manifest") {
888 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
891 pkg = getAttribute(tree, NULL, "package", NULL);
892 printf("package: name='%s' ", pkg.string());
893 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
895 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
898 if (versionCode > 0) {
899 printf("versionCode='%d' ", versionCode);
901 printf("versionCode='' ");
903 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
905 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
908 printf("versionName='%s'\n", versionName.string());
909 } else if (depth == 2) {
910 withinApplication = false;
911 if (tag == "application") {
912 withinApplication = true;
915 const size_t NL = locales.size();
916 for (size_t i=0; i<NL; i++) {
917 const char* localeStr = locales[i].string();
918 assets.setLocale(localeStr != NULL ? localeStr : "");
919 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
921 if (localeStr == NULL || strlen(localeStr) == 0) {
923 printf("application-label:'%s'\n", llabel.string());
928 printf("application-label-%s:'%s'\n", localeStr,
934 ResTable_config tmpConfig = config;
935 const size_t ND = densities.size();
936 for (size_t i=0; i<ND; i++) {
937 tmpConfig.density = densities[i];
938 assets.setConfiguration(tmpConfig);
939 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
941 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
944 assets.setConfiguration(config);
946 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
948 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
951 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
953 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
956 printf("application: label='%s' ", label.string());
957 printf("icon='%s'\n", icon.string());
959 printf("testOnly='%d'\n", testOnly);
962 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
964 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
967 if (debuggable != 0) {
968 printf("application-debuggable\n");
970 } else if (tag == "uses-sdk") {
971 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
974 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
976 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
980 if (name == "Donut") targetSdk = 4;
981 printf("sdkVersion:'%s'\n", name.string());
982 } else if (code != -1) {
984 printf("sdkVersion:'%d'\n", code);
986 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
988 printf("maxSdkVersion:'%d'\n", code);
990 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
993 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
995 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
999 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
1000 printf("targetSdkVersion:'%s'\n", name.string());
1001 } else if (code != -1) {
1002 if (targetSdk < code) {
1005 printf("targetSdkVersion:'%d'\n", code);
1007 } else if (tag == "uses-configuration") {
1008 int32_t reqTouchScreen = getIntegerAttribute(tree,
1009 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
1010 int32_t reqKeyboardType = getIntegerAttribute(tree,
1011 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
1012 int32_t reqHardKeyboard = getIntegerAttribute(tree,
1013 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
1014 int32_t reqNavigation = getIntegerAttribute(tree,
1015 REQ_NAVIGATION_ATTR, NULL, 0);
1016 int32_t reqFiveWayNav = getIntegerAttribute(tree,
1017 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
1018 printf("uses-configuration:");
1019 if (reqTouchScreen != 0) {
1020 printf(" reqTouchScreen='%d'", reqTouchScreen);
1022 if (reqKeyboardType != 0) {
1023 printf(" reqKeyboardType='%d'", reqKeyboardType);
1025 if (reqHardKeyboard != 0) {
1026 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1028 if (reqNavigation != 0) {
1029 printf(" reqNavigation='%d'", reqNavigation);
1031 if (reqFiveWayNav != 0) {
1032 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1035 } else if (tag == "supports-input") {
1036 withinSupportsInput = true;
1037 } else if (tag == "supports-screens") {
1038 smallScreen = getIntegerAttribute(tree,
1039 SMALL_SCREEN_ATTR, NULL, 1);
1040 normalScreen = getIntegerAttribute(tree,
1041 NORMAL_SCREEN_ATTR, NULL, 1);
1042 largeScreen = getIntegerAttribute(tree,
1043 LARGE_SCREEN_ATTR, NULL, 1);
1044 xlargeScreen = getIntegerAttribute(tree,
1045 XLARGE_SCREEN_ATTR, NULL, 1);
1046 anyDensity = getIntegerAttribute(tree,
1047 ANY_DENSITY_ATTR, NULL, 1);
1048 requiresSmallestWidthDp = getIntegerAttribute(tree,
1049 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1050 compatibleWidthLimitDp = getIntegerAttribute(tree,
1051 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1052 largestWidthLimitDp = getIntegerAttribute(tree,
1053 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1054 } else if (tag == "uses-feature") {
1055 String8 name = getAttribute(tree, NAME_ATTR, &error);
1057 if (name != "" && error == "") {
1058 int req = getIntegerAttribute(tree,
1059 REQUIRED_ATTR, NULL, 1);
1061 if (name == "android.hardware.camera") {
1062 specCameraFeature = true;
1063 } else if (name == "android.hardware.camera.autofocus") {
1064 // these have no corresponding permission to check for,
1065 // but should imply the foundational camera permission
1066 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
1067 specCameraAutofocusFeature = true;
1068 } else if (req && (name == "android.hardware.camera.flash")) {
1069 // these have no corresponding permission to check for,
1070 // but should imply the foundational camera permission
1071 reqCameraFlashFeature = true;
1072 } else if (name == "android.hardware.location") {
1073 specLocationFeature = true;
1074 } else if (name == "android.hardware.location.network") {
1075 specNetworkLocFeature = true;
1076 reqNetworkLocFeature = reqNetworkLocFeature || req;
1077 } else if (name == "android.hardware.location.gps") {
1078 specGpsFeature = true;
1079 reqGpsFeature = reqGpsFeature || req;
1080 } else if (name == "android.hardware.bluetooth") {
1081 specBluetoothFeature = true;
1082 } else if (name == "android.hardware.touchscreen") {
1083 specTouchscreenFeature = true;
1084 } else if (name == "android.hardware.touchscreen.multitouch") {
1085 specMultitouchFeature = true;
1086 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
1087 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
1088 } else if (name == "android.hardware.microphone") {
1089 specMicrophoneFeature = true;
1090 } else if (name == "android.hardware.wifi") {
1091 specWiFiFeature = true;
1092 } else if (name == "android.hardware.telephony") {
1093 specTelephonyFeature = true;
1094 } else if (req && (name == "android.hardware.telephony.gsm" ||
1095 name == "android.hardware.telephony.cdma")) {
1096 // these have no corresponding permission to check for,
1097 // but should imply the foundational telephony permission
1098 reqTelephonySubFeature = true;
1099 } else if (name == "android.hardware.screen.portrait") {
1100 specScreenPortraitFeature = true;
1101 } else if (name == "android.hardware.screen.landscape") {
1102 specScreenLandscapeFeature = true;
1104 printf("uses-feature%s:'%s'\n",
1105 req ? "" : "-not-required", name.string());
1107 int vers = getIntegerAttribute(tree,
1108 GL_ES_VERSION_ATTR, &error);
1110 printf("uses-gl-es:'0x%x'\n", vers);
1113 } else if (tag == "uses-permission") {
1114 String8 name = getAttribute(tree, NAME_ATTR, &error);
1115 if (name != "" && error == "") {
1116 if (name == "android.permission.CAMERA") {
1117 hasCameraPermission = true;
1118 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1119 hasGpsPermission = true;
1120 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1121 hasMockLocPermission = true;
1122 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1123 hasCoarseLocPermission = true;
1124 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1125 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1126 hasGeneralLocPermission = true;
1127 } else if (name == "android.permission.BLUETOOTH" ||
1128 name == "android.permission.BLUETOOTH_ADMIN") {
1129 hasBluetoothPermission = true;
1130 } else if (name == "android.permission.RECORD_AUDIO") {
1131 hasRecordAudioPermission = true;
1132 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1133 name == "android.permission.CHANGE_WIFI_STATE" ||
1134 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1135 hasWiFiPermission = true;
1136 } else if (name == "android.permission.CALL_PHONE" ||
1137 name == "android.permission.CALL_PRIVILEGED" ||
1138 name == "android.permission.MODIFY_PHONE_STATE" ||
1139 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1140 name == "android.permission.READ_SMS" ||
1141 name == "android.permission.RECEIVE_SMS" ||
1142 name == "android.permission.RECEIVE_MMS" ||
1143 name == "android.permission.RECEIVE_WAP_PUSH" ||
1144 name == "android.permission.SEND_SMS" ||
1145 name == "android.permission.WRITE_APN_SETTINGS" ||
1146 name == "android.permission.WRITE_SMS") {
1147 hasTelephonyPermission = true;
1148 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1149 hasWriteExternalStoragePermission = true;
1150 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1151 hasReadExternalStoragePermission = true;
1152 } else if (name == "android.permission.READ_PHONE_STATE") {
1153 hasReadPhoneStatePermission = true;
1154 } else if (name == "android.permission.READ_CONTACTS") {
1155 hasReadContactsPermission = true;
1156 } else if (name == "android.permission.WRITE_CONTACTS") {
1157 hasWriteContactsPermission = true;
1158 } else if (name == "android.permission.READ_CALL_LOG") {
1159 hasReadCallLogPermission = true;
1160 } else if (name == "android.permission.WRITE_CALL_LOG") {
1161 hasWriteCallLogPermission = true;
1163 printf("uses-permission:'%s'\n", name.string());
1164 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
1166 printf("optional-permission:'%s'\n", name.string());
1169 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1173 } else if (tag == "uses-package") {
1174 String8 name = getAttribute(tree, NAME_ATTR, &error);
1175 if (name != "" && error == "") {
1176 printf("uses-package:'%s'\n", name.string());
1178 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1182 } else if (tag == "original-package") {
1183 String8 name = getAttribute(tree, NAME_ATTR, &error);
1184 if (name != "" && error == "") {
1185 printf("original-package:'%s'\n", name.string());
1187 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1191 } else if (tag == "supports-gl-texture") {
1192 String8 name = getAttribute(tree, NAME_ATTR, &error);
1193 if (name != "" && error == "") {
1194 printf("supports-gl-texture:'%s'\n", name.string());
1196 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1200 } else if (tag == "compatible-screens") {
1201 printCompatibleScreens(tree);
1203 } else if (tag == "package-verifier") {
1204 String8 name = getAttribute(tree, NAME_ATTR, &error);
1205 if (name != "" && error == "") {
1206 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1207 if (publicKey != "" && error == "") {
1208 printf("package-verifier: name='%s' publicKey='%s'\n",
1209 name.string(), publicKey.string());
1213 } else if (depth == 3) {
1214 withinActivity = false;
1215 withinReceiver = false;
1216 withinService = false;
1217 hasIntentFilter = false;
1218 hasMetaHostPaymentCategory = false;
1219 hasMetaOffHostPaymentCategory = false;
1220 hasBindDeviceAdminPermission = false;
1221 hasBindInputMethodPermission = false;
1222 hasBindAccessibilityServicePermission = false;
1223 hasBindPrintServicePermission = false;
1224 hasBindNfcServicePermission = false;
1225 if (withinApplication) {
1226 if(tag == "activity") {
1227 withinActivity = true;
1228 activityName = getAttribute(tree, NAME_ATTR, &error);
1230 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1235 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1237 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1242 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1244 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1249 activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error);
1251 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1256 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1257 SCREEN_ORIENTATION_ATTR, &error);
1259 if (orien == 0 || orien == 6 || orien == 8) {
1260 // Requests landscape, sensorLandscape, or reverseLandscape.
1261 reqScreenLandscapeFeature = true;
1262 } else if (orien == 1 || orien == 7 || orien == 9) {
1263 // Requests portrait, sensorPortrait, or reversePortrait.
1264 reqScreenPortraitFeature = true;
1267 } else if (tag == "uses-library") {
1268 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1271 "ERROR getting 'android:name' attribute for uses-library"
1272 " %s\n", error.string());
1275 int req = getIntegerAttribute(tree,
1276 REQUIRED_ATTR, NULL, 1);
1277 printf("uses-library%s:'%s'\n",
1278 req ? "" : "-not-required", libraryName.string());
1279 } else if (tag == "receiver") {
1280 withinReceiver = true;
1281 receiverName = getAttribute(tree, NAME_ATTR, &error);
1285 "ERROR getting 'android:name' attribute for receiver:"
1286 " %s\n", error.string());
1290 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1292 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1293 hasBindDeviceAdminPermission = true;
1296 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1297 " receiver '%s': %s\n", receiverName.string(), error.string());
1299 } else if (tag == "service") {
1300 withinService = true;
1301 serviceName = getAttribute(tree, NAME_ATTR, &error);
1304 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1305 " service: %s\n", error.string());
1309 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1311 if (permission == "android.permission.BIND_INPUT_METHOD") {
1312 hasBindInputMethodPermission = true;
1313 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1314 hasBindAccessibilityServicePermission = true;
1315 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1316 hasBindPrintServicePermission = true;
1317 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1318 hasBindNfcServicePermission = true;
1321 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1322 " service '%s': %s\n", serviceName.string(), error.string());
1325 } else if (withinSupportsInput && tag == "input-type") {
1326 String8 name = getAttribute(tree, NAME_ATTR, &error);
1327 if (name != "" && error == "") {
1328 supportedInput.add(name);
1330 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1335 } else if (depth == 4) {
1336 if (tag == "intent-filter") {
1337 hasIntentFilter = true;
1338 withinIntentFilter = true;
1339 actMainActivity = false;
1340 actWidgetReceivers = false;
1341 actImeService = false;
1342 actWallpaperService = false;
1343 actAccessibilityService = false;
1344 actPrintService = false;
1345 actDeviceAdminEnabled = false;
1346 actHostApduService = false;
1347 actOffHostApduService = false;
1348 } else if (withinService && tag == "meta-data") {
1349 String8 name = getAttribute(tree, NAME_ATTR, &error);
1351 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1352 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1356 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1357 name == "android.nfc.cardemulation.off_host_apdu_service") {
1358 bool offHost = true;
1359 if (name == "android.nfc.cardemulation.host_apdu_service") {
1363 String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
1365 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1366 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1370 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1373 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1374 serviceName.string());
1378 const size_t catLen = categories.size();
1379 for (size_t i = 0; i < catLen; i++) {
1380 bool paymentCategory = (categories[i] == "payment");
1382 hasMetaOffHostPaymentCategory |= paymentCategory;
1384 hasMetaHostPaymentCategory |= paymentCategory;
1389 } else if ((depth == 5) && withinIntentFilter){
1391 if (tag == "action") {
1392 action = getAttribute(tree, NAME_ATTR, &error);
1394 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1397 if (withinActivity) {
1398 if (action == "android.intent.action.MAIN") {
1399 isMainActivity = true;
1400 actMainActivity = true;
1402 } else if (withinReceiver) {
1403 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1404 actWidgetReceivers = true;
1405 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1406 actDeviceAdminEnabled = true;
1408 } else if (withinService) {
1409 if (action == "android.view.InputMethod") {
1410 actImeService = true;
1411 } else if (action == "android.service.wallpaper.WallpaperService") {
1412 actWallpaperService = true;
1413 } else if (action == "android.accessibilityservice.AccessibilityService") {
1414 actAccessibilityService = true;
1415 } else if (action == "android.printservice.PrintService") {
1416 actPrintService = true;
1417 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1418 actHostApduService = true;
1419 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1420 actOffHostApduService = true;
1423 if (action == "android.intent.action.SEARCH") {
1424 isSearchable = true;
1428 if (tag == "category") {
1429 String8 category = getAttribute(tree, NAME_ATTR, &error);
1431 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1434 if (withinActivity) {
1435 if (category == "android.intent.category.LAUNCHER") {
1436 isLauncherActivity = true;
1437 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1438 isLeanbackLauncherActivity = true;
1445 // Pre-1.6 implicitly granted permission compatibility logic
1446 if (targetSdk < 4) {
1447 if (!hasWriteExternalStoragePermission) {
1448 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
1449 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1450 "'targetSdkVersion < 4'\n");
1451 hasWriteExternalStoragePermission = true;
1453 if (!hasReadPhoneStatePermission) {
1454 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
1455 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1456 "'targetSdkVersion < 4'\n");
1460 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1461 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1462 // do this (regardless of target API version) because we can't have
1463 // an app with write permission but not read permission.
1464 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1465 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
1466 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1467 "'requested WRITE_EXTERNAL_STORAGE'\n");
1470 // Pre-JellyBean call log permission compatibility.
1471 if (targetSdk < 16) {
1472 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1473 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
1474 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1475 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
1477 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1478 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
1479 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1480 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
1484 /* The following blocks handle printing "inferred" uses-features, based
1485 * on whether related features or permissions are used by the app.
1486 * Note that the various spec*Feature variables denote whether the
1487 * relevant tag was *present* in the AndroidManfest, not that it was
1488 * present and set to true.
1490 // Camera-related back-compatibility logic
1491 if (!specCameraFeature) {
1492 if (reqCameraFlashFeature) {
1493 // if app requested a sub-feature (autofocus or flash) and didn't
1494 // request the base camera feature, we infer that it meant to
1495 printf("uses-feature:'android.hardware.camera'\n");
1496 printf("uses-implied-feature:'android.hardware.camera'," \
1497 "'requested android.hardware.camera.flash feature'\n");
1498 } else if (reqCameraAutofocusFeature) {
1499 // if app requested a sub-feature (autofocus or flash) and didn't
1500 // request the base camera feature, we infer that it meant to
1501 printf("uses-feature:'android.hardware.camera'\n");
1502 printf("uses-implied-feature:'android.hardware.camera'," \
1503 "'requested android.hardware.camera.autofocus feature'\n");
1504 } else if (hasCameraPermission) {
1505 // if app wants to use camera but didn't request the feature, we infer
1506 // that it meant to, and further that it wants autofocus
1507 // (which was the 1.0 - 1.5 behavior)
1508 printf("uses-feature:'android.hardware.camera'\n");
1509 if (!specCameraAutofocusFeature) {
1510 printf("uses-feature:'android.hardware.camera.autofocus'\n");
1511 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1512 "'requested android.permission.CAMERA permission'\n");
1517 // Location-related back-compatibility logic
1518 if (!specLocationFeature &&
1519 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1520 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1521 // if app either takes a location-related permission or requests one of the
1522 // sub-features, we infer that it also meant to request the base location feature
1523 printf("uses-feature:'android.hardware.location'\n");
1524 printf("uses-implied-feature:'android.hardware.location'," \
1525 "'requested a location access permission'\n");
1527 if (!specGpsFeature && hasGpsPermission) {
1528 // if app takes GPS (FINE location) perm but does not request the GPS
1529 // feature, we infer that it meant to
1530 printf("uses-feature:'android.hardware.location.gps'\n");
1531 printf("uses-implied-feature:'android.hardware.location.gps'," \
1532 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
1534 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1535 // if app takes Network location (COARSE location) perm but does not request the
1536 // network location feature, we infer that it meant to
1537 printf("uses-feature:'android.hardware.location.network'\n");
1538 printf("uses-implied-feature:'android.hardware.location.network'," \
1539 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
1542 // Bluetooth-related compatibility logic
1543 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
1544 // if app takes a Bluetooth permission but does not request the Bluetooth
1545 // feature, we infer that it meant to
1546 printf("uses-feature:'android.hardware.bluetooth'\n");
1547 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1548 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1549 "permission and targetSdkVersion > 4'\n");
1552 // Microphone-related compatibility logic
1553 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1554 // if app takes the record-audio permission but does not request the microphone
1555 // feature, we infer that it meant to
1556 printf("uses-feature:'android.hardware.microphone'\n");
1557 printf("uses-implied-feature:'android.hardware.microphone'," \
1558 "'requested android.permission.RECORD_AUDIO permission'\n");
1561 // WiFi-related compatibility logic
1562 if (!specWiFiFeature && hasWiFiPermission) {
1563 // if app takes one of the WiFi permissions but does not request the WiFi
1564 // feature, we infer that it meant to
1565 printf("uses-feature:'android.hardware.wifi'\n");
1566 printf("uses-implied-feature:'android.hardware.wifi'," \
1567 "'requested android.permission.ACCESS_WIFI_STATE, " \
1568 "android.permission.CHANGE_WIFI_STATE, or " \
1569 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
1572 // Telephony-related compatibility logic
1573 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1574 // if app takes one of the telephony permissions or requests a sub-feature but
1575 // does not request the base telephony feature, we infer that it meant to
1576 printf("uses-feature:'android.hardware.telephony'\n");
1577 printf("uses-implied-feature:'android.hardware.telephony'," \
1578 "'requested a telephony-related permission or feature'\n");
1581 // Touchscreen-related back-compatibility logic
1582 if (!specTouchscreenFeature) { // not a typo!
1583 // all apps are presumed to require a touchscreen, unless they explicitly say
1584 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1585 // Note that specTouchscreenFeature is true if the tag is present, regardless
1586 // of whether its value is true or false, so this is safe
1587 printf("uses-feature:'android.hardware.touchscreen'\n");
1588 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1589 "'assumed you require a touch screen unless explicitly made optional'\n");
1591 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1592 // if app takes one of the telephony permissions or requests a sub-feature but
1593 // does not request the base telephony feature, we infer that it meant to
1594 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1595 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1596 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
1599 // Landscape/portrait-related compatibility logic
1600 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1601 // If the app has specified any activities in its manifest
1602 // that request a specific orientation, then assume that
1603 // orientation is required.
1604 if (reqScreenLandscapeFeature) {
1605 printf("uses-feature:'android.hardware.screen.landscape'\n");
1606 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1607 "'one or more activities have specified a landscape orientation'\n");
1609 if (reqScreenPortraitFeature) {
1610 printf("uses-feature:'android.hardware.screen.portrait'\n");
1611 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1612 "'one or more activities have specified a portrait orientation'\n");
1616 if (hasMainActivity) {
1619 if (hasWidgetReceivers) {
1620 printf("app-widget\n");
1622 if (hasDeviceAdminReceiver) {
1623 printf("device-admin\n");
1625 if (hasImeService) {
1628 if (hasWallpaperService) {
1629 printf("wallpaper\n");
1631 if (hasAccessibilityService) {
1632 printf("accessibility\n");
1634 if (hasPrintService) {
1637 if (hasPaymentService) {
1638 printf("payment\n");
1640 if (hasOtherActivities) {
1641 printf("other-activities\n");
1646 if (hasOtherReceivers) {
1647 printf("other-receivers\n");
1649 if (hasOtherServices) {
1650 printf("other-services\n");
1653 // For modern apps, if screen size buckets haven't been specified
1654 // but the new width ranges have, then infer the buckets from them.
1655 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1656 && requiresSmallestWidthDp > 0) {
1657 int compatWidth = compatibleWidthLimitDp;
1658 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1659 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1664 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1669 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1674 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1681 // Determine default values for any unspecified screen sizes,
1682 // based on the target SDK of the package. As of 4 (donut)
1683 // the screen size support was introduced, so all default to
1685 if (smallScreen > 0) {
1686 smallScreen = targetSdk >= 4 ? -1 : 0;
1688 if (normalScreen > 0) {
1691 if (largeScreen > 0) {
1692 largeScreen = targetSdk >= 4 ? -1 : 0;
1694 if (xlargeScreen > 0) {
1695 // Introduced in Gingerbread.
1696 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1698 if (anyDensity > 0) {
1699 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1700 || compatibleWidthLimitDp > 0) ? -1 : 0;
1702 printf("supports-screens:");
1703 if (smallScreen != 0) printf(" 'small'");
1704 if (normalScreen != 0) printf(" 'normal'");
1705 if (largeScreen != 0) printf(" 'large'");
1706 if (xlargeScreen != 0) printf(" 'xlarge'");
1708 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1709 if (requiresSmallestWidthDp > 0) {
1710 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1712 if (compatibleWidthLimitDp > 0) {
1713 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1715 if (largestWidthLimitDp > 0) {
1716 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1720 const size_t NL = locales.size();
1721 for (size_t i=0; i<NL; i++) {
1722 const char* localeStr = locales[i].string();
1723 if (localeStr == NULL || strlen(localeStr) == 0) {
1724 localeStr = "--_--";
1726 printf(" '%s'", localeStr);
1730 printf("densities:");
1731 const size_t ND = densities.size();
1732 for (size_t i=0; i<ND; i++) {
1733 printf(" '%d'", densities[i]);
1737 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1739 if (dir->getFileCount() > 0) {
1740 printf("native-code:");
1741 for (size_t i=0; i<dir->getFileCount(); i++) {
1742 printf(" '%s'", dir->getFileName(i).string());
1748 } else if (strcmp("badger", option) == 0) {
1749 printf("%s", CONSOLE_DATA);
1750 } else if (strcmp("configurations", option) == 0) {
1751 Vector<ResTable_config> configs;
1752 res.getConfigurations(&configs);
1753 const size_t N = configs.size();
1754 for (size_t i=0; i<N; i++) {
1755 printf("%s\n", configs[i].toString().string());
1758 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1769 return (result != NO_ERROR);
1774 * Handle the "add" command, which wants to add files to a new or
1775 * pre-existing archive.
1777 int doAdd(Bundle* bundle)
1779 ZipFile* zip = NULL;
1780 status_t result = UNKNOWN_ERROR;
1781 const char* zipFileName;
1783 if (bundle->getUpdate()) {
1784 /* avoid confusion */
1785 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1789 if (bundle->getFileSpecCount() < 1) {
1790 fprintf(stderr, "ERROR: must specify zip file name\n");
1793 zipFileName = bundle->getFileSpecEntry(0);
1795 if (bundle->getFileSpecCount() < 2) {
1796 fprintf(stderr, "NOTE: nothing to do\n");
1800 zip = openReadWrite(zipFileName, true);
1802 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1806 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1807 const char* fileName = bundle->getFileSpecEntry(i);
1809 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1810 printf(" '%s'... (from gzip)\n", fileName);
1811 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1813 if (bundle->getJunkPath()) {
1814 String8 storageName = String8(fileName).getPathLeaf();
1815 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1816 result = zip->add(fileName, storageName.string(),
1817 bundle->getCompressionMethod(), NULL);
1819 printf(" '%s'...\n", fileName);
1820 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1823 if (result != NO_ERROR) {
1824 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1825 if (result == NAME_NOT_FOUND)
1826 fprintf(stderr, ": file not found\n");
1827 else if (result == ALREADY_EXISTS)
1828 fprintf(stderr, ": already exists in archive\n");
1830 fprintf(stderr, "\n");
1839 return (result != NO_ERROR);
1844 * Delete files from an existing archive.
1846 int doRemove(Bundle* bundle)
1848 ZipFile* zip = NULL;
1849 status_t result = UNKNOWN_ERROR;
1850 const char* zipFileName;
1852 if (bundle->getFileSpecCount() < 1) {
1853 fprintf(stderr, "ERROR: must specify zip file name\n");
1856 zipFileName = bundle->getFileSpecEntry(0);
1858 if (bundle->getFileSpecCount() < 2) {
1859 fprintf(stderr, "NOTE: nothing to do\n");
1863 zip = openReadWrite(zipFileName, false);
1865 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1870 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1871 const char* fileName = bundle->getFileSpecEntry(i);
1874 entry = zip->getEntryByName(fileName);
1875 if (entry == NULL) {
1876 printf(" '%s' NOT FOUND\n", fileName);
1880 result = zip->remove(entry);
1882 if (result != NO_ERROR) {
1883 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1884 bundle->getFileSpecEntry(i), zipFileName);
1889 /* update the archive */
1894 return (result != NO_ERROR);
1899 * Package up an asset directory and associated application files.
1901 int doPackage(Bundle* bundle)
1903 const char* outputAPKFile;
1906 sp<AaptAssets> assets;
1909 String8 dependencyFile;
1911 // -c zz_ZZ means do pseudolocalization
1912 ResourceFilter filter;
1913 err = filter.parse(bundle->getConfigurations());
1914 if (err != NO_ERROR) {
1917 if (filter.containsPseudo()) {
1918 bundle->setPseudolocalize(true);
1921 N = bundle->getFileSpecCount();
1922 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1923 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
1924 fprintf(stderr, "ERROR: no input files\n");
1928 outputAPKFile = bundle->getOutputAPKFile();
1930 // Make sure the filenames provided exist and are of the appropriate type.
1931 if (outputAPKFile) {
1933 type = getFileType(outputAPKFile);
1934 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1936 "ERROR: output file '%s' exists but is not regular file\n",
1943 assets = new AaptAssets();
1945 // Set up the resource gathering in assets if we're going to generate
1946 // dependency files. Every time we encounter a resource while slurping
1947 // the tree, we'll add it to these stores so we have full resource paths
1948 // to write to a dependency file.
1949 if (bundle->getGenDependencies()) {
1950 sp<FilePathStore> resPathStore = new FilePathStore;
1951 assets->setFullResPaths(resPathStore);
1952 sp<FilePathStore> assetPathStore = new FilePathStore;
1953 assets->setFullAssetPaths(assetPathStore);
1956 err = assets->slurpFromArgs(bundle);
1961 if (bundle->getVerbose()) {
1962 assets->print(String8());
1965 // If they asked for any fileAs that need to be compiled, do so.
1966 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1967 err = buildResources(bundle, assets);
1973 // At this point we've read everything and processed everything. From here
1974 // on out it's just writing output files.
1975 if (SourcePos::hasErrors()) {
1979 // Update symbols with information about which ones are needed as Java symbols.
1980 assets->applyJavaSymbols();
1981 if (SourcePos::hasErrors()) {
1985 // If we've been asked to generate a dependency file, do that here
1986 if (bundle->getGenDependencies()) {
1987 // If this is the packaging step, generate the dependency file next to
1988 // the output apk (e.g. bin/resources.ap_.d)
1989 if (outputAPKFile) {
1990 dependencyFile = String8(outputAPKFile);
1991 // Add the .d extension to the dependency file.
1992 dependencyFile.append(".d");
1994 // Else if this is the R.java dependency generation step,
1995 // generate the dependency file in the R.java package subdirectory
1996 // e.g. gen/com/foo/app/R.java.d
1997 dependencyFile = String8(bundle->getRClassDir());
1998 dependencyFile.appendPath("R.java.d");
2000 // Make sure we have a clean dependency file to start with
2001 fp = fopen(dependencyFile, "w");
2005 // Write out R.java constants
2006 if (!assets->havePrivateSymbols()) {
2007 if (bundle->getCustomPackage() == NULL) {
2008 // Write the R.java file into the appropriate class directory
2009 // e.g. gen/com/foo/app/R.java
2010 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
2012 const String8 customPkg(bundle->getCustomPackage());
2013 err = writeResourceSymbols(bundle, assets, customPkg, true);
2018 // If we have library files, we're going to write our R.java file into
2019 // the appropriate class directory for those libraries as well.
2020 // e.g. gen/com/foo/app/lib/R.java
2021 if (bundle->getExtraPackages() != NULL) {
2023 String8 libs(bundle->getExtraPackages());
2024 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2025 while (packageString != NULL) {
2026 // Write the R.java file out with the correct package name
2027 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
2031 packageString = strtok(NULL, ":");
2033 libs.unlockBuffer();
2036 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
2040 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
2046 // Write out the ProGuard file
2047 err = writeProguardFile(bundle, assets);
2053 if (outputAPKFile) {
2054 err = writeAPK(bundle, assets, String8(outputAPKFile));
2055 if (err != NO_ERROR) {
2056 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
2061 // If we've been asked to generate a dependency file, we need to finish up here.
2062 // the writeResourceSymbols and writeAPK functions have already written the target
2063 // half of the dependency file, now we need to write the prerequisites. (files that
2064 // the R.java file or .ap_ file depend on)
2065 if (bundle->getGenDependencies()) {
2066 // Now that writeResourceSymbols or writeAPK has taken care of writing
2067 // the targets to our dependency file, we'll write the prereqs
2068 fp = fopen(dependencyFile, "a+");
2070 bool includeRaw = (outputAPKFile != NULL);
2071 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2072 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2073 // and therefore was not added to our pathstores during slurping
2074 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2080 if (SourcePos::hasErrors()) {
2081 SourcePos::printErrors(stderr);
2089 * -S flag points to a source directory containing drawable* folders
2090 * -C flag points to destination directory. The folder structure in the
2091 * source directory will be mirrored to the destination (cache) directory
2094 * Destination directory will be updated to match the PNG files in
2095 * the source directory.
2097 int doCrunch(Bundle* bundle)
2099 fprintf(stdout, "Crunching PNG Files in ");
2100 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2101 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2103 updatePreProcessedCache(bundle);
2109 * Do PNG Crunching on a single flag
2110 * -i points to a single png file
2111 * -o points to a single png output file
2113 int doSingleCrunch(Bundle* bundle)
2115 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2116 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2118 String8 input(bundle->getSingleCrunchInputFile());
2119 String8 output(bundle->getSingleCrunchOutputFile());
2121 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2122 // we can't return the status_t as it gets truncate to the lower 8 bits.
2129 char CONSOLE_DATA[2925] = {
2130 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2131 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2132 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2133 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2134 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2135 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2136 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2137 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2138 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2139 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2140 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2141 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2142 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2143 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2144 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2145 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2146 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2147 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2148 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2149 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2150 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2151 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2152 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2153 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2154 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2155 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2156 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2157 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2158 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2159 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2160 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2161 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2162 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2163 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2164 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2165 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2166 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2167 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2168 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2169 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2170 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2171 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2172 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2173 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2174 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2175 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2176 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2177 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2178 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2179 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2180 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2181 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2182 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2183 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2184 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2185 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2186 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2187 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2188 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2189 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2190 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2191 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2192 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2193 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2194 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2195 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2196 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2197 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2198 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2199 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2200 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2201 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2202 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2203 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2204 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2205 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2206 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2207 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2208 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2209 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2210 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2211 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2212 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2213 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2214 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2215 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2216 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2217 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2218 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2219 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2220 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2221 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2222 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2223 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2224 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2225 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2226 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2227 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2228 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2229 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2230 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2231 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2232 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2233 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2234 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2235 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2236 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2237 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2238 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2239 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2240 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2241 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2242 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2243 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2244 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2245 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2246 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2247 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2248 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2249 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2250 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2251 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2252 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2253 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2254 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2255 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2256 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2257 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2258 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2259 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2260 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2261 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2262 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2263 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2264 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2265 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2266 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2267 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2268 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2269 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2270 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2271 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2272 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2273 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2274 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2275 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2276 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2277 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2278 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2279 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2280 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2281 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2282 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2283 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2284 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2285 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2286 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2287 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2288 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2289 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2290 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2291 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2292 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10