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());
597 } else if (strcmp("badging", option
) == 0) {
598 Vector
<String8
> locales
;
599 res
.getLocales(&locales
);
601 Vector
<ResTable_config
> configs
;
602 res
.getConfigurations(&configs
);
603 SortedVector
<int> densities
;
604 const size_t NC
= configs
.size();
605 for (size_t i
=0; i
<NC
; i
++) {
606 int dens
= configs
[i
].density
;
607 if (dens
== 0) dens
= 160;
612 ResXMLTree::event_code_t code
;
615 bool withinActivity
= false;
616 bool isMainActivity
= false;
617 bool isLauncherActivity
= false;
618 bool isSearchable
= false;
619 bool withinApplication
= false;
620 bool withinReceiver
= false;
621 bool withinService
= false;
622 bool withinIntentFilter
= false;
623 bool hasMainActivity
= false;
624 bool hasOtherActivities
= false;
625 bool hasOtherReceivers
= false;
626 bool hasOtherServices
= false;
627 bool hasWallpaperService
= false;
628 bool hasImeService
= false;
629 bool hasWidgetReceivers
= false;
630 bool hasIntentFilter
= false;
631 bool actMainActivity
= false;
632 bool actWidgetReceivers
= false;
633 bool actImeService
= false;
634 bool actWallpaperService
= false;
636 // These two implement the implicit permissions that are granted
637 // to pre-1.6 applications.
638 bool hasWriteExternalStoragePermission
= false;
639 bool hasReadPhoneStatePermission
= false;
641 // If an app requests write storage, they will also get read storage.
642 bool hasReadExternalStoragePermission
= false;
644 // Implement transition to read and write call log.
645 bool hasReadContactsPermission
= false;
646 bool hasWriteContactsPermission
= false;
647 bool hasReadCallLogPermission
= false;
648 bool hasWriteCallLogPermission
= false;
650 // This next group of variables is used to implement a group of
651 // backward-compatibility heuristics necessitated by the addition of
652 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
653 // heuristic is "if an app requests a permission but doesn't explicitly
654 // request the corresponding <uses-feature>, presume it's there anyway".
655 bool specCameraFeature
= false; // camera-related
656 bool specCameraAutofocusFeature
= false;
657 bool reqCameraAutofocusFeature
= false;
658 bool reqCameraFlashFeature
= false;
659 bool hasCameraPermission
= false;
660 bool specLocationFeature
= false; // location-related
661 bool specNetworkLocFeature
= false;
662 bool reqNetworkLocFeature
= false;
663 bool specGpsFeature
= false;
664 bool reqGpsFeature
= false;
665 bool hasMockLocPermission
= false;
666 bool hasCoarseLocPermission
= false;
667 bool hasGpsPermission
= false;
668 bool hasGeneralLocPermission
= false;
669 bool specBluetoothFeature
= false; // Bluetooth API-related
670 bool hasBluetoothPermission
= false;
671 bool specMicrophoneFeature
= false; // microphone-related
672 bool hasRecordAudioPermission
= false;
673 bool specWiFiFeature
= false;
674 bool hasWiFiPermission
= false;
675 bool specTelephonyFeature
= false; // telephony-related
676 bool reqTelephonySubFeature
= false;
677 bool hasTelephonyPermission
= false;
678 bool specTouchscreenFeature
= false; // touchscreen-related
679 bool specMultitouchFeature
= false;
680 bool reqDistinctMultitouchFeature
= false;
681 bool specScreenPortraitFeature
= false;
682 bool specScreenLandscapeFeature
= false;
683 bool reqScreenPortraitFeature
= false;
684 bool reqScreenLandscapeFeature
= false;
685 // 2.2 also added some other features that apps can request, but that
686 // have no corresponding permission, so we cannot implement any
687 // back-compatibility heuristic for them. The below are thus unnecessary
688 // (but are retained here for documentary purposes.)
689 //bool specCompassFeature = false;
690 //bool specAccelerometerFeature = false;
691 //bool specProximityFeature = false;
692 //bool specAmbientLightFeature = false;
693 //bool specLiveWallpaperFeature = false;
697 int normalScreen
= 1;
699 int xlargeScreen
= 1;
701 int requiresSmallestWidthDp
= 0;
702 int compatibleWidthLimitDp
= 0;
703 int largestWidthLimitDp
= 0;
705 String8 activityName
;
706 String8 activityLabel
;
707 String8 activityIcon
;
708 String8 receiverName
;
710 while ((code
=tree
.next()) != ResXMLTree::END_DOCUMENT
&& code
!= ResXMLTree::BAD_DOCUMENT
) {
711 if (code
== ResXMLTree::END_TAG
) {
714 withinApplication
= false;
715 } else if (depth
< 3) {
716 if (withinActivity
&& isMainActivity
&& isLauncherActivity
) {
717 const char *aName
= getComponentName(pkg
, activityName
);
718 printf("launchable-activity:");
720 printf(" name='%s' ", aName
);
722 printf(" label='%s' icon='%s'\n",
723 activityLabel
.string(),
724 activityIcon
.string());
726 if (!hasIntentFilter
) {
727 hasOtherActivities
|= withinActivity
;
728 hasOtherReceivers
|= withinReceiver
;
729 hasOtherServices
|= withinService
;
731 withinActivity
= false;
732 withinService
= false;
733 withinReceiver
= false;
734 hasIntentFilter
= false;
735 isMainActivity
= isLauncherActivity
= false;
736 } else if (depth
< 4) {
737 if (withinIntentFilter
) {
738 if (withinActivity
) {
739 hasMainActivity
|= actMainActivity
;
740 hasOtherActivities
|= !actMainActivity
;
741 } else if (withinReceiver
) {
742 hasWidgetReceivers
|= actWidgetReceivers
;
743 hasOtherReceivers
|= !actWidgetReceivers
;
744 } else if (withinService
) {
745 hasImeService
|= actImeService
;
746 hasWallpaperService
|= actWallpaperService
;
747 hasOtherServices
|= (!actImeService
&& !actWallpaperService
);
750 withinIntentFilter
= false;
754 if (code
!= ResXMLTree::START_TAG
) {
758 String8
tag(tree
.getElementName(&len
));
759 //printf("Depth %d, %s\n", depth, tag.string());
761 if (tag
!= "manifest") {
762 fprintf(stderr
, "ERROR: manifest does not start with <manifest> tag\n");
765 pkg
= getAttribute(tree
, NULL
, "package", NULL
);
766 printf("package: name='%s' ", pkg
.string());
767 int32_t versionCode
= getIntegerAttribute(tree
, VERSION_CODE_ATTR
, &error
);
769 fprintf(stderr
, "ERROR getting 'android:versionCode' attribute: %s\n", error
.string());
772 if (versionCode
> 0) {
773 printf("versionCode='%d' ", versionCode
);
775 printf("versionCode='' ");
777 String8 versionName
= getResolvedAttribute(&res
, tree
, VERSION_NAME_ATTR
, &error
);
779 fprintf(stderr
, "ERROR getting 'android:versionName' attribute: %s\n", error
.string());
782 printf("versionName='%s'\n", versionName
.string());
783 } else if (depth
== 2) {
784 withinApplication
= false;
785 if (tag
== "application") {
786 withinApplication
= true;
789 const size_t NL
= locales
.size();
790 for (size_t i
=0; i
<NL
; i
++) {
791 const char* localeStr
= locales
[i
].string();
792 assets
.setLocale(localeStr
!= NULL
? localeStr
: "");
793 String8 llabel
= getResolvedAttribute(&res
, tree
, LABEL_ATTR
, &error
);
795 if (localeStr
== NULL
|| strlen(localeStr
) == 0) {
797 printf("application-label:'%s'\n", llabel
.string());
802 printf("application-label-%s:'%s'\n", localeStr
,
808 ResTable_config tmpConfig
= config
;
809 const size_t ND
= densities
.size();
810 for (size_t i
=0; i
<ND
; i
++) {
811 tmpConfig
.density
= densities
[i
];
812 assets
.setConfiguration(tmpConfig
);
813 String8 icon
= getResolvedAttribute(&res
, tree
, ICON_ATTR
, &error
);
815 printf("application-icon-%d:'%s'\n", densities
[i
], icon
.string());
818 assets
.setConfiguration(config
);
820 String8 icon
= getResolvedAttribute(&res
, tree
, ICON_ATTR
, &error
);
822 fprintf(stderr
, "ERROR getting 'android:icon' attribute: %s\n", error
.string());
825 int32_t testOnly
= getIntegerAttribute(tree
, TEST_ONLY_ATTR
, &error
, 0);
827 fprintf(stderr
, "ERROR getting 'android:testOnly' attribute: %s\n", error
.string());
830 printf("application: label='%s' ", label
.string());
831 printf("icon='%s'\n", icon
.string());
833 printf("testOnly='%d'\n", testOnly
);
836 int32_t debuggable
= getResolvedIntegerAttribute(&res
, tree
, DEBUGGABLE_ATTR
, &error
, 0);
838 fprintf(stderr
, "ERROR getting 'android:debuggable' attribute: %s\n", error
.string());
841 if (debuggable
!= 0) {
842 printf("application-debuggable\n");
844 } else if (tag
== "uses-sdk") {
845 int32_t code
= getIntegerAttribute(tree
, MIN_SDK_VERSION_ATTR
, &error
);
848 String8 name
= getResolvedAttribute(&res
, tree
, MIN_SDK_VERSION_ATTR
, &error
);
850 fprintf(stderr
, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
854 if (name
== "Donut") targetSdk
= 4;
855 printf("sdkVersion:'%s'\n", name
.string());
856 } else if (code
!= -1) {
858 printf("sdkVersion:'%d'\n", code
);
860 code
= getIntegerAttribute(tree
, MAX_SDK_VERSION_ATTR
, NULL
, -1);
862 printf("maxSdkVersion:'%d'\n", code
);
864 code
= getIntegerAttribute(tree
, TARGET_SDK_VERSION_ATTR
, &error
);
867 String8 name
= getResolvedAttribute(&res
, tree
, TARGET_SDK_VERSION_ATTR
, &error
);
869 fprintf(stderr
, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
873 if (name
== "Donut" && targetSdk
< 4) targetSdk
= 4;
874 printf("targetSdkVersion:'%s'\n", name
.string());
875 } else if (code
!= -1) {
876 if (targetSdk
< code
) {
879 printf("targetSdkVersion:'%d'\n", code
);
881 } else if (tag
== "uses-configuration") {
882 int32_t reqTouchScreen
= getIntegerAttribute(tree
,
883 REQ_TOUCH_SCREEN_ATTR
, NULL
, 0);
884 int32_t reqKeyboardType
= getIntegerAttribute(tree
,
885 REQ_KEYBOARD_TYPE_ATTR
, NULL
, 0);
886 int32_t reqHardKeyboard
= getIntegerAttribute(tree
,
887 REQ_HARD_KEYBOARD_ATTR
, NULL
, 0);
888 int32_t reqNavigation
= getIntegerAttribute(tree
,
889 REQ_NAVIGATION_ATTR
, NULL
, 0);
890 int32_t reqFiveWayNav
= getIntegerAttribute(tree
,
891 REQ_FIVE_WAY_NAV_ATTR
, NULL
, 0);
892 printf("uses-configuration:");
893 if (reqTouchScreen
!= 0) {
894 printf(" reqTouchScreen='%d'", reqTouchScreen
);
896 if (reqKeyboardType
!= 0) {
897 printf(" reqKeyboardType='%d'", reqKeyboardType
);
899 if (reqHardKeyboard
!= 0) {
900 printf(" reqHardKeyboard='%d'", reqHardKeyboard
);
902 if (reqNavigation
!= 0) {
903 printf(" reqNavigation='%d'", reqNavigation
);
905 if (reqFiveWayNav
!= 0) {
906 printf(" reqFiveWayNav='%d'", reqFiveWayNav
);
909 } else if (tag
== "supports-screens") {
910 smallScreen
= getIntegerAttribute(tree
,
911 SMALL_SCREEN_ATTR
, NULL
, 1);
912 normalScreen
= getIntegerAttribute(tree
,
913 NORMAL_SCREEN_ATTR
, NULL
, 1);
914 largeScreen
= getIntegerAttribute(tree
,
915 LARGE_SCREEN_ATTR
, NULL
, 1);
916 xlargeScreen
= getIntegerAttribute(tree
,
917 XLARGE_SCREEN_ATTR
, NULL
, 1);
918 anyDensity
= getIntegerAttribute(tree
,
919 ANY_DENSITY_ATTR
, NULL
, 1);
920 requiresSmallestWidthDp
= getIntegerAttribute(tree
,
921 REQUIRES_SMALLEST_WIDTH_DP_ATTR
, NULL
, 0);
922 compatibleWidthLimitDp
= getIntegerAttribute(tree
,
923 COMPATIBLE_WIDTH_LIMIT_DP_ATTR
, NULL
, 0);
924 largestWidthLimitDp
= getIntegerAttribute(tree
,
925 LARGEST_WIDTH_LIMIT_DP_ATTR
, NULL
, 0);
926 } else if (tag
== "uses-feature") {
927 String8 name
= getAttribute(tree
, NAME_ATTR
, &error
);
929 if (name
!= "" && error
== "") {
930 int req
= getIntegerAttribute(tree
,
931 REQUIRED_ATTR
, NULL
, 1);
933 if (name
== "android.hardware.camera") {
934 specCameraFeature
= true;
935 } else if (name
== "android.hardware.camera.autofocus") {
936 // these have no corresponding permission to check for,
937 // but should imply the foundational camera permission
938 reqCameraAutofocusFeature
= reqCameraAutofocusFeature
|| req
;
939 specCameraAutofocusFeature
= true;
940 } else if (req
&& (name
== "android.hardware.camera.flash")) {
941 // these have no corresponding permission to check for,
942 // but should imply the foundational camera permission
943 reqCameraFlashFeature
= true;
944 } else if (name
== "android.hardware.location") {
945 specLocationFeature
= true;
946 } else if (name
== "android.hardware.location.network") {
947 specNetworkLocFeature
= true;
948 reqNetworkLocFeature
= reqNetworkLocFeature
|| req
;
949 } else if (name
== "android.hardware.location.gps") {
950 specGpsFeature
= true;
951 reqGpsFeature
= reqGpsFeature
|| req
;
952 } else if (name
== "android.hardware.bluetooth") {
953 specBluetoothFeature
= true;
954 } else if (name
== "android.hardware.touchscreen") {
955 specTouchscreenFeature
= true;
956 } else if (name
== "android.hardware.touchscreen.multitouch") {
957 specMultitouchFeature
= true;
958 } else if (name
== "android.hardware.touchscreen.multitouch.distinct") {
959 reqDistinctMultitouchFeature
= reqDistinctMultitouchFeature
|| req
;
960 } else if (name
== "android.hardware.microphone") {
961 specMicrophoneFeature
= true;
962 } else if (name
== "android.hardware.wifi") {
963 specWiFiFeature
= true;
964 } else if (name
== "android.hardware.telephony") {
965 specTelephonyFeature
= true;
966 } else if (req
&& (name
== "android.hardware.telephony.gsm" ||
967 name
== "android.hardware.telephony.cdma")) {
968 // these have no corresponding permission to check for,
969 // but should imply the foundational telephony permission
970 reqTelephonySubFeature
= true;
971 } else if (name
== "android.hardware.screen.portrait") {
972 specScreenPortraitFeature
= true;
973 } else if (name
== "android.hardware.screen.landscape") {
974 specScreenLandscapeFeature
= true;
976 printf("uses-feature%s:'%s'\n",
977 req
? "" : "-not-required", name
.string());
979 int vers
= getIntegerAttribute(tree
,
980 GL_ES_VERSION_ATTR
, &error
);
982 printf("uses-gl-es:'0x%x'\n", vers
);
985 } else if (tag
== "uses-permission") {
986 String8 name
= getAttribute(tree
, NAME_ATTR
, &error
);
987 if (name
!= "" && error
== "") {
988 if (name
== "android.permission.CAMERA") {
989 hasCameraPermission
= true;
990 } else if (name
== "android.permission.ACCESS_FINE_LOCATION") {
991 hasGpsPermission
= true;
992 } else if (name
== "android.permission.ACCESS_MOCK_LOCATION") {
993 hasMockLocPermission
= true;
994 } else if (name
== "android.permission.ACCESS_COARSE_LOCATION") {
995 hasCoarseLocPermission
= true;
996 } else if (name
== "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
997 name
== "android.permission.INSTALL_LOCATION_PROVIDER") {
998 hasGeneralLocPermission
= true;
999 } else if (name
== "android.permission.BLUETOOTH" ||
1000 name
== "android.permission.BLUETOOTH_ADMIN") {
1001 hasBluetoothPermission
= true;
1002 } else if (name
== "android.permission.RECORD_AUDIO") {
1003 hasRecordAudioPermission
= true;
1004 } else if (name
== "android.permission.ACCESS_WIFI_STATE" ||
1005 name
== "android.permission.CHANGE_WIFI_STATE" ||
1006 name
== "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1007 hasWiFiPermission
= true;
1008 } else if (name
== "android.permission.CALL_PHONE" ||
1009 name
== "android.permission.CALL_PRIVILEGED" ||
1010 name
== "android.permission.MODIFY_PHONE_STATE" ||
1011 name
== "android.permission.PROCESS_OUTGOING_CALLS" ||
1012 name
== "android.permission.READ_SMS" ||
1013 name
== "android.permission.RECEIVE_SMS" ||
1014 name
== "android.permission.RECEIVE_MMS" ||
1015 name
== "android.permission.RECEIVE_WAP_PUSH" ||
1016 name
== "android.permission.SEND_SMS" ||
1017 name
== "android.permission.WRITE_APN_SETTINGS" ||
1018 name
== "android.permission.WRITE_SMS") {
1019 hasTelephonyPermission
= true;
1020 } else if (name
== "android.permission.WRITE_EXTERNAL_STORAGE") {
1021 hasWriteExternalStoragePermission
= true;
1022 } else if (name
== "android.permission.READ_EXTERNAL_STORAGE") {
1023 hasReadExternalStoragePermission
= true;
1024 } else if (name
== "android.permission.READ_PHONE_STATE") {
1025 hasReadPhoneStatePermission
= true;
1026 } else if (name
== "android.permission.READ_CONTACTS") {
1027 hasReadContactsPermission
= true;
1028 } else if (name
== "android.permission.WRITE_CONTACTS") {
1029 hasWriteContactsPermission
= true;
1030 } else if (name
== "android.permission.READ_CALL_LOG") {
1031 hasReadCallLogPermission
= true;
1032 } else if (name
== "android.permission.WRITE_CALL_LOG") {
1033 hasWriteCallLogPermission
= true;
1035 printf("uses-permission:'%s'\n", name
.string());
1037 fprintf(stderr
, "ERROR getting 'android:name' attribute: %s\n",
1041 } else if (tag
== "uses-package") {
1042 String8 name
= getAttribute(tree
, NAME_ATTR
, &error
);
1043 if (name
!= "" && error
== "") {
1044 printf("uses-package:'%s'\n", name
.string());
1046 fprintf(stderr
, "ERROR getting 'android:name' attribute: %s\n",
1050 } else if (tag
== "original-package") {
1051 String8 name
= getAttribute(tree
, NAME_ATTR
, &error
);
1052 if (name
!= "" && error
== "") {
1053 printf("original-package:'%s'\n", name
.string());
1055 fprintf(stderr
, "ERROR getting 'android:name' attribute: %s\n",
1059 } else if (tag
== "supports-gl-texture") {
1060 String8 name
= getAttribute(tree
, NAME_ATTR
, &error
);
1061 if (name
!= "" && error
== "") {
1062 printf("supports-gl-texture:'%s'\n", name
.string());
1064 fprintf(stderr
, "ERROR getting 'android:name' attribute: %s\n",
1068 } else if (tag
== "compatible-screens") {
1069 printCompatibleScreens(tree
);
1071 } else if (tag
== "package-verifier") {
1072 String8 name
= getAttribute(tree
, NAME_ATTR
, &error
);
1073 if (name
!= "" && error
== "") {
1074 String8 publicKey
= getAttribute(tree
, PUBLIC_KEY_ATTR
, &error
);
1075 if (publicKey
!= "" && error
== "") {
1076 printf("package-verifier: name='%s' publicKey='%s'\n",
1077 name
.string(), publicKey
.string());
1081 } else if (depth
== 3 && withinApplication
) {
1082 withinActivity
= false;
1083 withinReceiver
= false;
1084 withinService
= false;
1085 hasIntentFilter
= false;
1086 if(tag
== "activity") {
1087 withinActivity
= true;
1088 activityName
= getAttribute(tree
, NAME_ATTR
, &error
);
1090 fprintf(stderr
, "ERROR getting 'android:name' attribute: %s\n", error
.string());
1094 activityLabel
= getResolvedAttribute(&res
, tree
, LABEL_ATTR
, &error
);
1096 fprintf(stderr
, "ERROR getting 'android:label' attribute: %s\n", error
.string());
1100 activityIcon
= getResolvedAttribute(&res
, tree
, ICON_ATTR
, &error
);
1102 fprintf(stderr
, "ERROR getting 'android:icon' attribute: %s\n", error
.string());
1106 int32_t orien
= getResolvedIntegerAttribute(&res
, tree
,
1107 SCREEN_ORIENTATION_ATTR
, &error
);
1109 if (orien
== 0 || orien
== 6 || orien
== 8) {
1110 // Requests landscape, sensorLandscape, or reverseLandscape.
1111 reqScreenLandscapeFeature
= true;
1112 } else if (orien
== 1 || orien
== 7 || orien
== 9) {
1113 // Requests portrait, sensorPortrait, or reversePortrait.
1114 reqScreenPortraitFeature
= true;
1117 } else if (tag
== "uses-library") {
1118 String8 libraryName
= getAttribute(tree
, NAME_ATTR
, &error
);
1120 fprintf(stderr
, "ERROR getting 'android:name' attribute for uses-library: %s\n", error
.string());
1123 int req
= getIntegerAttribute(tree
,
1124 REQUIRED_ATTR
, NULL
, 1);
1125 printf("uses-library%s:'%s'\n",
1126 req
? "" : "-not-required", libraryName
.string());
1127 } else if (tag
== "receiver") {
1128 withinReceiver
= true;
1129 receiverName
= getAttribute(tree
, NAME_ATTR
, &error
);
1132 fprintf(stderr
, "ERROR getting 'android:name' attribute for receiver: %s\n", error
.string());
1135 } else if (tag
== "service") {
1136 withinService
= true;
1137 serviceName
= getAttribute(tree
, NAME_ATTR
, &error
);
1140 fprintf(stderr
, "ERROR getting 'android:name' attribute for service: %s\n", error
.string());
1144 } else if ((depth
== 4) && (tag
== "intent-filter")) {
1145 hasIntentFilter
= true;
1146 withinIntentFilter
= true;
1147 actMainActivity
= actWidgetReceivers
= actImeService
= actWallpaperService
= false;
1148 } else if ((depth
== 5) && withinIntentFilter
){
1150 if (tag
== "action") {
1151 action
= getAttribute(tree
, NAME_ATTR
, &error
);
1153 fprintf(stderr
, "ERROR getting 'android:name' attribute: %s\n", error
.string());
1156 if (withinActivity
) {
1157 if (action
== "android.intent.action.MAIN") {
1158 isMainActivity
= true;
1159 actMainActivity
= true;
1161 } else if (withinReceiver
) {
1162 if (action
== "android.appwidget.action.APPWIDGET_UPDATE") {
1163 actWidgetReceivers
= true;
1165 } else if (withinService
) {
1166 if (action
== "android.view.InputMethod") {
1167 actImeService
= true;
1168 } else if (action
== "android.service.wallpaper.WallpaperService") {
1169 actWallpaperService
= true;
1172 if (action
== "android.intent.action.SEARCH") {
1173 isSearchable
= true;
1177 if (tag
== "category") {
1178 String8 category
= getAttribute(tree
, NAME_ATTR
, &error
);
1180 fprintf(stderr
, "ERROR getting 'name' attribute: %s\n", error
.string());
1183 if (withinActivity
) {
1184 if (category
== "android.intent.category.LAUNCHER") {
1185 isLauncherActivity
= true;
1192 // Pre-1.6 implicitly granted permission compatibility logic
1193 if (targetSdk
< 4) {
1194 if (!hasWriteExternalStoragePermission
) {
1195 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
1196 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1197 "'targetSdkVersion < 4'\n");
1198 hasWriteExternalStoragePermission
= true;
1200 if (!hasReadPhoneStatePermission
) {
1201 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
1202 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1203 "'targetSdkVersion < 4'\n");
1207 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1208 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1209 // do this (regardless of target API version) because we can't have
1210 // an app with write permission but not read permission.
1211 if (!hasReadExternalStoragePermission
&& hasWriteExternalStoragePermission
) {
1212 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
1213 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1214 "'requested WRITE_EXTERNAL_STORAGE'\n");
1217 // Pre-JellyBean call log permission compatibility.
1218 if (targetSdk
< 16) {
1219 if (!hasReadCallLogPermission
&& hasReadContactsPermission
) {
1220 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
1221 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1222 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
1224 if (!hasWriteCallLogPermission
&& hasWriteContactsPermission
) {
1225 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
1226 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1227 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
1231 /* The following blocks handle printing "inferred" uses-features, based
1232 * on whether related features or permissions are used by the app.
1233 * Note that the various spec*Feature variables denote whether the
1234 * relevant tag was *present* in the AndroidManfest, not that it was
1235 * present and set to true.
1237 // Camera-related back-compatibility logic
1238 if (!specCameraFeature
) {
1239 if (reqCameraFlashFeature
) {
1240 // if app requested a sub-feature (autofocus or flash) and didn't
1241 // request the base camera feature, we infer that it meant to
1242 printf("uses-feature:'android.hardware.camera'\n");
1243 printf("uses-implied-feature:'android.hardware.camera'," \
1244 "'requested android.hardware.camera.flash feature'\n");
1245 } else if (reqCameraAutofocusFeature
) {
1246 // if app requested a sub-feature (autofocus or flash) and didn't
1247 // request the base camera feature, we infer that it meant to
1248 printf("uses-feature:'android.hardware.camera'\n");
1249 printf("uses-implied-feature:'android.hardware.camera'," \
1250 "'requested android.hardware.camera.autofocus feature'\n");
1251 } else if (hasCameraPermission
) {
1252 // if app wants to use camera but didn't request the feature, we infer
1253 // that it meant to, and further that it wants autofocus
1254 // (which was the 1.0 - 1.5 behavior)
1255 printf("uses-feature:'android.hardware.camera'\n");
1256 if (!specCameraAutofocusFeature
) {
1257 printf("uses-feature:'android.hardware.camera.autofocus'\n");
1258 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1259 "'requested android.permission.CAMERA permission'\n");
1264 // Location-related back-compatibility logic
1265 if (!specLocationFeature
&&
1266 (hasMockLocPermission
|| hasCoarseLocPermission
|| hasGpsPermission
||
1267 hasGeneralLocPermission
|| reqNetworkLocFeature
|| reqGpsFeature
)) {
1268 // if app either takes a location-related permission or requests one of the
1269 // sub-features, we infer that it also meant to request the base location feature
1270 printf("uses-feature:'android.hardware.location'\n");
1271 printf("uses-implied-feature:'android.hardware.location'," \
1272 "'requested a location access permission'\n");
1274 if (!specGpsFeature
&& hasGpsPermission
) {
1275 // if app takes GPS (FINE location) perm but does not request the GPS
1276 // feature, we infer that it meant to
1277 printf("uses-feature:'android.hardware.location.gps'\n");
1278 printf("uses-implied-feature:'android.hardware.location.gps'," \
1279 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
1281 if (!specNetworkLocFeature
&& hasCoarseLocPermission
) {
1282 // if app takes Network location (COARSE location) perm but does not request the
1283 // network location feature, we infer that it meant to
1284 printf("uses-feature:'android.hardware.location.network'\n");
1285 printf("uses-implied-feature:'android.hardware.location.network'," \
1286 "'requested android.permission.ACCESS_COURSE_LOCATION permission'\n");
1289 // Bluetooth-related compatibility logic
1290 if (!specBluetoothFeature
&& hasBluetoothPermission
&& (targetSdk
> 4)) {
1291 // if app takes a Bluetooth permission but does not request the Bluetooth
1292 // feature, we infer that it meant to
1293 printf("uses-feature:'android.hardware.bluetooth'\n");
1294 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1295 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1296 "permission and targetSdkVersion > 4'\n");
1299 // Microphone-related compatibility logic
1300 if (!specMicrophoneFeature
&& hasRecordAudioPermission
) {
1301 // if app takes the record-audio permission but does not request the microphone
1302 // feature, we infer that it meant to
1303 printf("uses-feature:'android.hardware.microphone'\n");
1304 printf("uses-implied-feature:'android.hardware.microphone'," \
1305 "'requested android.permission.RECORD_AUDIO permission'\n");
1308 // WiFi-related compatibility logic
1309 if (!specWiFiFeature
&& hasWiFiPermission
) {
1310 // if app takes one of the WiFi permissions but does not request the WiFi
1311 // feature, we infer that it meant to
1312 printf("uses-feature:'android.hardware.wifi'\n");
1313 printf("uses-implied-feature:'android.hardware.wifi'," \
1314 "'requested android.permission.ACCESS_WIFI_STATE, " \
1315 "android.permission.CHANGE_WIFI_STATE, or " \
1316 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
1319 // Telephony-related compatibility logic
1320 if (!specTelephonyFeature
&& (hasTelephonyPermission
|| reqTelephonySubFeature
)) {
1321 // if app takes one of the telephony permissions or requests a sub-feature but
1322 // does not request the base telephony feature, we infer that it meant to
1323 printf("uses-feature:'android.hardware.telephony'\n");
1324 printf("uses-implied-feature:'android.hardware.telephony'," \
1325 "'requested a telephony-related permission or feature'\n");
1328 // Touchscreen-related back-compatibility logic
1329 if (!specTouchscreenFeature
) { // not a typo!
1330 // all apps are presumed to require a touchscreen, unless they explicitly say
1331 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1332 // Note that specTouchscreenFeature is true if the tag is present, regardless
1333 // of whether its value is true or false, so this is safe
1334 printf("uses-feature:'android.hardware.touchscreen'\n");
1335 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1336 "'assumed you require a touch screen unless explicitly made optional'\n");
1338 if (!specMultitouchFeature
&& reqDistinctMultitouchFeature
) {
1339 // if app takes one of the telephony permissions or requests a sub-feature but
1340 // does not request the base telephony feature, we infer that it meant to
1341 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1342 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1343 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
1346 // Landscape/portrait-related compatibility logic
1347 if (!specScreenLandscapeFeature
&& !specScreenPortraitFeature
) {
1348 // If the app has specified any activities in its manifest
1349 // that request a specific orientation, then assume that
1350 // orientation is required.
1351 if (reqScreenLandscapeFeature
) {
1352 printf("uses-feature:'android.hardware.screen.landscape'\n");
1353 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1354 "'one or more activities have specified a landscape orientation'\n");
1356 if (reqScreenPortraitFeature
) {
1357 printf("uses-feature:'android.hardware.screen.portrait'\n");
1358 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1359 "'one or more activities have specified a portrait orientation'\n");
1363 if (hasMainActivity
) {
1366 if (hasWidgetReceivers
) {
1367 printf("app-widget\n");
1369 if (hasImeService
) {
1372 if (hasWallpaperService
) {
1373 printf("wallpaper\n");
1375 if (hasOtherActivities
) {
1376 printf("other-activities\n");
1381 if (hasOtherReceivers
) {
1382 printf("other-receivers\n");
1384 if (hasOtherServices
) {
1385 printf("other-services\n");
1388 // For modern apps, if screen size buckets haven't been specified
1389 // but the new width ranges have, then infer the buckets from them.
1390 if (smallScreen
> 0 && normalScreen
> 0 && largeScreen
> 0 && xlargeScreen
> 0
1391 && requiresSmallestWidthDp
> 0) {
1392 int compatWidth
= compatibleWidthLimitDp
;
1393 if (compatWidth
<= 0) compatWidth
= requiresSmallestWidthDp
;
1394 if (requiresSmallestWidthDp
<= 240 && compatWidth
>= 240) {
1399 if (requiresSmallestWidthDp
<= 320 && compatWidth
>= 320) {
1404 if (requiresSmallestWidthDp
<= 480 && compatWidth
>= 480) {
1409 if (requiresSmallestWidthDp
<= 720 && compatWidth
>= 720) {
1416 // Determine default values for any unspecified screen sizes,
1417 // based on the target SDK of the package. As of 4 (donut)
1418 // the screen size support was introduced, so all default to
1420 if (smallScreen
> 0) {
1421 smallScreen
= targetSdk
>= 4 ? -1 : 0;
1423 if (normalScreen
> 0) {
1426 if (largeScreen
> 0) {
1427 largeScreen
= targetSdk
>= 4 ? -1 : 0;
1429 if (xlargeScreen
> 0) {
1430 // Introduced in Gingerbread.
1431 xlargeScreen
= targetSdk
>= 9 ? -1 : 0;
1433 if (anyDensity
> 0) {
1434 anyDensity
= (targetSdk
>= 4 || requiresSmallestWidthDp
> 0
1435 || compatibleWidthLimitDp
> 0) ? -1 : 0;
1437 printf("supports-screens:");
1438 if (smallScreen
!= 0) printf(" 'small'");
1439 if (normalScreen
!= 0) printf(" 'normal'");
1440 if (largeScreen
!= 0) printf(" 'large'");
1441 if (xlargeScreen
!= 0) printf(" 'xlarge'");
1443 printf("supports-any-density: '%s'\n", anyDensity
? "true" : "false");
1444 if (requiresSmallestWidthDp
> 0) {
1445 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp
);
1447 if (compatibleWidthLimitDp
> 0) {
1448 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp
);
1450 if (largestWidthLimitDp
> 0) {
1451 printf("largest-width-limit:'%d'\n", largestWidthLimitDp
);
1455 const size_t NL
= locales
.size();
1456 for (size_t i
=0; i
<NL
; i
++) {
1457 const char* localeStr
= locales
[i
].string();
1458 if (localeStr
== NULL
|| strlen(localeStr
) == 0) {
1459 localeStr
= "--_--";
1461 printf(" '%s'", localeStr
);
1465 printf("densities:");
1466 const size_t ND
= densities
.size();
1467 for (size_t i
=0; i
<ND
; i
++) {
1468 printf(" '%d'", densities
[i
]);
1472 AssetDir
* dir
= assets
.openNonAssetDir(assetsCookie
, "lib");
1474 if (dir
->getFileCount() > 0) {
1475 printf("native-code:");
1476 for (size_t i
=0; i
<dir
->getFileCount(); i
++) {
1477 printf(" '%s'", dir
->getFileName(i
).string());
1483 } else if (strcmp("badger", option
) == 0) {
1484 printf("%s", CONSOLE_DATA
);
1485 } else if (strcmp("configurations", option
) == 0) {
1486 Vector
<ResTable_config
> configs
;
1487 res
.getConfigurations(&configs
);
1488 const size_t N
= configs
.size();
1489 for (size_t i
=0; i
<N
; i
++) {
1490 printf("%s\n", configs
[i
].toString().string());
1493 fprintf(stderr
, "ERROR: unknown dump option '%s'\n", option
);
1504 return (result
!= NO_ERROR
);
1509 * Handle the "add" command, which wants to add files to a new or
1510 * pre-existing archive.
1512 int doAdd(Bundle
* bundle
)
1514 ZipFile
* zip
= NULL
;
1515 status_t result
= UNKNOWN_ERROR
;
1516 const char* zipFileName
;
1518 if (bundle
->getUpdate()) {
1519 /* avoid confusion */
1520 fprintf(stderr
, "ERROR: can't use '-u' with add\n");
1524 if (bundle
->getFileSpecCount() < 1) {
1525 fprintf(stderr
, "ERROR: must specify zip file name\n");
1528 zipFileName
= bundle
->getFileSpecEntry(0);
1530 if (bundle
->getFileSpecCount() < 2) {
1531 fprintf(stderr
, "NOTE: nothing to do\n");
1535 zip
= openReadWrite(zipFileName
, true);
1537 fprintf(stderr
, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName
);
1541 for (int i
= 1; i
< bundle
->getFileSpecCount(); i
++) {
1542 const char* fileName
= bundle
->getFileSpecEntry(i
);
1544 if (strcasecmp(String8(fileName
).getPathExtension().string(), ".gz") == 0) {
1545 printf(" '%s'... (from gzip)\n", fileName
);
1546 result
= zip
->addGzip(fileName
, String8(fileName
).getBasePath().string(), NULL
);
1548 if (bundle
->getJunkPath()) {
1549 String8 storageName
= String8(fileName
).getPathLeaf();
1550 printf(" '%s' as '%s'...\n", fileName
, storageName
.string());
1551 result
= zip
->add(fileName
, storageName
.string(),
1552 bundle
->getCompressionMethod(), NULL
);
1554 printf(" '%s'...\n", fileName
);
1555 result
= zip
->add(fileName
, bundle
->getCompressionMethod(), NULL
);
1558 if (result
!= NO_ERROR
) {
1559 fprintf(stderr
, "Unable to add '%s' to '%s'", bundle
->getFileSpecEntry(i
), zipFileName
);
1560 if (result
== NAME_NOT_FOUND
)
1561 fprintf(stderr
, ": file not found\n");
1562 else if (result
== ALREADY_EXISTS
)
1563 fprintf(stderr
, ": already exists in archive\n");
1565 fprintf(stderr
, "\n");
1574 return (result
!= NO_ERROR
);
1579 * Delete files from an existing archive.
1581 int doRemove(Bundle
* bundle
)
1583 ZipFile
* zip
= NULL
;
1584 status_t result
= UNKNOWN_ERROR
;
1585 const char* zipFileName
;
1587 if (bundle
->getFileSpecCount() < 1) {
1588 fprintf(stderr
, "ERROR: must specify zip file name\n");
1591 zipFileName
= bundle
->getFileSpecEntry(0);
1593 if (bundle
->getFileSpecCount() < 2) {
1594 fprintf(stderr
, "NOTE: nothing to do\n");
1598 zip
= openReadWrite(zipFileName
, false);
1600 fprintf(stderr
, "ERROR: failed opening Zip archive '%s'\n",
1605 for (int i
= 1; i
< bundle
->getFileSpecCount(); i
++) {
1606 const char* fileName
= bundle
->getFileSpecEntry(i
);
1609 entry
= zip
->getEntryByName(fileName
);
1610 if (entry
== NULL
) {
1611 printf(" '%s' NOT FOUND\n", fileName
);
1615 result
= zip
->remove(entry
);
1617 if (result
!= NO_ERROR
) {
1618 fprintf(stderr
, "Unable to delete '%s' from '%s'\n",
1619 bundle
->getFileSpecEntry(i
), zipFileName
);
1624 /* update the archive */
1629 return (result
!= NO_ERROR
);
1634 * Package up an asset directory and associated application files.
1636 int doPackage(Bundle
* bundle
)
1638 const char* outputAPKFile
;
1641 sp
<AaptAssets
> assets
;
1644 String8 dependencyFile
;
1646 // -c zz_ZZ means do pseudolocalization
1647 ResourceFilter filter
;
1648 err
= filter
.parse(bundle
->getConfigurations());
1649 if (err
!= NO_ERROR
) {
1652 if (filter
.containsPseudo()) {
1653 bundle
->setPseudolocalize(true);
1656 N
= bundle
->getFileSpecCount();
1657 if (N
< 1 && bundle
->getResourceSourceDirs().size() == 0 && bundle
->getJarFiles().size() == 0
1658 && bundle
->getAndroidManifestFile() == NULL
&& bundle
->getAssetSourceDir() == NULL
) {
1659 fprintf(stderr
, "ERROR: no input files\n");
1663 outputAPKFile
= bundle
->getOutputAPKFile();
1665 // Make sure the filenames provided exist and are of the appropriate type.
1666 if (outputAPKFile
) {
1668 type
= getFileType(outputAPKFile
);
1669 if (type
!= kFileTypeNonexistent
&& type
!= kFileTypeRegular
) {
1671 "ERROR: output file '%s' exists but is not regular file\n",
1678 assets
= new AaptAssets();
1680 // Set up the resource gathering in assets if we're going to generate
1681 // dependency files. Every time we encounter a resource while slurping
1682 // the tree, we'll add it to these stores so we have full resource paths
1683 // to write to a dependency file.
1684 if (bundle
->getGenDependencies()) {
1685 sp
<FilePathStore
> resPathStore
= new FilePathStore
;
1686 assets
->setFullResPaths(resPathStore
);
1687 sp
<FilePathStore
> assetPathStore
= new FilePathStore
;
1688 assets
->setFullAssetPaths(assetPathStore
);
1691 err
= assets
->slurpFromArgs(bundle
);
1696 if (bundle
->getVerbose()) {
1697 assets
->print(String8());
1700 // If they asked for any fileAs that need to be compiled, do so.
1701 if (bundle
->getResourceSourceDirs().size() || bundle
->getAndroidManifestFile()) {
1702 err
= buildResources(bundle
, assets
);
1708 // At this point we've read everything and processed everything. From here
1709 // on out it's just writing output files.
1710 if (SourcePos::hasErrors()) {
1714 // Update symbols with information about which ones are needed as Java symbols.
1715 assets
->applyJavaSymbols();
1716 if (SourcePos::hasErrors()) {
1720 // If we've been asked to generate a dependency file, do that here
1721 if (bundle
->getGenDependencies()) {
1722 // If this is the packaging step, generate the dependency file next to
1723 // the output apk (e.g. bin/resources.ap_.d)
1724 if (outputAPKFile
) {
1725 dependencyFile
= String8(outputAPKFile
);
1726 // Add the .d extension to the dependency file.
1727 dependencyFile
.append(".d");
1729 // Else if this is the R.java dependency generation step,
1730 // generate the dependency file in the R.java package subdirectory
1731 // e.g. gen/com/foo/app/R.java.d
1732 dependencyFile
= String8(bundle
->getRClassDir());
1733 dependencyFile
.appendPath("R.java.d");
1735 // Make sure we have a clean dependency file to start with
1736 fp
= fopen(dependencyFile
, "w");
1740 // Write out R.java constants
1741 if (!assets
->havePrivateSymbols()) {
1742 if (bundle
->getCustomPackage() == NULL
) {
1743 // Write the R.java file into the appropriate class directory
1744 // e.g. gen/com/foo/app/R.java
1745 err
= writeResourceSymbols(bundle
, assets
, assets
->getPackage(), true);
1747 const String8
customPkg(bundle
->getCustomPackage());
1748 err
= writeResourceSymbols(bundle
, assets
, customPkg
, true);
1753 // If we have library files, we're going to write our R.java file into
1754 // the appropriate class directory for those libraries as well.
1755 // e.g. gen/com/foo/app/lib/R.java
1756 if (bundle
->getExtraPackages() != NULL
) {
1758 String8
libs(bundle
->getExtraPackages());
1759 char* packageString
= strtok(libs
.lockBuffer(libs
.length()), ":");
1760 while (packageString
!= NULL
) {
1761 // Write the R.java file out with the correct package name
1762 err
= writeResourceSymbols(bundle
, assets
, String8(packageString
), true);
1766 packageString
= strtok(NULL
, ":");
1768 libs
.unlockBuffer();
1771 err
= writeResourceSymbols(bundle
, assets
, assets
->getPackage(), false);
1775 err
= writeResourceSymbols(bundle
, assets
, assets
->getSymbolsPrivatePackage(), true);
1781 // Write out the ProGuard file
1782 err
= writeProguardFile(bundle
, assets
);
1788 if (outputAPKFile
) {
1789 err
= writeAPK(bundle
, assets
, String8(outputAPKFile
));
1790 if (err
!= NO_ERROR
) {
1791 fprintf(stderr
, "ERROR: packaging of '%s' failed\n", outputAPKFile
);
1796 // If we've been asked to generate a dependency file, we need to finish up here.
1797 // the writeResourceSymbols and writeAPK functions have already written the target
1798 // half of the dependency file, now we need to write the prerequisites. (files that
1799 // the R.java file or .ap_ file depend on)
1800 if (bundle
->getGenDependencies()) {
1801 // Now that writeResourceSymbols or writeAPK has taken care of writing
1802 // the targets to our dependency file, we'll write the prereqs
1803 fp
= fopen(dependencyFile
, "a+");
1805 bool includeRaw
= (outputAPKFile
!= NULL
);
1806 err
= writeDependencyPreReqs(bundle
, assets
, fp
, includeRaw
);
1807 // Also manually add the AndroidManifeset since it's not under res/ or assets/
1808 // and therefore was not added to our pathstores during slurping
1809 fprintf(fp
, "%s \\\n", bundle
->getAndroidManifestFile());
1815 if (SourcePos::hasErrors()) {
1816 SourcePos::printErrors(stderr
);
1824 * -S flag points to a source directory containing drawable* folders
1825 * -C flag points to destination directory. The folder structure in the
1826 * source directory will be mirrored to the destination (cache) directory
1829 * Destination directory will be updated to match the PNG files in
1830 * the source directory.
1832 int doCrunch(Bundle
* bundle
)
1834 fprintf(stdout
, "Crunching PNG Files in ");
1835 fprintf(stdout
, "source dir: %s\n", bundle
->getResourceSourceDirs()[0]);
1836 fprintf(stdout
, "To destination dir: %s\n", bundle
->getCrunchedOutputDir());
1838 updatePreProcessedCache(bundle
);
1844 * Do PNG Crunching on a single flag
1845 * -i points to a single png file
1846 * -o points to a single png output file
1848 int doSingleCrunch(Bundle
* bundle
)
1850 fprintf(stdout
, "Crunching single PNG file: %s\n", bundle
->getSingleCrunchInputFile());
1851 fprintf(stdout
, "\tOutput file: %s\n", bundle
->getSingleCrunchOutputFile());
1853 String8
input(bundle
->getSingleCrunchInputFile());
1854 String8
output(bundle
->getSingleCrunchOutputFile());
1855 return preProcessImageToCache(bundle
, input
, output
);
1858 char CONSOLE_DATA
[2925] = {
1859 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1860 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1861 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1862 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1863 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1864 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1865 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1866 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1867 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1868 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1869 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1870 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1871 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1872 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1873 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1874 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1875 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1876 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1877 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1878 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1879 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1880 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1881 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1882 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1883 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1884 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1885 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1886 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1887 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1888 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1889 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1890 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1891 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1892 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1893 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1894 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1895 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1896 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1897 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1898 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1899 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1900 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
1901 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1902 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1903 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
1904 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
1905 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1906 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
1907 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
1908 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1909 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
1910 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
1911 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
1912 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1913 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1914 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
1915 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
1916 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
1917 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1918 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
1919 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
1920 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
1921 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
1922 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1923 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1924 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
1925 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
1926 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
1927 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
1928 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
1929 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
1930 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
1931 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1932 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
1933 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1934 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
1935 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1936 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
1937 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
1938 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1939 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
1940 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
1941 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
1942 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1943 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1944 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
1945 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1946 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
1947 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
1948 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1949 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1950 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
1951 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
1952 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1953 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1954 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1955 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1956 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1957 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
1958 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
1959 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1960 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1961 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1962 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
1963 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1964 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1965 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
1966 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1967 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1968 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
1969 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
1970 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1971 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1972 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1973 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1974 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1975 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
1976 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
1977 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1978 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1979 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
1980 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
1981 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1982 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1983 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
1984 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
1985 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
1986 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
1987 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
1988 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1989 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1990 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
1991 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
1992 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
1993 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
1994 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
1995 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1996 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1997 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1998 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1999 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2000 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2001 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2002 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2003 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2004 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2005 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2006 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2007 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2008 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2009 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2010 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2011 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2012 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2013 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2014 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2015 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2016 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2017 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2018 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2019 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2020 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2021 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10