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 DEBUGGABLE_ATTR = 0x0101000f,
350 VERSION_CODE_ATTR = 0x0101021b,
351 VERSION_NAME_ATTR = 0x0101021c,
352 SCREEN_ORIENTATION_ATTR = 0x0101001e,
353 MIN_SDK_VERSION_ATTR = 0x0101020c,
354 MAX_SDK_VERSION_ATTR = 0x01010271,
355 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
356 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
357 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
358 REQ_NAVIGATION_ATTR = 0x0101022a,
359 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
360 TARGET_SDK_VERSION_ATTR = 0x01010270,
361 TEST_ONLY_ATTR = 0x01010272,
362 ANY_DENSITY_ATTR = 0x0101026c,
363 GL_ES_VERSION_ATTR = 0x01010281,
364 SMALL_SCREEN_ATTR = 0x01010284,
365 NORMAL_SCREEN_ATTR = 0x01010285,
366 LARGE_SCREEN_ATTR = 0x01010286,
367 XLARGE_SCREEN_ATTR = 0x010102bf,
368 REQUIRED_ATTR = 0x0101028e,
369 SCREEN_SIZE_ATTR = 0x010102ca,
370 SCREEN_DENSITY_ATTR = 0x010102cb,
371 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
372 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
373 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
374 PUBLIC_KEY_ATTR = 0x010103a6,
377 const char *getComponentName(String8 &pkgName, String8 &componentName) {
378 ssize_t idx = componentName.find(".");
379 String8 retStr(pkgName);
381 retStr += componentName;
382 } else if (idx < 0) {
384 retStr += componentName;
386 return componentName.string();
388 return retStr.string();
391 static void printCompatibleScreens(ResXMLTree& tree) {
393 ResXMLTree::event_code_t code;
396 printf("compatible-screens:");
397 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
398 if (code == ResXMLTree::END_TAG) {
405 if (code != ResXMLTree::START_TAG) {
409 String8 tag(tree.getElementName(&len));
410 if (tag == "screen") {
411 int32_t screenSize = getIntegerAttribute(tree,
412 SCREEN_SIZE_ATTR, NULL, -1);
413 int32_t screenDensity = getIntegerAttribute(tree,
414 SCREEN_DENSITY_ATTR, NULL, -1);
415 if (screenSize > 0 && screenDensity > 0) {
420 printf("'%d/%d'", screenSize, screenDensity);
428 * Handle the "dump" command, to extract select data from an archive.
430 extern char CONSOLE_DATA[2925]; // see EOF
431 int doDump(Bundle* bundle)
433 status_t result = UNKNOWN_ERROR;
436 if (bundle->getFileSpecCount() < 1) {
437 fprintf(stderr, "ERROR: no dump option specified\n");
441 if (bundle->getFileSpecCount() < 2) {
442 fprintf(stderr, "ERROR: no dump file specified\n");
446 const char* option = bundle->getFileSpecEntry(0);
447 const char* filename = bundle->getFileSpecEntry(1);
451 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
452 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
456 // Make a dummy config for retrieving resources... we need to supply
457 // non-default values for some configs so that we can retrieve resources
458 // in the app that don't have a default. The most important of these is
459 // the API version because key resources like icons will have an implicit
460 // version if they are using newer config types like density.
461 ResTable_config config;
462 config.language[0] = 'e';
463 config.language[1] = 'n';
464 config.country[0] = 'U';
465 config.country[1] = 'S';
466 config.orientation = ResTable_config::ORIENTATION_PORT;
467 config.density = ResTable_config::DENSITY_MEDIUM;
468 config.sdkVersion = 10000; // Very high.
469 config.screenWidthDp = 320;
470 config.screenHeightDp = 480;
471 config.smallestScreenWidthDp = 320;
472 assets.setConfiguration(config);
474 const ResTable& res = assets.getResources(false);
476 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
480 if (strcmp("resources", option) == 0) {
481 #ifndef HAVE_ANDROID_OS
482 res.print(bundle->getValues());
485 } else if (strcmp("strings", option) == 0) {
486 const ResStringPool* pool = res.getTableStringBlock(0);
487 printStringPool(pool);
489 } else if (strcmp("xmltree", option) == 0) {
490 if (bundle->getFileSpecCount() < 3) {
491 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
495 for (int i=2; i<bundle->getFileSpecCount(); i++) {
496 const char* resname = bundle->getFileSpecEntry(i);
498 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
500 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
504 if (tree.setTo(asset->getBuffer(true),
505 asset->getLength()) != NO_ERROR) {
506 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
510 printXMLBlock(&tree);
516 } else if (strcmp("xmlstrings", option) == 0) {
517 if (bundle->getFileSpecCount() < 3) {
518 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
522 for (int i=2; i<bundle->getFileSpecCount(); i++) {
523 const char* resname = bundle->getFileSpecEntry(i);
525 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
527 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
531 if (tree.setTo(asset->getBuffer(true),
532 asset->getLength()) != NO_ERROR) {
533 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
536 printStringPool(&tree.getStrings());
543 asset = assets.openNonAsset("AndroidManifest.xml",
544 Asset::ACCESS_BUFFER);
546 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
550 if (tree.setTo(asset->getBuffer(true),
551 asset->getLength()) != NO_ERROR) {
552 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
557 if (strcmp("permissions", option) == 0) {
559 ResXMLTree::event_code_t code;
561 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
562 if (code == ResXMLTree::END_TAG) {
566 if (code != ResXMLTree::START_TAG) {
570 String8 tag(tree.getElementName(&len));
571 //printf("Depth %d tag %s\n", depth, tag.string());
573 if (tag != "manifest") {
574 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
577 String8 pkg = getAttribute(tree, NULL, "package", NULL);
578 printf("package: %s\n", pkg.string());
579 } else if (depth == 2 && tag == "permission") {
581 String8 name = getAttribute(tree, NAME_ATTR, &error);
583 fprintf(stderr, "ERROR: %s\n", error.string());
586 printf("permission: %s\n", name.string());
587 } else if (depth == 2 && tag == "uses-permission") {
589 String8 name = getAttribute(tree, NAME_ATTR, &error);
591 fprintf(stderr, "ERROR: %s\n", error.string());
594 printf("uses-permission: %s\n", name.string());
595 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
597 printf("optional-permission: %s\n", name.string());
601 } else if (strcmp("badging", option) == 0) {
602 Vector<String8> locales;
603 res.getLocales(&locales);
605 Vector<ResTable_config> configs;
606 res.getConfigurations(&configs);
607 SortedVector<int> densities;
608 const size_t NC = configs.size();
609 for (size_t i=0; i<NC; i++) {
610 int dens = configs[i].density;
611 if (dens == 0) dens = 160;
616 ResXMLTree::event_code_t code;
619 bool withinActivity = false;
620 bool isMainActivity = false;
621 bool isLauncherActivity = false;
622 bool isSearchable = false;
623 bool withinApplication = false;
624 bool withinReceiver = false;
625 bool withinService = false;
626 bool withinIntentFilter = false;
627 bool hasMainActivity = false;
628 bool hasOtherActivities = false;
629 bool hasOtherReceivers = false;
630 bool hasOtherServices = false;
631 bool hasWallpaperService = false;
632 bool hasImeService = false;
633 bool hasWidgetReceivers = false;
634 bool hasIntentFilter = false;
635 bool actMainActivity = false;
636 bool actWidgetReceivers = false;
637 bool actImeService = false;
638 bool actWallpaperService = false;
640 // These two implement the implicit permissions that are granted
641 // to pre-1.6 applications.
642 bool hasWriteExternalStoragePermission = false;
643 bool hasReadPhoneStatePermission = false;
645 // If an app requests write storage, they will also get read storage.
646 bool hasReadExternalStoragePermission = false;
648 // Implement transition to read and write call log.
649 bool hasReadContactsPermission = false;
650 bool hasWriteContactsPermission = false;
651 bool hasReadCallLogPermission = false;
652 bool hasWriteCallLogPermission = false;
654 // This next group of variables is used to implement a group of
655 // backward-compatibility heuristics necessitated by the addition of
656 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
657 // heuristic is "if an app requests a permission but doesn't explicitly
658 // request the corresponding <uses-feature>, presume it's there anyway".
659 bool specCameraFeature = false; // camera-related
660 bool specCameraAutofocusFeature = false;
661 bool reqCameraAutofocusFeature = false;
662 bool reqCameraFlashFeature = false;
663 bool hasCameraPermission = false;
664 bool specLocationFeature = false; // location-related
665 bool specNetworkLocFeature = false;
666 bool reqNetworkLocFeature = false;
667 bool specGpsFeature = false;
668 bool reqGpsFeature = false;
669 bool hasMockLocPermission = false;
670 bool hasCoarseLocPermission = false;
671 bool hasGpsPermission = false;
672 bool hasGeneralLocPermission = false;
673 bool specBluetoothFeature = false; // Bluetooth API-related
674 bool hasBluetoothPermission = false;
675 bool specMicrophoneFeature = false; // microphone-related
676 bool hasRecordAudioPermission = false;
677 bool specWiFiFeature = false;
678 bool hasWiFiPermission = false;
679 bool specTelephonyFeature = false; // telephony-related
680 bool reqTelephonySubFeature = false;
681 bool hasTelephonyPermission = false;
682 bool specTouchscreenFeature = false; // touchscreen-related
683 bool specMultitouchFeature = false;
684 bool reqDistinctMultitouchFeature = false;
685 bool specScreenPortraitFeature = false;
686 bool specScreenLandscapeFeature = false;
687 bool reqScreenPortraitFeature = false;
688 bool reqScreenLandscapeFeature = false;
689 // 2.2 also added some other features that apps can request, but that
690 // have no corresponding permission, so we cannot implement any
691 // back-compatibility heuristic for them. The below are thus unnecessary
692 // (but are retained here for documentary purposes.)
693 //bool specCompassFeature = false;
694 //bool specAccelerometerFeature = false;
695 //bool specProximityFeature = false;
696 //bool specAmbientLightFeature = false;
697 //bool specLiveWallpaperFeature = false;
701 int normalScreen = 1;
703 int xlargeScreen = 1;
705 int requiresSmallestWidthDp = 0;
706 int compatibleWidthLimitDp = 0;
707 int largestWidthLimitDp = 0;
709 String8 activityName;
710 String8 activityLabel;
711 String8 activityIcon;
712 String8 receiverName;
714 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
715 if (code == ResXMLTree::END_TAG) {
718 withinApplication = false;
719 } else if (depth < 3) {
720 if (withinActivity && isMainActivity && isLauncherActivity) {
721 const char *aName = getComponentName(pkg, activityName);
722 printf("launchable-activity:");
724 printf(" name='%s' ", aName);
726 printf(" label='%s' icon='%s'\n",
727 activityLabel.string(),
728 activityIcon.string());
730 if (!hasIntentFilter) {
731 hasOtherActivities |= withinActivity;
732 hasOtherReceivers |= withinReceiver;
733 hasOtherServices |= withinService;
735 withinActivity = false;
736 withinService = false;
737 withinReceiver = false;
738 hasIntentFilter = false;
739 isMainActivity = isLauncherActivity = false;
740 } else if (depth < 4) {
741 if (withinIntentFilter) {
742 if (withinActivity) {
743 hasMainActivity |= actMainActivity;
744 hasOtherActivities |= !actMainActivity;
745 } else if (withinReceiver) {
746 hasWidgetReceivers |= actWidgetReceivers;
747 hasOtherReceivers |= !actWidgetReceivers;
748 } else if (withinService) {
749 hasImeService |= actImeService;
750 hasWallpaperService |= actWallpaperService;
751 hasOtherServices |= (!actImeService && !actWallpaperService);
754 withinIntentFilter = false;
758 if (code != ResXMLTree::START_TAG) {
762 String8 tag(tree.getElementName(&len));
763 //printf("Depth %d, %s\n", depth, tag.string());
765 if (tag != "manifest") {
766 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
769 pkg = getAttribute(tree, NULL, "package", NULL);
770 printf("package: name='%s' ", pkg.string());
771 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
773 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
776 if (versionCode > 0) {
777 printf("versionCode='%d' ", versionCode);
779 printf("versionCode='' ");
781 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
783 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
786 printf("versionName='%s'\n", versionName.string());
787 } else if (depth == 2) {
788 withinApplication = false;
789 if (tag == "application") {
790 withinApplication = true;
793 const size_t NL = locales.size();
794 for (size_t i=0; i<NL; i++) {
795 const char* localeStr = locales[i].string();
796 assets.setLocale(localeStr != NULL ? localeStr : "");
797 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
799 if (localeStr == NULL || strlen(localeStr) == 0) {
801 printf("application-label:'%s'\n", llabel.string());
806 printf("application-label-%s:'%s'\n", localeStr,
812 ResTable_config tmpConfig = config;
813 const size_t ND = densities.size();
814 for (size_t i=0; i<ND; i++) {
815 tmpConfig.density = densities[i];
816 assets.setConfiguration(tmpConfig);
817 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
819 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
822 assets.setConfiguration(config);
824 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
826 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
829 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
831 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
834 printf("application: label='%s' ", label.string());
835 printf("icon='%s'\n", icon.string());
837 printf("testOnly='%d'\n", testOnly);
840 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
842 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
845 if (debuggable != 0) {
846 printf("application-debuggable\n");
848 } else if (tag == "uses-sdk") {
849 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
852 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
854 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
858 if (name == "Donut") targetSdk = 4;
859 printf("sdkVersion:'%s'\n", name.string());
860 } else if (code != -1) {
862 printf("sdkVersion:'%d'\n", code);
864 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
866 printf("maxSdkVersion:'%d'\n", code);
868 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
871 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
873 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
877 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
878 printf("targetSdkVersion:'%s'\n", name.string());
879 } else if (code != -1) {
880 if (targetSdk < code) {
883 printf("targetSdkVersion:'%d'\n", code);
885 } else if (tag == "uses-configuration") {
886 int32_t reqTouchScreen = getIntegerAttribute(tree,
887 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
888 int32_t reqKeyboardType = getIntegerAttribute(tree,
889 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
890 int32_t reqHardKeyboard = getIntegerAttribute(tree,
891 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
892 int32_t reqNavigation = getIntegerAttribute(tree,
893 REQ_NAVIGATION_ATTR, NULL, 0);
894 int32_t reqFiveWayNav = getIntegerAttribute(tree,
895 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
896 printf("uses-configuration:");
897 if (reqTouchScreen != 0) {
898 printf(" reqTouchScreen='%d'", reqTouchScreen);
900 if (reqKeyboardType != 0) {
901 printf(" reqKeyboardType='%d'", reqKeyboardType);
903 if (reqHardKeyboard != 0) {
904 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
906 if (reqNavigation != 0) {
907 printf(" reqNavigation='%d'", reqNavigation);
909 if (reqFiveWayNav != 0) {
910 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
913 } else if (tag == "supports-screens") {
914 smallScreen = getIntegerAttribute(tree,
915 SMALL_SCREEN_ATTR, NULL, 1);
916 normalScreen = getIntegerAttribute(tree,
917 NORMAL_SCREEN_ATTR, NULL, 1);
918 largeScreen = getIntegerAttribute(tree,
919 LARGE_SCREEN_ATTR, NULL, 1);
920 xlargeScreen = getIntegerAttribute(tree,
921 XLARGE_SCREEN_ATTR, NULL, 1);
922 anyDensity = getIntegerAttribute(tree,
923 ANY_DENSITY_ATTR, NULL, 1);
924 requiresSmallestWidthDp = getIntegerAttribute(tree,
925 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
926 compatibleWidthLimitDp = getIntegerAttribute(tree,
927 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
928 largestWidthLimitDp = getIntegerAttribute(tree,
929 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
930 } else if (tag == "uses-feature") {
931 String8 name = getAttribute(tree, NAME_ATTR, &error);
933 if (name != "" && error == "") {
934 int req = getIntegerAttribute(tree,
935 REQUIRED_ATTR, NULL, 1);
937 if (name == "android.hardware.camera") {
938 specCameraFeature = true;
939 } else if (name == "android.hardware.camera.autofocus") {
940 // these have no corresponding permission to check for,
941 // but should imply the foundational camera permission
942 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
943 specCameraAutofocusFeature = true;
944 } else if (req && (name == "android.hardware.camera.flash")) {
945 // these have no corresponding permission to check for,
946 // but should imply the foundational camera permission
947 reqCameraFlashFeature = true;
948 } else if (name == "android.hardware.location") {
949 specLocationFeature = true;
950 } else if (name == "android.hardware.location.network") {
951 specNetworkLocFeature = true;
952 reqNetworkLocFeature = reqNetworkLocFeature || req;
953 } else if (name == "android.hardware.location.gps") {
954 specGpsFeature = true;
955 reqGpsFeature = reqGpsFeature || req;
956 } else if (name == "android.hardware.bluetooth") {
957 specBluetoothFeature = true;
958 } else if (name == "android.hardware.touchscreen") {
959 specTouchscreenFeature = true;
960 } else if (name == "android.hardware.touchscreen.multitouch") {
961 specMultitouchFeature = true;
962 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
963 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
964 } else if (name == "android.hardware.microphone") {
965 specMicrophoneFeature = true;
966 } else if (name == "android.hardware.wifi") {
967 specWiFiFeature = true;
968 } else if (name == "android.hardware.telephony") {
969 specTelephonyFeature = true;
970 } else if (req && (name == "android.hardware.telephony.gsm" ||
971 name == "android.hardware.telephony.cdma")) {
972 // these have no corresponding permission to check for,
973 // but should imply the foundational telephony permission
974 reqTelephonySubFeature = true;
975 } else if (name == "android.hardware.screen.portrait") {
976 specScreenPortraitFeature = true;
977 } else if (name == "android.hardware.screen.landscape") {
978 specScreenLandscapeFeature = true;
980 printf("uses-feature%s:'%s'\n",
981 req ? "" : "-not-required", name.string());
983 int vers = getIntegerAttribute(tree,
984 GL_ES_VERSION_ATTR, &error);
986 printf("uses-gl-es:'0x%x'\n", vers);
989 } else if (tag == "uses-permission") {
990 String8 name = getAttribute(tree, NAME_ATTR, &error);
991 if (name != "" && error == "") {
992 if (name == "android.permission.CAMERA") {
993 hasCameraPermission = true;
994 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
995 hasGpsPermission = true;
996 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
997 hasMockLocPermission = true;
998 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
999 hasCoarseLocPermission = true;
1000 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1001 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1002 hasGeneralLocPermission = true;
1003 } else if (name == "android.permission.BLUETOOTH" ||
1004 name == "android.permission.BLUETOOTH_ADMIN") {
1005 hasBluetoothPermission = true;
1006 } else if (name == "android.permission.RECORD_AUDIO") {
1007 hasRecordAudioPermission = true;
1008 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1009 name == "android.permission.CHANGE_WIFI_STATE" ||
1010 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1011 hasWiFiPermission = true;
1012 } else if (name == "android.permission.CALL_PHONE" ||
1013 name == "android.permission.CALL_PRIVILEGED" ||
1014 name == "android.permission.MODIFY_PHONE_STATE" ||
1015 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1016 name == "android.permission.READ_SMS" ||
1017 name == "android.permission.RECEIVE_SMS" ||
1018 name == "android.permission.RECEIVE_MMS" ||
1019 name == "android.permission.RECEIVE_WAP_PUSH" ||
1020 name == "android.permission.SEND_SMS" ||
1021 name == "android.permission.WRITE_APN_SETTINGS" ||
1022 name == "android.permission.WRITE_SMS") {
1023 hasTelephonyPermission = true;
1024 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1025 hasWriteExternalStoragePermission = true;
1026 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1027 hasReadExternalStoragePermission = true;
1028 } else if (name == "android.permission.READ_PHONE_STATE") {
1029 hasReadPhoneStatePermission = true;
1030 } else if (name == "android.permission.READ_CONTACTS") {
1031 hasReadContactsPermission = true;
1032 } else if (name == "android.permission.WRITE_CONTACTS") {
1033 hasWriteContactsPermission = true;
1034 } else if (name == "android.permission.READ_CALL_LOG") {
1035 hasReadCallLogPermission = true;
1036 } else if (name == "android.permission.WRITE_CALL_LOG") {
1037 hasWriteCallLogPermission = true;
1039 printf("uses-permission:'%s'\n", name.string());
1040 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
1042 printf("optional-permission:'%s'\n", name.string());
1045 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1049 } else if (tag == "uses-package") {
1050 String8 name = getAttribute(tree, NAME_ATTR, &error);
1051 if (name != "" && error == "") {
1052 printf("uses-package:'%s'\n", name.string());
1054 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1058 } else if (tag == "original-package") {
1059 String8 name = getAttribute(tree, NAME_ATTR, &error);
1060 if (name != "" && error == "") {
1061 printf("original-package:'%s'\n", name.string());
1063 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1067 } else if (tag == "supports-gl-texture") {
1068 String8 name = getAttribute(tree, NAME_ATTR, &error);
1069 if (name != "" && error == "") {
1070 printf("supports-gl-texture:'%s'\n", name.string());
1072 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1076 } else if (tag == "compatible-screens") {
1077 printCompatibleScreens(tree);
1079 } else if (tag == "package-verifier") {
1080 String8 name = getAttribute(tree, NAME_ATTR, &error);
1081 if (name != "" && error == "") {
1082 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1083 if (publicKey != "" && error == "") {
1084 printf("package-verifier: name='%s' publicKey='%s'\n",
1085 name.string(), publicKey.string());
1089 } else if (depth == 3 && withinApplication) {
1090 withinActivity = false;
1091 withinReceiver = false;
1092 withinService = false;
1093 hasIntentFilter = false;
1094 if(tag == "activity") {
1095 withinActivity = true;
1096 activityName = getAttribute(tree, NAME_ATTR, &error);
1098 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1102 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1104 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
1108 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1110 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1114 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1115 SCREEN_ORIENTATION_ATTR, &error);
1117 if (orien == 0 || orien == 6 || orien == 8) {
1118 // Requests landscape, sensorLandscape, or reverseLandscape.
1119 reqScreenLandscapeFeature = true;
1120 } else if (orien == 1 || orien == 7 || orien == 9) {
1121 // Requests portrait, sensorPortrait, or reversePortrait.
1122 reqScreenPortraitFeature = true;
1125 } else if (tag == "uses-library") {
1126 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1128 fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
1131 int req = getIntegerAttribute(tree,
1132 REQUIRED_ATTR, NULL, 1);
1133 printf("uses-library%s:'%s'\n",
1134 req ? "" : "-not-required", libraryName.string());
1135 } else if (tag == "receiver") {
1136 withinReceiver = true;
1137 receiverName = getAttribute(tree, NAME_ATTR, &error);
1140 fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
1143 } else if (tag == "service") {
1144 withinService = true;
1145 serviceName = getAttribute(tree, NAME_ATTR, &error);
1148 fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
1152 } else if ((depth == 4) && (tag == "intent-filter")) {
1153 hasIntentFilter = true;
1154 withinIntentFilter = true;
1155 actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
1156 } else if ((depth == 5) && withinIntentFilter){
1158 if (tag == "action") {
1159 action = getAttribute(tree, NAME_ATTR, &error);
1161 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1164 if (withinActivity) {
1165 if (action == "android.intent.action.MAIN") {
1166 isMainActivity = true;
1167 actMainActivity = true;
1169 } else if (withinReceiver) {
1170 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1171 actWidgetReceivers = true;
1173 } else if (withinService) {
1174 if (action == "android.view.InputMethod") {
1175 actImeService = true;
1176 } else if (action == "android.service.wallpaper.WallpaperService") {
1177 actWallpaperService = true;
1180 if (action == "android.intent.action.SEARCH") {
1181 isSearchable = true;
1185 if (tag == "category") {
1186 String8 category = getAttribute(tree, NAME_ATTR, &error);
1188 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1191 if (withinActivity) {
1192 if (category == "android.intent.category.LAUNCHER") {
1193 isLauncherActivity = true;
1200 // Pre-1.6 implicitly granted permission compatibility logic
1201 if (targetSdk < 4) {
1202 if (!hasWriteExternalStoragePermission) {
1203 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
1204 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1205 "'targetSdkVersion < 4'\n");
1206 hasWriteExternalStoragePermission = true;
1208 if (!hasReadPhoneStatePermission) {
1209 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
1210 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1211 "'targetSdkVersion < 4'\n");
1215 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1216 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1217 // do this (regardless of target API version) because we can't have
1218 // an app with write permission but not read permission.
1219 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1220 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
1221 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1222 "'requested WRITE_EXTERNAL_STORAGE'\n");
1225 // Pre-JellyBean call log permission compatibility.
1226 if (targetSdk < 16) {
1227 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1228 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
1229 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1230 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
1232 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1233 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
1234 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1235 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
1239 /* The following blocks handle printing "inferred" uses-features, based
1240 * on whether related features or permissions are used by the app.
1241 * Note that the various spec*Feature variables denote whether the
1242 * relevant tag was *present* in the AndroidManfest, not that it was
1243 * present and set to true.
1245 // Camera-related back-compatibility logic
1246 if (!specCameraFeature) {
1247 if (reqCameraFlashFeature) {
1248 // if app requested a sub-feature (autofocus or flash) and didn't
1249 // request the base camera feature, we infer that it meant to
1250 printf("uses-feature:'android.hardware.camera'\n");
1251 printf("uses-implied-feature:'android.hardware.camera'," \
1252 "'requested android.hardware.camera.flash feature'\n");
1253 } else if (reqCameraAutofocusFeature) {
1254 // if app requested a sub-feature (autofocus or flash) and didn't
1255 // request the base camera feature, we infer that it meant to
1256 printf("uses-feature:'android.hardware.camera'\n");
1257 printf("uses-implied-feature:'android.hardware.camera'," \
1258 "'requested android.hardware.camera.autofocus feature'\n");
1259 } else if (hasCameraPermission) {
1260 // if app wants to use camera but didn't request the feature, we infer
1261 // that it meant to, and further that it wants autofocus
1262 // (which was the 1.0 - 1.5 behavior)
1263 printf("uses-feature:'android.hardware.camera'\n");
1264 if (!specCameraAutofocusFeature) {
1265 printf("uses-feature:'android.hardware.camera.autofocus'\n");
1266 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1267 "'requested android.permission.CAMERA permission'\n");
1272 // Location-related back-compatibility logic
1273 if (!specLocationFeature &&
1274 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1275 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1276 // if app either takes a location-related permission or requests one of the
1277 // sub-features, we infer that it also meant to request the base location feature
1278 printf("uses-feature:'android.hardware.location'\n");
1279 printf("uses-implied-feature:'android.hardware.location'," \
1280 "'requested a location access permission'\n");
1282 if (!specGpsFeature && hasGpsPermission) {
1283 // if app takes GPS (FINE location) perm but does not request the GPS
1284 // feature, we infer that it meant to
1285 printf("uses-feature:'android.hardware.location.gps'\n");
1286 printf("uses-implied-feature:'android.hardware.location.gps'," \
1287 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
1289 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1290 // if app takes Network location (COARSE location) perm but does not request the
1291 // network location feature, we infer that it meant to
1292 printf("uses-feature:'android.hardware.location.network'\n");
1293 printf("uses-implied-feature:'android.hardware.location.network'," \
1294 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
1297 // Bluetooth-related compatibility logic
1298 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
1299 // if app takes a Bluetooth permission but does not request the Bluetooth
1300 // feature, we infer that it meant to
1301 printf("uses-feature:'android.hardware.bluetooth'\n");
1302 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1303 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1304 "permission and targetSdkVersion > 4'\n");
1307 // Microphone-related compatibility logic
1308 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1309 // if app takes the record-audio permission but does not request the microphone
1310 // feature, we infer that it meant to
1311 printf("uses-feature:'android.hardware.microphone'\n");
1312 printf("uses-implied-feature:'android.hardware.microphone'," \
1313 "'requested android.permission.RECORD_AUDIO permission'\n");
1316 // WiFi-related compatibility logic
1317 if (!specWiFiFeature && hasWiFiPermission) {
1318 // if app takes one of the WiFi permissions but does not request the WiFi
1319 // feature, we infer that it meant to
1320 printf("uses-feature:'android.hardware.wifi'\n");
1321 printf("uses-implied-feature:'android.hardware.wifi'," \
1322 "'requested android.permission.ACCESS_WIFI_STATE, " \
1323 "android.permission.CHANGE_WIFI_STATE, or " \
1324 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
1327 // Telephony-related compatibility logic
1328 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1329 // if app takes one of the telephony permissions or requests a sub-feature but
1330 // does not request the base telephony feature, we infer that it meant to
1331 printf("uses-feature:'android.hardware.telephony'\n");
1332 printf("uses-implied-feature:'android.hardware.telephony'," \
1333 "'requested a telephony-related permission or feature'\n");
1336 // Touchscreen-related back-compatibility logic
1337 if (!specTouchscreenFeature) { // not a typo!
1338 // all apps are presumed to require a touchscreen, unless they explicitly say
1339 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1340 // Note that specTouchscreenFeature is true if the tag is present, regardless
1341 // of whether its value is true or false, so this is safe
1342 printf("uses-feature:'android.hardware.touchscreen'\n");
1343 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1344 "'assumed you require a touch screen unless explicitly made optional'\n");
1346 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1347 // if app takes one of the telephony permissions or requests a sub-feature but
1348 // does not request the base telephony feature, we infer that it meant to
1349 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1350 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1351 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
1354 // Landscape/portrait-related compatibility logic
1355 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1356 // If the app has specified any activities in its manifest
1357 // that request a specific orientation, then assume that
1358 // orientation is required.
1359 if (reqScreenLandscapeFeature) {
1360 printf("uses-feature:'android.hardware.screen.landscape'\n");
1361 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1362 "'one or more activities have specified a landscape orientation'\n");
1364 if (reqScreenPortraitFeature) {
1365 printf("uses-feature:'android.hardware.screen.portrait'\n");
1366 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1367 "'one or more activities have specified a portrait orientation'\n");
1371 if (hasMainActivity) {
1374 if (hasWidgetReceivers) {
1375 printf("app-widget\n");
1377 if (hasImeService) {
1380 if (hasWallpaperService) {
1381 printf("wallpaper\n");
1383 if (hasOtherActivities) {
1384 printf("other-activities\n");
1389 if (hasOtherReceivers) {
1390 printf("other-receivers\n");
1392 if (hasOtherServices) {
1393 printf("other-services\n");
1396 // For modern apps, if screen size buckets haven't been specified
1397 // but the new width ranges have, then infer the buckets from them.
1398 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1399 && requiresSmallestWidthDp > 0) {
1400 int compatWidth = compatibleWidthLimitDp;
1401 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1402 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1407 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1412 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1417 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1424 // Determine default values for any unspecified screen sizes,
1425 // based on the target SDK of the package. As of 4 (donut)
1426 // the screen size support was introduced, so all default to
1428 if (smallScreen > 0) {
1429 smallScreen = targetSdk >= 4 ? -1 : 0;
1431 if (normalScreen > 0) {
1434 if (largeScreen > 0) {
1435 largeScreen = targetSdk >= 4 ? -1 : 0;
1437 if (xlargeScreen > 0) {
1438 // Introduced in Gingerbread.
1439 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1441 if (anyDensity > 0) {
1442 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1443 || compatibleWidthLimitDp > 0) ? -1 : 0;
1445 printf("supports-screens:");
1446 if (smallScreen != 0) printf(" 'small'");
1447 if (normalScreen != 0) printf(" 'normal'");
1448 if (largeScreen != 0) printf(" 'large'");
1449 if (xlargeScreen != 0) printf(" 'xlarge'");
1451 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1452 if (requiresSmallestWidthDp > 0) {
1453 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1455 if (compatibleWidthLimitDp > 0) {
1456 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1458 if (largestWidthLimitDp > 0) {
1459 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1463 const size_t NL = locales.size();
1464 for (size_t i=0; i<NL; i++) {
1465 const char* localeStr = locales[i].string();
1466 if (localeStr == NULL || strlen(localeStr) == 0) {
1467 localeStr = "--_--";
1469 printf(" '%s'", localeStr);
1473 printf("densities:");
1474 const size_t ND = densities.size();
1475 for (size_t i=0; i<ND; i++) {
1476 printf(" '%d'", densities[i]);
1480 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1482 if (dir->getFileCount() > 0) {
1483 printf("native-code:");
1484 for (size_t i=0; i<dir->getFileCount(); i++) {
1485 printf(" '%s'", dir->getFileName(i).string());
1491 } else if (strcmp("badger", option) == 0) {
1492 printf("%s", CONSOLE_DATA);
1493 } else if (strcmp("configurations", option) == 0) {
1494 Vector<ResTable_config> configs;
1495 res.getConfigurations(&configs);
1496 const size_t N = configs.size();
1497 for (size_t i=0; i<N; i++) {
1498 printf("%s\n", configs[i].toString().string());
1501 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1512 return (result != NO_ERROR);
1517 * Handle the "add" command, which wants to add files to a new or
1518 * pre-existing archive.
1520 int doAdd(Bundle* bundle)
1522 ZipFile* zip = NULL;
1523 status_t result = UNKNOWN_ERROR;
1524 const char* zipFileName;
1526 if (bundle->getUpdate()) {
1527 /* avoid confusion */
1528 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1532 if (bundle->getFileSpecCount() < 1) {
1533 fprintf(stderr, "ERROR: must specify zip file name\n");
1536 zipFileName = bundle->getFileSpecEntry(0);
1538 if (bundle->getFileSpecCount() < 2) {
1539 fprintf(stderr, "NOTE: nothing to do\n");
1543 zip = openReadWrite(zipFileName, true);
1545 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1549 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1550 const char* fileName = bundle->getFileSpecEntry(i);
1552 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1553 printf(" '%s'... (from gzip)\n", fileName);
1554 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1556 if (bundle->getJunkPath()) {
1557 String8 storageName = String8(fileName).getPathLeaf();
1558 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1559 result = zip->add(fileName, storageName.string(),
1560 bundle->getCompressionMethod(), NULL);
1562 printf(" '%s'...\n", fileName);
1563 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1566 if (result != NO_ERROR) {
1567 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1568 if (result == NAME_NOT_FOUND)
1569 fprintf(stderr, ": file not found\n");
1570 else if (result == ALREADY_EXISTS)
1571 fprintf(stderr, ": already exists in archive\n");
1573 fprintf(stderr, "\n");
1582 return (result != NO_ERROR);
1587 * Delete files from an existing archive.
1589 int doRemove(Bundle* bundle)
1591 ZipFile* zip = NULL;
1592 status_t result = UNKNOWN_ERROR;
1593 const char* zipFileName;
1595 if (bundle->getFileSpecCount() < 1) {
1596 fprintf(stderr, "ERROR: must specify zip file name\n");
1599 zipFileName = bundle->getFileSpecEntry(0);
1601 if (bundle->getFileSpecCount() < 2) {
1602 fprintf(stderr, "NOTE: nothing to do\n");
1606 zip = openReadWrite(zipFileName, false);
1608 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1613 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1614 const char* fileName = bundle->getFileSpecEntry(i);
1617 entry = zip->getEntryByName(fileName);
1618 if (entry == NULL) {
1619 printf(" '%s' NOT FOUND\n", fileName);
1623 result = zip->remove(entry);
1625 if (result != NO_ERROR) {
1626 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1627 bundle->getFileSpecEntry(i), zipFileName);
1632 /* update the archive */
1637 return (result != NO_ERROR);
1642 * Package up an asset directory and associated application files.
1644 int doPackage(Bundle* bundle)
1646 const char* outputAPKFile;
1649 sp<AaptAssets> assets;
1652 String8 dependencyFile;
1654 // -c zz_ZZ means do pseudolocalization
1655 ResourceFilter filter;
1656 err = filter.parse(bundle->getConfigurations());
1657 if (err != NO_ERROR) {
1660 if (filter.containsPseudo()) {
1661 bundle->setPseudolocalize(true);
1664 N = bundle->getFileSpecCount();
1665 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1666 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1667 fprintf(stderr, "ERROR: no input files\n");
1671 outputAPKFile = bundle->getOutputAPKFile();
1673 // Make sure the filenames provided exist and are of the appropriate type.
1674 if (outputAPKFile) {
1676 type = getFileType(outputAPKFile);
1677 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1679 "ERROR: output file '%s' exists but is not regular file\n",
1686 assets = new AaptAssets();
1688 // Set up the resource gathering in assets if we're going to generate
1689 // dependency files. Every time we encounter a resource while slurping
1690 // the tree, we'll add it to these stores so we have full resource paths
1691 // to write to a dependency file.
1692 if (bundle->getGenDependencies()) {
1693 sp<FilePathStore> resPathStore = new FilePathStore;
1694 assets->setFullResPaths(resPathStore);
1695 sp<FilePathStore> assetPathStore = new FilePathStore;
1696 assets->setFullAssetPaths(assetPathStore);
1699 err = assets->slurpFromArgs(bundle);
1704 if (bundle->getVerbose()) {
1705 assets->print(String8());
1708 // If they asked for any fileAs that need to be compiled, do so.
1709 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1710 err = buildResources(bundle, assets);
1716 // At this point we've read everything and processed everything. From here
1717 // on out it's just writing output files.
1718 if (SourcePos::hasErrors()) {
1722 // Update symbols with information about which ones are needed as Java symbols.
1723 assets->applyJavaSymbols();
1724 if (SourcePos::hasErrors()) {
1728 // If we've been asked to generate a dependency file, do that here
1729 if (bundle->getGenDependencies()) {
1730 // If this is the packaging step, generate the dependency file next to
1731 // the output apk (e.g. bin/resources.ap_.d)
1732 if (outputAPKFile) {
1733 dependencyFile = String8(outputAPKFile);
1734 // Add the .d extension to the dependency file.
1735 dependencyFile.append(".d");
1737 // Else if this is the R.java dependency generation step,
1738 // generate the dependency file in the R.java package subdirectory
1739 // e.g. gen/com/foo/app/R.java.d
1740 dependencyFile = String8(bundle->getRClassDir());
1741 dependencyFile.appendPath("R.java.d");
1743 // Make sure we have a clean dependency file to start with
1744 fp = fopen(dependencyFile, "w");
1748 // Write out R.java constants
1749 if (!assets->havePrivateSymbols()) {
1750 if (bundle->getCustomPackage() == NULL) {
1751 // Write the R.java file into the appropriate class directory
1752 // e.g. gen/com/foo/app/R.java
1753 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1755 const String8 customPkg(bundle->getCustomPackage());
1756 err = writeResourceSymbols(bundle, assets, customPkg, true);
1761 // If we have library files, we're going to write our R.java file into
1762 // the appropriate class directory for those libraries as well.
1763 // e.g. gen/com/foo/app/lib/R.java
1764 if (bundle->getExtraPackages() != NULL) {
1766 String8 libs(bundle->getExtraPackages());
1767 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
1768 while (packageString != NULL) {
1769 // Write the R.java file out with the correct package name
1770 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
1774 packageString = strtok(NULL, ":");
1776 libs.unlockBuffer();
1779 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1783 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1789 // Write out the ProGuard file
1790 err = writeProguardFile(bundle, assets);
1796 if (outputAPKFile) {
1797 err = writeAPK(bundle, assets, String8(outputAPKFile));
1798 if (err != NO_ERROR) {
1799 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1804 // If we've been asked to generate a dependency file, we need to finish up here.
1805 // the writeResourceSymbols and writeAPK functions have already written the target
1806 // half of the dependency file, now we need to write the prerequisites. (files that
1807 // the R.java file or .ap_ file depend on)
1808 if (bundle->getGenDependencies()) {
1809 // Now that writeResourceSymbols or writeAPK has taken care of writing
1810 // the targets to our dependency file, we'll write the prereqs
1811 fp = fopen(dependencyFile, "a+");
1813 bool includeRaw = (outputAPKFile != NULL);
1814 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
1815 // Also manually add the AndroidManifeset since it's not under res/ or assets/
1816 // and therefore was not added to our pathstores during slurping
1817 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1823 if (SourcePos::hasErrors()) {
1824 SourcePos::printErrors(stderr);
1832 * -S flag points to a source directory containing drawable* folders
1833 * -C flag points to destination directory. The folder structure in the
1834 * source directory will be mirrored to the destination (cache) directory
1837 * Destination directory will be updated to match the PNG files in
1838 * the source directory.
1840 int doCrunch(Bundle* bundle)
1842 fprintf(stdout, "Crunching PNG Files in ");
1843 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1844 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1846 updatePreProcessedCache(bundle);
1852 * Do PNG Crunching on a single flag
1853 * -i points to a single png file
1854 * -o points to a single png output file
1856 int doSingleCrunch(Bundle* bundle)
1858 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
1859 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
1861 String8 input(bundle->getSingleCrunchInputFile());
1862 String8 output(bundle->getSingleCrunchOutputFile());
1864 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
1865 // we can't return the status_t as it gets truncate to the lower 8 bits.
1872 char CONSOLE_DATA[2925] = {
1873 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1874 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1875 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1876 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1877 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1878 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1879 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1880 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1881 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1882 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1883 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1884 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1885 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1886 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1887 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1888 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1889 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1890 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1891 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1892 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1893 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1894 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1895 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1896 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1897 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1898 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1899 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1900 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1901 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1902 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1903 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1904 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1905 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1906 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1907 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1908 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1909 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1910 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1911 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1912 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1913 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1914 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
1915 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1916 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1917 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
1918 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
1919 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1920 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
1921 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
1922 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1923 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
1924 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
1925 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
1926 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1927 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1928 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
1929 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
1930 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
1931 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1932 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
1933 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
1934 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
1935 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
1936 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1937 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1938 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
1939 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
1940 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
1941 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
1942 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
1943 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
1944 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
1945 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1946 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
1947 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1948 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
1949 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1950 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
1951 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
1952 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1953 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
1954 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
1955 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
1956 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1957 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1958 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
1959 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1960 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
1961 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
1962 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1963 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1964 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
1965 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
1966 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1967 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1968 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1969 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1970 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1971 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
1972 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
1973 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1974 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1975 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1976 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
1977 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1978 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1979 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
1980 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1981 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1982 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
1983 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
1984 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1985 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1986 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1987 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1988 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1989 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
1990 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
1991 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1992 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1993 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
1994 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
1995 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1996 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1997 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
1998 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
1999 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2000 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2001 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2002 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2003 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2004 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2005 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2006 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2007 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2008 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2009 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2010 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2011 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2012 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2013 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2014 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2015 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2016 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2017 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2018 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2019 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2020 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2021 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2022 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2023 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2024 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2025 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2026 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2027 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2028 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2029 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2030 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2031 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2032 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2033 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2034 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2035 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10