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,
380 const char *getComponentName(String8 &pkgName, String8 &componentName) {
381 ssize_t idx = componentName.find(".");
382 String8 retStr(pkgName);
384 retStr += componentName;
385 } else if (idx < 0) {
387 retStr += componentName;
389 return componentName.string();
391 return retStr.string();
394 static void printCompatibleScreens(ResXMLTree& tree) {
396 ResXMLTree::event_code_t code;
399 printf("compatible-screens:");
400 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
401 if (code == ResXMLTree::END_TAG) {
408 if (code != ResXMLTree::START_TAG) {
412 String8 tag(tree.getElementName(&len));
413 if (tag == "screen") {
414 int32_t screenSize = getIntegerAttribute(tree,
415 SCREEN_SIZE_ATTR, NULL, -1);
416 int32_t screenDensity = getIntegerAttribute(tree,
417 SCREEN_DENSITY_ATTR, NULL, -1);
418 if (screenSize > 0 && screenDensity > 0) {
423 printf("'%d/%d'", screenSize, screenDensity);
430 Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
431 String8 *outError = NULL)
433 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
434 if (aidAsset == NULL) {
435 if (outError != NULL) *outError = "xml resource does not exist";
436 return Vector<String8>();
439 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
441 bool withinApduService = false;
442 Vector<String8> categories;
446 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
450 ResXMLTree::event_code_t code;
451 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
452 if (code == ResXMLTree::END_TAG) {
454 String8 tag(tree.getElementName(&len));
456 if (depth == 0 && tag == serviceTagName) {
457 withinApduService = false;
460 } else if (code == ResXMLTree::START_TAG) {
462 String8 tag(tree.getElementName(&len));
465 if (tag == serviceTagName) {
466 withinApduService = true;
468 } else if (depth == 2 && withinApduService) {
469 if (tag == "aid-group") {
470 String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
472 if (outError != NULL) *outError = error;
473 return Vector<String8>();
476 categories.add(category);
486 * Handle the "dump" command, to extract select data from an archive.
488 extern char CONSOLE_DATA[2925]; // see EOF
489 int doDump(Bundle* bundle)
491 status_t result = UNKNOWN_ERROR;
494 if (bundle->getFileSpecCount() < 1) {
495 fprintf(stderr, "ERROR: no dump option specified\n");
499 if (bundle->getFileSpecCount() < 2) {
500 fprintf(stderr, "ERROR: no dump file specified\n");
504 const char* option = bundle->getFileSpecEntry(0);
505 const char* filename = bundle->getFileSpecEntry(1);
509 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
510 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
514 // Make a dummy config for retrieving resources... we need to supply
515 // non-default values for some configs so that we can retrieve resources
516 // in the app that don't have a default. The most important of these is
517 // the API version because key resources like icons will have an implicit
518 // version if they are using newer config types like density.
519 ResTable_config config;
520 config.language[0] = 'e';
521 config.language[1] = 'n';
522 config.country[0] = 'U';
523 config.country[1] = 'S';
524 config.orientation = ResTable_config::ORIENTATION_PORT;
525 config.density = ResTable_config::DENSITY_MEDIUM;
526 config.sdkVersion = 10000; // Very high.
527 config.screenWidthDp = 320;
528 config.screenHeightDp = 480;
529 config.smallestScreenWidthDp = 320;
530 assets.setConfiguration(config);
532 const ResTable& res = assets.getResources(false);
534 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
538 if (strcmp("resources", option) == 0) {
539 #ifndef HAVE_ANDROID_OS
540 res.print(bundle->getValues());
543 } else if (strcmp("strings", option) == 0) {
544 const ResStringPool* pool = res.getTableStringBlock(0);
545 printStringPool(pool);
547 } else if (strcmp("xmltree", option) == 0) {
548 if (bundle->getFileSpecCount() < 3) {
549 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
553 for (int i=2; i<bundle->getFileSpecCount(); i++) {
554 const char* resname = bundle->getFileSpecEntry(i);
556 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
558 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
562 if (tree.setTo(asset->getBuffer(true),
563 asset->getLength()) != NO_ERROR) {
564 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
568 printXMLBlock(&tree);
574 } else if (strcmp("xmlstrings", option) == 0) {
575 if (bundle->getFileSpecCount() < 3) {
576 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
580 for (int i=2; i<bundle->getFileSpecCount(); i++) {
581 const char* resname = bundle->getFileSpecEntry(i);
583 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
585 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
589 if (tree.setTo(asset->getBuffer(true),
590 asset->getLength()) != NO_ERROR) {
591 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
594 printStringPool(&tree.getStrings());
601 asset = assets.openNonAsset("AndroidManifest.xml",
602 Asset::ACCESS_BUFFER);
604 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
608 if (tree.setTo(asset->getBuffer(true),
609 asset->getLength()) != NO_ERROR) {
610 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
615 if (strcmp("permissions", option) == 0) {
617 ResXMLTree::event_code_t code;
619 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
620 if (code == ResXMLTree::END_TAG) {
624 if (code != ResXMLTree::START_TAG) {
628 String8 tag(tree.getElementName(&len));
629 //printf("Depth %d tag %s\n", depth, tag.string());
631 if (tag != "manifest") {
632 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
635 String8 pkg = getAttribute(tree, NULL, "package", NULL);
636 printf("package: %s\n", pkg.string());
637 } else if (depth == 2 && tag == "permission") {
639 String8 name = getAttribute(tree, NAME_ATTR, &error);
641 fprintf(stderr, "ERROR: %s\n", error.string());
644 printf("permission: %s\n", name.string());
645 } else if (depth == 2 && tag == "uses-permission") {
647 String8 name = getAttribute(tree, NAME_ATTR, &error);
649 fprintf(stderr, "ERROR: %s\n", error.string());
652 printf("uses-permission: %s\n", name.string());
653 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
655 printf("optional-permission: %s\n", name.string());
659 } else if (strcmp("badging", option) == 0) {
660 Vector<String8> locales;
661 res.getLocales(&locales);
663 Vector<ResTable_config> configs;
664 res.getConfigurations(&configs);
665 SortedVector<int> densities;
666 const size_t NC = configs.size();
667 for (size_t i=0; i<NC; i++) {
668 int dens = configs[i].density;
669 if (dens == 0) dens = 160;
674 ResXMLTree::event_code_t code;
677 bool withinActivity = false;
678 bool isMainActivity = false;
679 bool isLauncherActivity = false;
680 bool isSearchable = false;
681 bool withinApplication = false;
682 bool withinSupportsInput = false;
683 bool withinReceiver = false;
684 bool withinService = false;
685 bool withinIntentFilter = false;
686 bool hasMainActivity = false;
687 bool hasOtherActivities = false;
688 bool hasOtherReceivers = false;
689 bool hasOtherServices = false;
690 bool hasWallpaperService = false;
691 bool hasImeService = false;
692 bool hasAccessibilityService = false;
693 bool hasPrintService = false;
694 bool hasWidgetReceivers = false;
695 bool hasDeviceAdminReceiver = false;
696 bool hasIntentFilter = false;
697 bool hasPaymentService = false;
698 bool actMainActivity = false;
699 bool actWidgetReceivers = false;
700 bool actDeviceAdminEnabled = false;
701 bool actImeService = false;
702 bool actWallpaperService = false;
703 bool actAccessibilityService = false;
704 bool actPrintService = false;
705 bool actHostApduService = false;
706 bool actOffHostApduService = false;
707 bool hasMetaHostPaymentCategory = false;
708 bool hasMetaOffHostPaymentCategory = false;
710 // These permissions are required by services implementing services
711 // the system binds to (IME, Accessibility, PrintServices, etc.)
712 bool hasBindDeviceAdminPermission = false;
713 bool hasBindInputMethodPermission = false;
714 bool hasBindAccessibilityServicePermission = false;
715 bool hasBindPrintServicePermission = false;
716 bool hasBindNfcServicePermission = false;
718 // These two implement the implicit permissions that are granted
719 // to pre-1.6 applications.
720 bool hasWriteExternalStoragePermission = false;
721 bool hasReadPhoneStatePermission = false;
723 // If an app requests write storage, they will also get read storage.
724 bool hasReadExternalStoragePermission = false;
726 // Implement transition to read and write call log.
727 bool hasReadContactsPermission = false;
728 bool hasWriteContactsPermission = false;
729 bool hasReadCallLogPermission = false;
730 bool hasWriteCallLogPermission = false;
732 // This next group of variables is used to implement a group of
733 // backward-compatibility heuristics necessitated by the addition of
734 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
735 // heuristic is "if an app requests a permission but doesn't explicitly
736 // request the corresponding <uses-feature>, presume it's there anyway".
737 bool specCameraFeature = false; // camera-related
738 bool specCameraAutofocusFeature = false;
739 bool reqCameraAutofocusFeature = false;
740 bool reqCameraFlashFeature = false;
741 bool hasCameraPermission = false;
742 bool specLocationFeature = false; // location-related
743 bool specNetworkLocFeature = false;
744 bool reqNetworkLocFeature = false;
745 bool specGpsFeature = false;
746 bool reqGpsFeature = false;
747 bool hasMockLocPermission = false;
748 bool hasCoarseLocPermission = false;
749 bool hasGpsPermission = false;
750 bool hasGeneralLocPermission = false;
751 bool specBluetoothFeature = false; // Bluetooth API-related
752 bool hasBluetoothPermission = false;
753 bool specMicrophoneFeature = false; // microphone-related
754 bool hasRecordAudioPermission = false;
755 bool specWiFiFeature = false;
756 bool hasWiFiPermission = false;
757 bool specTelephonyFeature = false; // telephony-related
758 bool reqTelephonySubFeature = false;
759 bool hasTelephonyPermission = false;
760 bool specTouchscreenFeature = false; // touchscreen-related
761 bool specMultitouchFeature = false;
762 bool reqDistinctMultitouchFeature = false;
763 bool specScreenPortraitFeature = false;
764 bool specScreenLandscapeFeature = false;
765 bool reqScreenPortraitFeature = false;
766 bool reqScreenLandscapeFeature = false;
767 // 2.2 also added some other features that apps can request, but that
768 // have no corresponding permission, so we cannot implement any
769 // back-compatibility heuristic for them. The below are thus unnecessary
770 // (but are retained here for documentary purposes.)
771 //bool specCompassFeature = false;
772 //bool specAccelerometerFeature = false;
773 //bool specProximityFeature = false;
774 //bool specAmbientLightFeature = false;
775 //bool specLiveWallpaperFeature = false;
779 int normalScreen = 1;
781 int xlargeScreen = 1;
783 int requiresSmallestWidthDp = 0;
784 int compatibleWidthLimitDp = 0;
785 int largestWidthLimitDp = 0;
787 String8 activityName;
788 String8 activityLabel;
789 String8 activityIcon;
790 String8 receiverName;
792 Vector<String8> supportedInput;
793 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
794 if (code == ResXMLTree::END_TAG) {
797 if (withinSupportsInput && !supportedInput.isEmpty()) {
798 printf("supports-input: '");
799 const size_t N = supportedInput.size();
800 for (size_t i=0; i<N; i++) {
801 printf("%s", supportedInput[i].string());
808 supportedInput.clear();
810 withinApplication = false;
811 withinSupportsInput = false;
812 } else if (depth < 3) {
813 if (withinActivity && isMainActivity && isLauncherActivity) {
814 const char *aName = getComponentName(pkg, activityName);
815 printf("launchable-activity:");
817 printf(" name='%s' ", aName);
819 printf(" label='%s' icon='%s'\n",
820 activityLabel.string(),
821 activityIcon.string());
823 if (!hasIntentFilter) {
824 hasOtherActivities |= withinActivity;
825 hasOtherReceivers |= withinReceiver;
826 hasOtherServices |= withinService;
829 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
830 hasBindNfcServicePermission);
831 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
832 hasBindNfcServicePermission);
835 withinActivity = false;
836 withinService = false;
837 withinReceiver = false;
838 hasIntentFilter = false;
839 isMainActivity = isLauncherActivity = false;
840 } else if (depth < 4) {
841 if (withinIntentFilter) {
842 if (withinActivity) {
843 hasMainActivity |= actMainActivity;
844 hasOtherActivities |= !actMainActivity;
845 } else if (withinReceiver) {
846 hasWidgetReceivers |= actWidgetReceivers;
847 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
848 hasBindDeviceAdminPermission);
849 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
850 } else if (withinService) {
851 hasImeService |= actImeService;
852 hasWallpaperService |= actWallpaperService;
853 hasAccessibilityService |= (actAccessibilityService &&
854 hasBindAccessibilityServicePermission);
855 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
856 hasOtherServices |= (!actImeService && !actWallpaperService &&
857 !actAccessibilityService && !actPrintService &&
858 !actHostApduService && !actOffHostApduService);
861 withinIntentFilter = false;
865 if (code != ResXMLTree::START_TAG) {
869 String8 tag(tree.getElementName(&len));
870 //printf("Depth %d, %s\n", depth, tag.string());
872 if (tag != "manifest") {
873 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
876 pkg = getAttribute(tree, NULL, "package", NULL);
877 printf("package: name='%s' ", pkg.string());
878 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
880 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
883 if (versionCode > 0) {
884 printf("versionCode='%d' ", versionCode);
886 printf("versionCode='' ");
888 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
890 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
893 printf("versionName='%s'\n", versionName.string());
894 } else if (depth == 2) {
895 withinApplication = false;
896 if (tag == "application") {
897 withinApplication = true;
900 const size_t NL = locales.size();
901 for (size_t i=0; i<NL; i++) {
902 const char* localeStr = locales[i].string();
903 assets.setLocale(localeStr != NULL ? localeStr : "");
904 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
906 if (localeStr == NULL || strlen(localeStr) == 0) {
908 printf("application-label:'%s'\n", llabel.string());
913 printf("application-label-%s:'%s'\n", localeStr,
919 ResTable_config tmpConfig = config;
920 const size_t ND = densities.size();
921 for (size_t i=0; i<ND; i++) {
922 tmpConfig.density = densities[i];
923 assets.setConfiguration(tmpConfig);
924 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
926 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
929 assets.setConfiguration(config);
931 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
933 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
936 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
938 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
941 printf("application: label='%s' ", label.string());
942 printf("icon='%s'\n", icon.string());
944 printf("testOnly='%d'\n", testOnly);
947 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
949 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
952 if (debuggable != 0) {
953 printf("application-debuggable\n");
955 } else if (tag == "uses-sdk") {
956 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
959 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
961 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
965 if (name == "Donut") targetSdk = 4;
966 printf("sdkVersion:'%s'\n", name.string());
967 } else if (code != -1) {
969 printf("sdkVersion:'%d'\n", code);
971 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
973 printf("maxSdkVersion:'%d'\n", code);
975 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
978 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
980 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
984 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
985 printf("targetSdkVersion:'%s'\n", name.string());
986 } else if (code != -1) {
987 if (targetSdk < code) {
990 printf("targetSdkVersion:'%d'\n", code);
992 } else if (tag == "uses-configuration") {
993 int32_t reqTouchScreen = getIntegerAttribute(tree,
994 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
995 int32_t reqKeyboardType = getIntegerAttribute(tree,
996 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
997 int32_t reqHardKeyboard = getIntegerAttribute(tree,
998 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
999 int32_t reqNavigation = getIntegerAttribute(tree,
1000 REQ_NAVIGATION_ATTR, NULL, 0);
1001 int32_t reqFiveWayNav = getIntegerAttribute(tree,
1002 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
1003 printf("uses-configuration:");
1004 if (reqTouchScreen != 0) {
1005 printf(" reqTouchScreen='%d'", reqTouchScreen);
1007 if (reqKeyboardType != 0) {
1008 printf(" reqKeyboardType='%d'", reqKeyboardType);
1010 if (reqHardKeyboard != 0) {
1011 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1013 if (reqNavigation != 0) {
1014 printf(" reqNavigation='%d'", reqNavigation);
1016 if (reqFiveWayNav != 0) {
1017 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1020 } else if (tag == "supports-input") {
1021 withinSupportsInput = true;
1022 } else if (tag == "supports-screens") {
1023 smallScreen = getIntegerAttribute(tree,
1024 SMALL_SCREEN_ATTR, NULL, 1);
1025 normalScreen = getIntegerAttribute(tree,
1026 NORMAL_SCREEN_ATTR, NULL, 1);
1027 largeScreen = getIntegerAttribute(tree,
1028 LARGE_SCREEN_ATTR, NULL, 1);
1029 xlargeScreen = getIntegerAttribute(tree,
1030 XLARGE_SCREEN_ATTR, NULL, 1);
1031 anyDensity = getIntegerAttribute(tree,
1032 ANY_DENSITY_ATTR, NULL, 1);
1033 requiresSmallestWidthDp = getIntegerAttribute(tree,
1034 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1035 compatibleWidthLimitDp = getIntegerAttribute(tree,
1036 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1037 largestWidthLimitDp = getIntegerAttribute(tree,
1038 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1039 } else if (tag == "uses-feature") {
1040 String8 name = getAttribute(tree, NAME_ATTR, &error);
1042 if (name != "" && error == "") {
1043 int req = getIntegerAttribute(tree,
1044 REQUIRED_ATTR, NULL, 1);
1046 if (name == "android.hardware.camera") {
1047 specCameraFeature = true;
1048 } else if (name == "android.hardware.camera.autofocus") {
1049 // these have no corresponding permission to check for,
1050 // but should imply the foundational camera permission
1051 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
1052 specCameraAutofocusFeature = true;
1053 } else if (req && (name == "android.hardware.camera.flash")) {
1054 // these have no corresponding permission to check for,
1055 // but should imply the foundational camera permission
1056 reqCameraFlashFeature = true;
1057 } else if (name == "android.hardware.location") {
1058 specLocationFeature = true;
1059 } else if (name == "android.hardware.location.network") {
1060 specNetworkLocFeature = true;
1061 reqNetworkLocFeature = reqNetworkLocFeature || req;
1062 } else if (name == "android.hardware.location.gps") {
1063 specGpsFeature = true;
1064 reqGpsFeature = reqGpsFeature || req;
1065 } else if (name == "android.hardware.bluetooth") {
1066 specBluetoothFeature = true;
1067 } else if (name == "android.hardware.touchscreen") {
1068 specTouchscreenFeature = true;
1069 } else if (name == "android.hardware.touchscreen.multitouch") {
1070 specMultitouchFeature = true;
1071 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
1072 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
1073 } else if (name == "android.hardware.microphone") {
1074 specMicrophoneFeature = true;
1075 } else if (name == "android.hardware.wifi") {
1076 specWiFiFeature = true;
1077 } else if (name == "android.hardware.telephony") {
1078 specTelephonyFeature = true;
1079 } else if (req && (name == "android.hardware.telephony.gsm" ||
1080 name == "android.hardware.telephony.cdma")) {
1081 // these have no corresponding permission to check for,
1082 // but should imply the foundational telephony permission
1083 reqTelephonySubFeature = true;
1084 } else if (name == "android.hardware.screen.portrait") {
1085 specScreenPortraitFeature = true;
1086 } else if (name == "android.hardware.screen.landscape") {
1087 specScreenLandscapeFeature = true;
1089 printf("uses-feature%s:'%s'\n",
1090 req ? "" : "-not-required", name.string());
1092 int vers = getIntegerAttribute(tree,
1093 GL_ES_VERSION_ATTR, &error);
1095 printf("uses-gl-es:'0x%x'\n", vers);
1098 } else if (tag == "uses-permission") {
1099 String8 name = getAttribute(tree, NAME_ATTR, &error);
1100 if (name != "" && error == "") {
1101 if (name == "android.permission.CAMERA") {
1102 hasCameraPermission = true;
1103 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1104 hasGpsPermission = true;
1105 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1106 hasMockLocPermission = true;
1107 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1108 hasCoarseLocPermission = true;
1109 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1110 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1111 hasGeneralLocPermission = true;
1112 } else if (name == "android.permission.BLUETOOTH" ||
1113 name == "android.permission.BLUETOOTH_ADMIN") {
1114 hasBluetoothPermission = true;
1115 } else if (name == "android.permission.RECORD_AUDIO") {
1116 hasRecordAudioPermission = true;
1117 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1118 name == "android.permission.CHANGE_WIFI_STATE" ||
1119 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1120 hasWiFiPermission = true;
1121 } else if (name == "android.permission.CALL_PHONE" ||
1122 name == "android.permission.CALL_PRIVILEGED" ||
1123 name == "android.permission.MODIFY_PHONE_STATE" ||
1124 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1125 name == "android.permission.READ_SMS" ||
1126 name == "android.permission.RECEIVE_SMS" ||
1127 name == "android.permission.RECEIVE_MMS" ||
1128 name == "android.permission.RECEIVE_WAP_PUSH" ||
1129 name == "android.permission.SEND_SMS" ||
1130 name == "android.permission.WRITE_APN_SETTINGS" ||
1131 name == "android.permission.WRITE_SMS") {
1132 hasTelephonyPermission = true;
1133 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1134 hasWriteExternalStoragePermission = true;
1135 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1136 hasReadExternalStoragePermission = true;
1137 } else if (name == "android.permission.READ_PHONE_STATE") {
1138 hasReadPhoneStatePermission = true;
1139 } else if (name == "android.permission.READ_CONTACTS") {
1140 hasReadContactsPermission = true;
1141 } else if (name == "android.permission.WRITE_CONTACTS") {
1142 hasWriteContactsPermission = true;
1143 } else if (name == "android.permission.READ_CALL_LOG") {
1144 hasReadCallLogPermission = true;
1145 } else if (name == "android.permission.WRITE_CALL_LOG") {
1146 hasWriteCallLogPermission = true;
1148 printf("uses-permission:'%s'\n", name.string());
1149 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
1151 printf("optional-permission:'%s'\n", name.string());
1154 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1158 } else if (tag == "uses-package") {
1159 String8 name = getAttribute(tree, NAME_ATTR, &error);
1160 if (name != "" && error == "") {
1161 printf("uses-package:'%s'\n", name.string());
1163 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1167 } else if (tag == "original-package") {
1168 String8 name = getAttribute(tree, NAME_ATTR, &error);
1169 if (name != "" && error == "") {
1170 printf("original-package:'%s'\n", name.string());
1172 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1176 } else if (tag == "supports-gl-texture") {
1177 String8 name = getAttribute(tree, NAME_ATTR, &error);
1178 if (name != "" && error == "") {
1179 printf("supports-gl-texture:'%s'\n", name.string());
1181 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1185 } else if (tag == "compatible-screens") {
1186 printCompatibleScreens(tree);
1188 } else if (tag == "package-verifier") {
1189 String8 name = getAttribute(tree, NAME_ATTR, &error);
1190 if (name != "" && error == "") {
1191 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1192 if (publicKey != "" && error == "") {
1193 printf("package-verifier: name='%s' publicKey='%s'\n",
1194 name.string(), publicKey.string());
1198 } else if (depth == 3) {
1199 withinActivity = false;
1200 withinReceiver = false;
1201 withinService = false;
1202 hasIntentFilter = false;
1203 hasMetaHostPaymentCategory = false;
1204 hasMetaOffHostPaymentCategory = false;
1205 hasBindDeviceAdminPermission = false;
1206 hasBindInputMethodPermission = false;
1207 hasBindAccessibilityServicePermission = false;
1208 hasBindPrintServicePermission = false;
1209 hasBindNfcServicePermission = false;
1210 if (withinApplication) {
1211 if(tag == "activity") {
1212 withinActivity = true;
1213 activityName = getAttribute(tree, NAME_ATTR, &error);
1215 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1220 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1222 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1227 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1229 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1234 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1235 SCREEN_ORIENTATION_ATTR, &error);
1237 if (orien == 0 || orien == 6 || orien == 8) {
1238 // Requests landscape, sensorLandscape, or reverseLandscape.
1239 reqScreenLandscapeFeature = true;
1240 } else if (orien == 1 || orien == 7 || orien == 9) {
1241 // Requests portrait, sensorPortrait, or reversePortrait.
1242 reqScreenPortraitFeature = true;
1245 } else if (tag == "uses-library") {
1246 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1249 "ERROR getting 'android:name' attribute for uses-library"
1250 " %s\n", error.string());
1253 int req = getIntegerAttribute(tree,
1254 REQUIRED_ATTR, NULL, 1);
1255 printf("uses-library%s:'%s'\n",
1256 req ? "" : "-not-required", libraryName.string());
1257 } else if (tag == "receiver") {
1258 withinReceiver = true;
1259 receiverName = getAttribute(tree, NAME_ATTR, &error);
1263 "ERROR getting 'android:name' attribute for receiver:"
1264 " %s\n", error.string());
1268 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1270 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1271 hasBindDeviceAdminPermission = true;
1274 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1275 " receiver '%s': %s\n", receiverName.string(), error.string());
1277 } else if (tag == "service") {
1278 withinService = true;
1279 serviceName = getAttribute(tree, NAME_ATTR, &error);
1282 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1283 " service: %s\n", error.string());
1287 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1289 if (permission == "android.permission.BIND_INPUT_METHOD") {
1290 hasBindInputMethodPermission = true;
1291 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1292 hasBindAccessibilityServicePermission = true;
1293 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1294 hasBindPrintServicePermission = true;
1295 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1296 hasBindNfcServicePermission = true;
1299 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1300 " service '%s': %s\n", serviceName.string(), error.string());
1303 } else if (withinSupportsInput && tag == "input-type") {
1304 String8 name = getAttribute(tree, NAME_ATTR, &error);
1305 if (name != "" && error == "") {
1306 supportedInput.add(name);
1308 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1313 } else if (depth == 4) {
1314 if (tag == "intent-filter") {
1315 hasIntentFilter = true;
1316 withinIntentFilter = true;
1317 actMainActivity = false;
1318 actWidgetReceivers = false;
1319 actImeService = false;
1320 actWallpaperService = false;
1321 actAccessibilityService = false;
1322 actPrintService = false;
1323 actDeviceAdminEnabled = false;
1324 actHostApduService = false;
1325 actOffHostApduService = false;
1326 } else if (withinService && tag == "meta-data") {
1327 String8 name = getAttribute(tree, NAME_ATTR, &error);
1329 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1330 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1334 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1335 name == "android.nfc.cardemulation.off_host_apdu_service") {
1336 bool offHost = true;
1337 if (name == "android.nfc.cardemulation.host_apdu_service") {
1341 String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
1343 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1344 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1348 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1351 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1352 serviceName.string());
1356 const size_t catLen = categories.size();
1357 for (size_t i = 0; i < catLen; i++) {
1358 bool paymentCategory = (categories[i] == "payment");
1360 hasMetaOffHostPaymentCategory |= paymentCategory;
1362 hasMetaHostPaymentCategory |= paymentCategory;
1367 } else if ((depth == 5) && withinIntentFilter){
1369 if (tag == "action") {
1370 action = getAttribute(tree, NAME_ATTR, &error);
1372 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1375 if (withinActivity) {
1376 if (action == "android.intent.action.MAIN") {
1377 isMainActivity = true;
1378 actMainActivity = true;
1380 } else if (withinReceiver) {
1381 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1382 actWidgetReceivers = true;
1383 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1384 actDeviceAdminEnabled = true;
1386 } else if (withinService) {
1387 if (action == "android.view.InputMethod") {
1388 actImeService = true;
1389 } else if (action == "android.service.wallpaper.WallpaperService") {
1390 actWallpaperService = true;
1391 } else if (action == "android.accessibilityservice.AccessibilityService") {
1392 actAccessibilityService = true;
1393 } else if (action == "android.printservice.PrintService") {
1394 actPrintService = true;
1395 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1396 actHostApduService = true;
1397 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1398 actOffHostApduService = true;
1401 if (action == "android.intent.action.SEARCH") {
1402 isSearchable = true;
1406 if (tag == "category") {
1407 String8 category = getAttribute(tree, NAME_ATTR, &error);
1409 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1412 if (withinActivity) {
1413 if (category == "android.intent.category.LAUNCHER") {
1414 isLauncherActivity = true;
1421 // Pre-1.6 implicitly granted permission compatibility logic
1422 if (targetSdk < 4) {
1423 if (!hasWriteExternalStoragePermission) {
1424 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
1425 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1426 "'targetSdkVersion < 4'\n");
1427 hasWriteExternalStoragePermission = true;
1429 if (!hasReadPhoneStatePermission) {
1430 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
1431 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1432 "'targetSdkVersion < 4'\n");
1436 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1437 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1438 // do this (regardless of target API version) because we can't have
1439 // an app with write permission but not read permission.
1440 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1441 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
1442 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1443 "'requested WRITE_EXTERNAL_STORAGE'\n");
1446 // Pre-JellyBean call log permission compatibility.
1447 if (targetSdk < 16) {
1448 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1449 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
1450 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1451 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
1453 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1454 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
1455 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1456 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
1460 /* The following blocks handle printing "inferred" uses-features, based
1461 * on whether related features or permissions are used by the app.
1462 * Note that the various spec*Feature variables denote whether the
1463 * relevant tag was *present* in the AndroidManfest, not that it was
1464 * present and set to true.
1466 // Camera-related back-compatibility logic
1467 if (!specCameraFeature) {
1468 if (reqCameraFlashFeature) {
1469 // if app requested a sub-feature (autofocus or flash) and didn't
1470 // request the base camera feature, we infer that it meant to
1471 printf("uses-feature:'android.hardware.camera'\n");
1472 printf("uses-implied-feature:'android.hardware.camera'," \
1473 "'requested android.hardware.camera.flash feature'\n");
1474 } else if (reqCameraAutofocusFeature) {
1475 // if app requested a sub-feature (autofocus or flash) and didn't
1476 // request the base camera feature, we infer that it meant to
1477 printf("uses-feature:'android.hardware.camera'\n");
1478 printf("uses-implied-feature:'android.hardware.camera'," \
1479 "'requested android.hardware.camera.autofocus feature'\n");
1480 } else if (hasCameraPermission) {
1481 // if app wants to use camera but didn't request the feature, we infer
1482 // that it meant to, and further that it wants autofocus
1483 // (which was the 1.0 - 1.5 behavior)
1484 printf("uses-feature:'android.hardware.camera'\n");
1485 if (!specCameraAutofocusFeature) {
1486 printf("uses-feature:'android.hardware.camera.autofocus'\n");
1487 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1488 "'requested android.permission.CAMERA permission'\n");
1493 // Location-related back-compatibility logic
1494 if (!specLocationFeature &&
1495 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1496 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1497 // if app either takes a location-related permission or requests one of the
1498 // sub-features, we infer that it also meant to request the base location feature
1499 printf("uses-feature:'android.hardware.location'\n");
1500 printf("uses-implied-feature:'android.hardware.location'," \
1501 "'requested a location access permission'\n");
1503 if (!specGpsFeature && hasGpsPermission) {
1504 // if app takes GPS (FINE location) perm but does not request the GPS
1505 // feature, we infer that it meant to
1506 printf("uses-feature:'android.hardware.location.gps'\n");
1507 printf("uses-implied-feature:'android.hardware.location.gps'," \
1508 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
1510 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1511 // if app takes Network location (COARSE location) perm but does not request the
1512 // network location feature, we infer that it meant to
1513 printf("uses-feature:'android.hardware.location.network'\n");
1514 printf("uses-implied-feature:'android.hardware.location.network'," \
1515 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
1518 // Bluetooth-related compatibility logic
1519 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
1520 // if app takes a Bluetooth permission but does not request the Bluetooth
1521 // feature, we infer that it meant to
1522 printf("uses-feature:'android.hardware.bluetooth'\n");
1523 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1524 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1525 "permission and targetSdkVersion > 4'\n");
1528 // Microphone-related compatibility logic
1529 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1530 // if app takes the record-audio permission but does not request the microphone
1531 // feature, we infer that it meant to
1532 printf("uses-feature:'android.hardware.microphone'\n");
1533 printf("uses-implied-feature:'android.hardware.microphone'," \
1534 "'requested android.permission.RECORD_AUDIO permission'\n");
1537 // WiFi-related compatibility logic
1538 if (!specWiFiFeature && hasWiFiPermission) {
1539 // if app takes one of the WiFi permissions but does not request the WiFi
1540 // feature, we infer that it meant to
1541 printf("uses-feature:'android.hardware.wifi'\n");
1542 printf("uses-implied-feature:'android.hardware.wifi'," \
1543 "'requested android.permission.ACCESS_WIFI_STATE, " \
1544 "android.permission.CHANGE_WIFI_STATE, or " \
1545 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
1548 // Telephony-related compatibility logic
1549 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1550 // if app takes one of the telephony permissions or requests a sub-feature but
1551 // does not request the base telephony feature, we infer that it meant to
1552 printf("uses-feature:'android.hardware.telephony'\n");
1553 printf("uses-implied-feature:'android.hardware.telephony'," \
1554 "'requested a telephony-related permission or feature'\n");
1557 // Touchscreen-related back-compatibility logic
1558 if (!specTouchscreenFeature) { // not a typo!
1559 // all apps are presumed to require a touchscreen, unless they explicitly say
1560 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1561 // Note that specTouchscreenFeature is true if the tag is present, regardless
1562 // of whether its value is true or false, so this is safe
1563 printf("uses-feature:'android.hardware.touchscreen'\n");
1564 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1565 "'assumed you require a touch screen unless explicitly made optional'\n");
1567 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1568 // if app takes one of the telephony permissions or requests a sub-feature but
1569 // does not request the base telephony feature, we infer that it meant to
1570 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1571 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1572 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
1575 // Landscape/portrait-related compatibility logic
1576 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1577 // If the app has specified any activities in its manifest
1578 // that request a specific orientation, then assume that
1579 // orientation is required.
1580 if (reqScreenLandscapeFeature) {
1581 printf("uses-feature:'android.hardware.screen.landscape'\n");
1582 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1583 "'one or more activities have specified a landscape orientation'\n");
1585 if (reqScreenPortraitFeature) {
1586 printf("uses-feature:'android.hardware.screen.portrait'\n");
1587 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1588 "'one or more activities have specified a portrait orientation'\n");
1592 if (hasMainActivity) {
1595 if (hasWidgetReceivers) {
1596 printf("app-widget\n");
1598 if (hasDeviceAdminReceiver) {
1599 printf("device-admin\n");
1601 if (hasImeService) {
1604 if (hasWallpaperService) {
1605 printf("wallpaper\n");
1607 if (hasAccessibilityService) {
1608 printf("accessibility\n");
1610 if (hasPrintService) {
1613 if (hasPaymentService) {
1614 printf("payment\n");
1616 if (hasOtherActivities) {
1617 printf("other-activities\n");
1622 if (hasOtherReceivers) {
1623 printf("other-receivers\n");
1625 if (hasOtherServices) {
1626 printf("other-services\n");
1629 // For modern apps, if screen size buckets haven't been specified
1630 // but the new width ranges have, then infer the buckets from them.
1631 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1632 && requiresSmallestWidthDp > 0) {
1633 int compatWidth = compatibleWidthLimitDp;
1634 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1635 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1640 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1645 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1650 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1657 // Determine default values for any unspecified screen sizes,
1658 // based on the target SDK of the package. As of 4 (donut)
1659 // the screen size support was introduced, so all default to
1661 if (smallScreen > 0) {
1662 smallScreen = targetSdk >= 4 ? -1 : 0;
1664 if (normalScreen > 0) {
1667 if (largeScreen > 0) {
1668 largeScreen = targetSdk >= 4 ? -1 : 0;
1670 if (xlargeScreen > 0) {
1671 // Introduced in Gingerbread.
1672 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1674 if (anyDensity > 0) {
1675 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1676 || compatibleWidthLimitDp > 0) ? -1 : 0;
1678 printf("supports-screens:");
1679 if (smallScreen != 0) printf(" 'small'");
1680 if (normalScreen != 0) printf(" 'normal'");
1681 if (largeScreen != 0) printf(" 'large'");
1682 if (xlargeScreen != 0) printf(" 'xlarge'");
1684 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1685 if (requiresSmallestWidthDp > 0) {
1686 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1688 if (compatibleWidthLimitDp > 0) {
1689 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1691 if (largestWidthLimitDp > 0) {
1692 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1696 const size_t NL = locales.size();
1697 for (size_t i=0; i<NL; i++) {
1698 const char* localeStr = locales[i].string();
1699 if (localeStr == NULL || strlen(localeStr) == 0) {
1700 localeStr = "--_--";
1702 printf(" '%s'", localeStr);
1706 printf("densities:");
1707 const size_t ND = densities.size();
1708 for (size_t i=0; i<ND; i++) {
1709 printf(" '%d'", densities[i]);
1713 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1715 if (dir->getFileCount() > 0) {
1716 printf("native-code:");
1717 for (size_t i=0; i<dir->getFileCount(); i++) {
1718 printf(" '%s'", dir->getFileName(i).string());
1724 } else if (strcmp("badger", option) == 0) {
1725 printf("%s", CONSOLE_DATA);
1726 } else if (strcmp("configurations", option) == 0) {
1727 Vector<ResTable_config> configs;
1728 res.getConfigurations(&configs);
1729 const size_t N = configs.size();
1730 for (size_t i=0; i<N; i++) {
1731 printf("%s\n", configs[i].toString().string());
1734 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1745 return (result != NO_ERROR);
1750 * Handle the "add" command, which wants to add files to a new or
1751 * pre-existing archive.
1753 int doAdd(Bundle* bundle)
1755 ZipFile* zip = NULL;
1756 status_t result = UNKNOWN_ERROR;
1757 const char* zipFileName;
1759 if (bundle->getUpdate()) {
1760 /* avoid confusion */
1761 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1765 if (bundle->getFileSpecCount() < 1) {
1766 fprintf(stderr, "ERROR: must specify zip file name\n");
1769 zipFileName = bundle->getFileSpecEntry(0);
1771 if (bundle->getFileSpecCount() < 2) {
1772 fprintf(stderr, "NOTE: nothing to do\n");
1776 zip = openReadWrite(zipFileName, true);
1778 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1782 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1783 const char* fileName = bundle->getFileSpecEntry(i);
1785 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1786 printf(" '%s'... (from gzip)\n", fileName);
1787 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1789 if (bundle->getJunkPath()) {
1790 String8 storageName = String8(fileName).getPathLeaf();
1791 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1792 result = zip->add(fileName, storageName.string(),
1793 bundle->getCompressionMethod(), NULL);
1795 printf(" '%s'...\n", fileName);
1796 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1799 if (result != NO_ERROR) {
1800 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1801 if (result == NAME_NOT_FOUND)
1802 fprintf(stderr, ": file not found\n");
1803 else if (result == ALREADY_EXISTS)
1804 fprintf(stderr, ": already exists in archive\n");
1806 fprintf(stderr, "\n");
1815 return (result != NO_ERROR);
1820 * Delete files from an existing archive.
1822 int doRemove(Bundle* bundle)
1824 ZipFile* zip = NULL;
1825 status_t result = UNKNOWN_ERROR;
1826 const char* zipFileName;
1828 if (bundle->getFileSpecCount() < 1) {
1829 fprintf(stderr, "ERROR: must specify zip file name\n");
1832 zipFileName = bundle->getFileSpecEntry(0);
1834 if (bundle->getFileSpecCount() < 2) {
1835 fprintf(stderr, "NOTE: nothing to do\n");
1839 zip = openReadWrite(zipFileName, false);
1841 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1846 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1847 const char* fileName = bundle->getFileSpecEntry(i);
1850 entry = zip->getEntryByName(fileName);
1851 if (entry == NULL) {
1852 printf(" '%s' NOT FOUND\n", fileName);
1856 result = zip->remove(entry);
1858 if (result != NO_ERROR) {
1859 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1860 bundle->getFileSpecEntry(i), zipFileName);
1865 /* update the archive */
1870 return (result != NO_ERROR);
1875 * Package up an asset directory and associated application files.
1877 int doPackage(Bundle* bundle)
1879 const char* outputAPKFile;
1882 sp<AaptAssets> assets;
1885 String8 dependencyFile;
1887 // -c zz_ZZ means do pseudolocalization
1888 ResourceFilter filter;
1889 err = filter.parse(bundle->getConfigurations());
1890 if (err != NO_ERROR) {
1893 if (filter.containsPseudo()) {
1894 bundle->setPseudolocalize(true);
1897 N = bundle->getFileSpecCount();
1898 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1899 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1900 fprintf(stderr, "ERROR: no input files\n");
1904 outputAPKFile = bundle->getOutputAPKFile();
1906 // Make sure the filenames provided exist and are of the appropriate type.
1907 if (outputAPKFile) {
1909 type = getFileType(outputAPKFile);
1910 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1912 "ERROR: output file '%s' exists but is not regular file\n",
1919 assets = new AaptAssets();
1921 // Set up the resource gathering in assets if we're going to generate
1922 // dependency files. Every time we encounter a resource while slurping
1923 // the tree, we'll add it to these stores so we have full resource paths
1924 // to write to a dependency file.
1925 if (bundle->getGenDependencies()) {
1926 sp<FilePathStore> resPathStore = new FilePathStore;
1927 assets->setFullResPaths(resPathStore);
1928 sp<FilePathStore> assetPathStore = new FilePathStore;
1929 assets->setFullAssetPaths(assetPathStore);
1932 err = assets->slurpFromArgs(bundle);
1937 if (bundle->getVerbose()) {
1938 assets->print(String8());
1941 // If they asked for any fileAs that need to be compiled, do so.
1942 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1943 err = buildResources(bundle, assets);
1949 // At this point we've read everything and processed everything. From here
1950 // on out it's just writing output files.
1951 if (SourcePos::hasErrors()) {
1955 // Update symbols with information about which ones are needed as Java symbols.
1956 assets->applyJavaSymbols();
1957 if (SourcePos::hasErrors()) {
1961 // If we've been asked to generate a dependency file, do that here
1962 if (bundle->getGenDependencies()) {
1963 // If this is the packaging step, generate the dependency file next to
1964 // the output apk (e.g. bin/resources.ap_.d)
1965 if (outputAPKFile) {
1966 dependencyFile = String8(outputAPKFile);
1967 // Add the .d extension to the dependency file.
1968 dependencyFile.append(".d");
1970 // Else if this is the R.java dependency generation step,
1971 // generate the dependency file in the R.java package subdirectory
1972 // e.g. gen/com/foo/app/R.java.d
1973 dependencyFile = String8(bundle->getRClassDir());
1974 dependencyFile.appendPath("R.java.d");
1976 // Make sure we have a clean dependency file to start with
1977 fp = fopen(dependencyFile, "w");
1981 // Write out R.java constants
1982 if (!assets->havePrivateSymbols()) {
1983 if (bundle->getCustomPackage() == NULL) {
1984 // Write the R.java file into the appropriate class directory
1985 // e.g. gen/com/foo/app/R.java
1986 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1988 const String8 customPkg(bundle->getCustomPackage());
1989 err = writeResourceSymbols(bundle, assets, customPkg, true);
1994 // If we have library files, we're going to write our R.java file into
1995 // the appropriate class directory for those libraries as well.
1996 // e.g. gen/com/foo/app/lib/R.java
1997 if (bundle->getExtraPackages() != NULL) {
1999 String8 libs(bundle->getExtraPackages());
2000 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2001 while (packageString != NULL) {
2002 // Write the R.java file out with the correct package name
2003 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
2007 packageString = strtok(NULL, ":");
2009 libs.unlockBuffer();
2012 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
2016 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
2022 // Write out the ProGuard file
2023 err = writeProguardFile(bundle, assets);
2029 if (outputAPKFile) {
2030 err = writeAPK(bundle, assets, String8(outputAPKFile));
2031 if (err != NO_ERROR) {
2032 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
2037 // If we've been asked to generate a dependency file, we need to finish up here.
2038 // the writeResourceSymbols and writeAPK functions have already written the target
2039 // half of the dependency file, now we need to write the prerequisites. (files that
2040 // the R.java file or .ap_ file depend on)
2041 if (bundle->getGenDependencies()) {
2042 // Now that writeResourceSymbols or writeAPK has taken care of writing
2043 // the targets to our dependency file, we'll write the prereqs
2044 fp = fopen(dependencyFile, "a+");
2046 bool includeRaw = (outputAPKFile != NULL);
2047 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2048 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2049 // and therefore was not added to our pathstores during slurping
2050 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2056 if (SourcePos::hasErrors()) {
2057 SourcePos::printErrors(stderr);
2065 * -S flag points to a source directory containing drawable* folders
2066 * -C flag points to destination directory. The folder structure in the
2067 * source directory will be mirrored to the destination (cache) directory
2070 * Destination directory will be updated to match the PNG files in
2071 * the source directory.
2073 int doCrunch(Bundle* bundle)
2075 fprintf(stdout, "Crunching PNG Files in ");
2076 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2077 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2079 updatePreProcessedCache(bundle);
2085 * Do PNG Crunching on a single flag
2086 * -i points to a single png file
2087 * -o points to a single png output file
2089 int doSingleCrunch(Bundle* bundle)
2091 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2092 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2094 String8 input(bundle->getSingleCrunchInputFile());
2095 String8 output(bundle->getSingleCrunchOutputFile());
2097 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2098 // we can't return the status_t as it gets truncate to the lower 8 bits.
2105 char CONSOLE_DATA[2925] = {
2106 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2107 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2108 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2109 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2110 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2111 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2112 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2113 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2114 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2115 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2116 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2117 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2118 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2119 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2120 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2121 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2122 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2123 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2124 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2125 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2126 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2127 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2128 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2129 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2130 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2131 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2132 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2133 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2134 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2135 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2136 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2137 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2138 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2139 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2140 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2141 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2142 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2143 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2144 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2145 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2146 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2147 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2148 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2149 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2150 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2151 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2152 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2153 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2154 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2155 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2156 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2157 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2158 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2159 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2160 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2161 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2162 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2163 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2164 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2165 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2166 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2167 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2168 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2169 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2170 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2171 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2172 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2173 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2174 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2175 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2176 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2177 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2178 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2179 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2180 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2181 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2182 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2183 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2184 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2185 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2186 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2187 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2188 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2189 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2190 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2191 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2192 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2193 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2194 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2195 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2196 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2197 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2198 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2199 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2200 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2201 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2202 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2203 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2204 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2205 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2206 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2207 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2208 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2209 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2210 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2211 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2212 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2213 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2214 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2215 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2216 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2217 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2218 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2219 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2220 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2221 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2222 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2223 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2224 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2225 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2226 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2227 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2228 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2229 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2230 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2231 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2232 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2233 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2234 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2235 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2236 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2237 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2238 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2239 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2240 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2241 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2242 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2243 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2244 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2245 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2246 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2247 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2248 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2249 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2250 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2251 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2252 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2253 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2254 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2255 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2256 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2257 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2258 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2259 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2260 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2261 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2262 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2263 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2264 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2265 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2266 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2267 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2268 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10