2 // Copyright 2006 The Android Open Source Project
4 // Android Asset Packaging Tool main entry point.
8 #include "ResourceFilter.h"
9 #include "ResourceTable.h"
12 #include <utils/Log.h>
13 #include <utils/threads.h>
14 #include <utils/List.h>
15 #include <utils/Errors.h>
20 using namespace android
;
23 * Show version info. All the cool kids do it.
25 int doVersion(Bundle
* bundle
)
27 if (bundle
->getFileSpecCount() != 0)
28 printf("(ignoring extra arguments)\n");
29 printf("Android Asset Packaging Tool, v0.2\n");
36 * Open the file read only. The call fails if the file doesn't exist.
38 * Returns NULL on failure.
40 ZipFile
* openReadOnly(const char* fileName
)
46 result
= zip
->open(fileName
, ZipFile::kOpenReadOnly
);
47 if (result
!= NO_ERROR
) {
48 if (result
== NAME_NOT_FOUND
)
49 fprintf(stderr
, "ERROR: '%s' not found\n", fileName
);
50 else if (result
== PERMISSION_DENIED
)
51 fprintf(stderr
, "ERROR: '%s' access denied\n", fileName
);
53 fprintf(stderr
, "ERROR: failed opening '%s' as Zip file\n",
63 * Open the file read-write. The file will be created if it doesn't
64 * already exist and "okayToCreate" is set.
66 * Returns NULL on failure.
68 ZipFile
* openReadWrite(const char* fileName
, bool okayToCreate
)
74 flags
= ZipFile::kOpenReadWrite
;
76 flags
|= ZipFile::kOpenCreate
;
79 result
= zip
->open(fileName
, flags
);
80 if (result
!= NO_ERROR
) {
92 * Return a short string describing the compression method.
94 const char* compressionName(int method
)
96 if (method
== ZipEntry::kCompressStored
)
98 else if (method
== ZipEntry::kCompressDeflated
)
105 * Return the percent reduction in size (0% == no compression).
107 int calcPercent(long uncompressedLen
, long compressedLen
)
109 if (!uncompressedLen
)
112 return (int) (100.0 - (compressedLen
* 100.0) / uncompressedLen
+ 0.5);
116 * Handle the "list" command, which can be a simple file dump or
119 * The verbose listing closely matches the output of the Info-ZIP "unzip"
122 int doList(Bundle
* bundle
)
126 const ZipEntry
* entry
;
127 long totalUncLen
, totalCompLen
;
128 const char* zipFileName
;
130 if (bundle
->getFileSpecCount() != 1) {
131 fprintf(stderr
, "ERROR: specify zip file name (only)\n");
134 zipFileName
= bundle
->getFileSpecEntry(0);
136 zip
= openReadOnly(zipFileName
);
142 if (bundle
->getVerbose()) {
143 printf("Archive: %s\n", zipFileName
);
145 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
147 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
150 totalUncLen
= totalCompLen
= 0;
152 count
= zip
->getNumEntries();
153 for (i
= 0; i
< count
; i
++) {
154 entry
= zip
->getEntryByIndex(i
);
155 if (bundle
->getVerbose()) {
159 when
= entry
->getModWhen();
160 strftime(dateBuf
, sizeof(dateBuf
), "%m-%d-%y %H:%M",
163 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
164 (long) entry
->getUncompressedLen(),
165 compressionName(entry
->getCompressionMethod()),
166 (long) entry
->getCompressedLen(),
167 calcPercent(entry
->getUncompressedLen(),
168 entry
->getCompressedLen()),
169 (size_t) entry
->getLFHOffset(),
172 entry
->getFileName());
174 printf("%s\n", entry
->getFileName());
177 totalUncLen
+= entry
->getUncompressedLen();
178 totalCompLen
+= entry
->getCompressedLen();
181 if (bundle
->getVerbose()) {
183 "-------- ------- --- -------\n");
184 printf("%8ld %7ld %2d%% %d files\n",
187 calcPercent(totalUncLen
, totalCompLen
),
188 zip
->getNumEntries());
191 if (bundle
->getAndroidList()) {
193 if (!assets
.addAssetPath(String8(zipFileName
), NULL
)) {
194 fprintf(stderr
, "ERROR: list -a failed because assets could not be loaded\n");
198 const ResTable
& res
= assets
.getResources(false);
200 printf("\nNo resource table found.\n");
202 #ifndef HAVE_ANDROID_OS
203 printf("\nResource table:\n");
208 Asset
* manifestAsset
= assets
.openNonAsset("AndroidManifest.xml",
209 Asset::ACCESS_BUFFER
);
210 if (manifestAsset
== NULL
) {
211 printf("\nNo AndroidManifest.xml found.\n");
213 printf("\nAndroid manifest:\n");
215 tree
.setTo(manifestAsset
->getBuffer(true),
216 manifestAsset
->getLength());
217 printXMLBlock(&tree
);
219 delete manifestAsset
;
229 static ssize_t
indexOfAttribute(const ResXMLTree
& tree
, uint32_t attrRes
)
231 size_t N
= tree
.getAttributeCount();
232 for (size_t i
=0; i
<N
; i
++) {
233 if (tree
.getAttributeNameResID(i
) == attrRes
) {
240 String8
getAttribute(const ResXMLTree
& tree
, const char* ns
,
241 const char* attr
, String8
* outError
)
243 ssize_t idx
= tree
.indexOfAttribute(ns
, attr
);
248 if (tree
.getAttributeValue(idx
, &value
) != NO_ERROR
) {
249 if (value
.dataType
!= Res_value::TYPE_STRING
) {
250 if (outError
!= NULL
) *outError
= "attribute is not a string value";
255 const uint16_t* str
= tree
.getAttributeStringValue(idx
, &len
);
256 return str
? String8(str
, len
) : String8();
259 static String8
getAttribute(const ResXMLTree
& tree
, uint32_t attrRes
, String8
* outError
)
261 ssize_t idx
= indexOfAttribute(tree
, attrRes
);
266 if (tree
.getAttributeValue(idx
, &value
) != NO_ERROR
) {
267 if (value
.dataType
!= Res_value::TYPE_STRING
) {
268 if (outError
!= NULL
) *outError
= "attribute is not a string value";
273 const uint16_t* str
= tree
.getAttributeStringValue(idx
, &len
);
274 return str
? String8(str
, len
) : String8();
277 static int32_t getIntegerAttribute(const ResXMLTree
& tree
, uint32_t attrRes
,
278 String8
* outError
, int32_t defValue
= -1)
280 ssize_t idx
= indexOfAttribute(tree
, attrRes
);
285 if (tree
.getAttributeValue(idx
, &value
) != NO_ERROR
) {
286 if (value
.dataType
< Res_value::TYPE_FIRST_INT
287 || value
.dataType
> Res_value::TYPE_LAST_INT
) {
288 if (outError
!= NULL
) *outError
= "attribute is not an integer value";
295 static int32_t getResolvedIntegerAttribute(const ResTable
* resTable
, const ResXMLTree
& tree
,
296 uint32_t attrRes
, String8
* outError
, int32_t defValue
= -1)
298 ssize_t idx
= indexOfAttribute(tree
, attrRes
);
303 if (tree
.getAttributeValue(idx
, &value
) != NO_ERROR
) {
304 if (value
.dataType
== Res_value::TYPE_REFERENCE
) {
305 resTable
->resolveReference(&value
, 0);
307 if (value
.dataType
< Res_value::TYPE_FIRST_INT
308 || value
.dataType
> Res_value::TYPE_LAST_INT
) {
309 if (outError
!= NULL
) *outError
= "attribute is not an integer value";
316 static String8
getResolvedAttribute(const ResTable
* resTable
, const ResXMLTree
& tree
,
317 uint32_t attrRes
, String8
* outError
)
319 ssize_t idx
= indexOfAttribute(tree
, attrRes
);
324 if (tree
.getAttributeValue(idx
, &value
) != NO_ERROR
) {
325 if (value
.dataType
== Res_value::TYPE_STRING
) {
327 const uint16_t* str
= tree
.getAttributeStringValue(idx
, &len
);
328 return str
? String8(str
, len
) : String8();
330 resTable
->resolveReference(&value
, 0);
331 if (value
.dataType
!= Res_value::TYPE_STRING
) {
332 if (outError
!= NULL
) *outError
= "attribute is not a string value";
337 const Res_value
* value2
= &value
;
338 const char16_t* str
= const_cast<ResTable
*>(resTable
)->valueToString(value2
, 0, NULL
, &len
);
339 return str
? String8(str
, len
) : String8();
342 // These are attribute resource constants for the platform, as found
345 LABEL_ATTR
= 0x01010001,
346 ICON_ATTR
= 0x01010002,
347 NAME_ATTR
= 0x01010003,
348 DEBUGGABLE_ATTR
= 0x0101000f,
349 VERSION_CODE_ATTR
= 0x0101021b,
350 VERSION_NAME_ATTR
= 0x0101021c,
351 SCREEN_ORIENTATION_ATTR
= 0x0101001e,
352 MIN_SDK_VERSION_ATTR
= 0x0101020c,
353 MAX_SDK_VERSION_ATTR
= 0x01010271,
354 REQ_TOUCH_SCREEN_ATTR
= 0x01010227,
355 REQ_KEYBOARD_TYPE_ATTR
= 0x01010228,
356 REQ_HARD_KEYBOARD_ATTR
= 0x01010229,
357 REQ_NAVIGATION_ATTR
= 0x0101022a,
358 REQ_FIVE_WAY_NAV_ATTR
= 0x01010232,
359 TARGET_SDK_VERSION_ATTR
= 0x01010270,
360 TEST_ONLY_ATTR
= 0x01010272,
361 ANY_DENSITY_ATTR
= 0x0101026c,
362 GL_ES_VERSION_ATTR
= 0x01010281,
363 SMALL_SCREEN_ATTR
= 0x01010284,
364 NORMAL_SCREEN_ATTR
= 0x01010285,
365 LARGE_SCREEN_ATTR
= 0x01010286,
366 XLARGE_SCREEN_ATTR
= 0x010102bf,
367 REQUIRED_ATTR
= 0x0101028e,
368 SCREEN_SIZE_ATTR
= 0x010102ca,
369 SCREEN_DENSITY_ATTR
= 0x010102cb,
370 REQUIRES_SMALLEST_WIDTH_DP_ATTR
= 0x01010364,
371 COMPATIBLE_WIDTH_LIMIT_DP_ATTR
= 0x01010365,
372 LARGEST_WIDTH_LIMIT_DP_ATTR
= 0x01010366,
373 PUBLIC_KEY_ATTR
= 0x010103a6,
376 const char *getComponentName(String8
&pkgName
, String8
&componentName
) {
377 ssize_t idx
= componentName
.find(".");
378 String8
retStr(pkgName
);
380 retStr
+= componentName
;
381 } else if (idx
< 0) {
383 retStr
+= componentName
;
385 return componentName
.string();
387 return retStr
.string();
390 static void printCompatibleScreens(ResXMLTree
& tree
) {
392 ResXMLTree::event_code_t code
;
395 printf("compatible-screens:");
396 while ((code
=tree
.next()) != ResXMLTree::END_DOCUMENT
&& code
!= ResXMLTree::BAD_DOCUMENT
) {
397 if (code
== ResXMLTree::END_TAG
) {
404 if (code
!= ResXMLTree::START_TAG
) {
408 String8
tag(tree
.getElementName(&len
));
409 if (tag
== "screen") {
410 int32_t screenSize
= getIntegerAttribute(tree
,
411 SCREEN_SIZE_ATTR
, NULL
, -1);
412 int32_t screenDensity
= getIntegerAttribute(tree
,
413 SCREEN_DENSITY_ATTR
, NULL
, -1);
414 if (screenSize
> 0 && screenDensity
> 0) {
419 printf("'%d/%d'", screenSize
, screenDensity
);
427 * Handle the "dump" command, to extract select data from an archive.
429 extern char CONSOLE_DATA
[2925]; // see EOF
430 int doDump(Bundle
* bundle
)
432 status_t result
= UNKNOWN_ERROR
;
435 if (bundle
->getFileSpecCount() < 1) {
436 fprintf(stderr
, "ERROR: no dump option specified\n");
440 if (bundle
->getFileSpecCount() < 2) {
441 fprintf(stderr
, "ERROR: no dump file specified\n");
445 const char* option
= bundle
->getFileSpecEntry(0);
446 const char* filename
= bundle
->getFileSpecEntry(1);
450 if (!assets
.addAssetPath(String8(filename
), &assetsCookie
)) {
451 fprintf(stderr
, "ERROR: dump failed because assets could not be loaded\n");
455 // Make a dummy config for retrieving resources... we need to supply
456 // non-default values for some configs so that we can retrieve resources
457 // in the app that don't have a default. The most important of these is
458 // the API version because key resources like icons will have an implicit
459 // version if they are using newer config types like density.
460 ResTable_config config
;
461 config
.language
[0] = 'e';
462 config
.language
[1] = 'n';
463 config
.country
[0] = 'U';
464 config
.country
[1] = 'S';
465 config
.orientation
= ResTable_config::ORIENTATION_PORT
;
466 config
.density
= ResTable_config::DENSITY_MEDIUM
;
467 config
.sdkVersion
= 10000; // Very high.
468 config
.screenWidthDp
= 320;
469 config
.screenHeightDp
= 480;
470 config
.smallestScreenWidthDp
= 320;
471 assets
.setConfiguration(config
);
473 const ResTable
& res
= assets
.getResources(false);
475 fprintf(stderr
, "ERROR: dump failed because no resource table was found\n");
479 if (strcmp("resources", option
) == 0) {
480 #ifndef HAVE_ANDROID_OS
481 res
.print(bundle
->getValues());
484 } else if (strcmp("strings", option
) == 0) {
485 const ResStringPool
* pool
= res
.getTableStringBlock(0);
486 printStringPool(pool
);
488 } else if (strcmp("xmltree", option
) == 0) {
489 if (bundle
->getFileSpecCount() < 3) {
490 fprintf(stderr
, "ERROR: no dump xmltree resource file specified\n");
494 for (int i
=2; i
<bundle
->getFileSpecCount(); i
++) {
495 const char* resname
= bundle
->getFileSpecEntry(i
);
497 asset
= assets
.openNonAsset(resname
, Asset::ACCESS_BUFFER
);
499 fprintf(stderr
, "ERROR: dump failed because resource %s found\n", resname
);
503 if (tree
.setTo(asset
->getBuffer(true),
504 asset
->getLength()) != NO_ERROR
) {
505 fprintf(stderr
, "ERROR: Resource %s is corrupt\n", resname
);
509 printXMLBlock(&tree
);
515 } else if (strcmp("xmlstrings", option
) == 0) {
516 if (bundle
->getFileSpecCount() < 3) {
517 fprintf(stderr
, "ERROR: no dump xmltree resource file specified\n");
521 for (int i
=2; i
<bundle
->getFileSpecCount(); i
++) {
522 const char* resname
= bundle
->getFileSpecEntry(i
);
524 asset
= assets
.openNonAsset(resname
, Asset::ACCESS_BUFFER
);
526 fprintf(stderr
, "ERROR: dump failed because resource %s found\n", resname
);
530 if (tree
.setTo(asset
->getBuffer(true),
531 asset
->getLength()) != NO_ERROR
) {
532 fprintf(stderr
, "ERROR: Resource %s is corrupt\n", resname
);
535 printStringPool(&tree
.getStrings());
542 asset
= assets
.openNonAsset("AndroidManifest.xml",
543 Asset::ACCESS_BUFFER
);
545 fprintf(stderr
, "ERROR: dump failed because no AndroidManifest.xml found\n");
549 if (tree
.setTo(asset
->getBuffer(true),
550 asset
->getLength()) != NO_ERROR
) {
551 fprintf(stderr
, "ERROR: AndroidManifest.xml is corrupt\n");
556 if (strcmp("permissions", option
) == 0) {
558 ResXMLTree::event_code_t code
;
560 while ((code
=tree
.next()) != ResXMLTree::END_DOCUMENT
&& code
!= ResXMLTree::BAD_DOCUMENT
) {
561 if (code
== ResXMLTree::END_TAG
) {
565 if (code
!= ResXMLTree::START_TAG
) {
569 String8
tag(tree
.getElementName(&len
));
570 //printf("Depth %d tag %s\n", depth, tag.string());
572 if (tag
!= "manifest") {
573 fprintf(stderr
, "ERROR: manifest does not start with <manifest> tag\n");
576 String8 pkg
= getAttribute(tree
, NULL
, "package", NULL
);
577 printf("package: %s\n", pkg
.string());
578 } else if (depth
== 2 && tag
== "permission") {
580 String8 name
= getAttribute(tree
, NAME_ATTR
, &error
);
582 fprintf(stderr
, "ERROR: %s\n", error
.string());
585 printf("permission: %s\n", name
.string());
586 } else if (depth
== 2 && tag
== "uses-permission") {
588 String8 name
= getAttribute(tree
, NAME_ATTR
, &error
);
590 fprintf(stderr
, "ERROR: %s\n", error
.string());
593 printf("uses-permission: %s\n", name
.string());
596 } else if (strcmp("badging", option
) == 0) {
597 Vector
<String8
> locales
;
598 res
.getLocales(&locales
);
600 Vector
<ResTable_config
> configs
;
601 res
.getConfigurations(&configs
);
602 SortedVector
<int> densities
;
603 const size_t NC
= configs
.size();
604 for (size_t i
=0; i
<NC
; i
++) {
605 int dens
= configs
[i
].density
;
606 if (dens
== 0) dens
= 160;
611 ResXMLTree::event_code_t code
;
614 bool withinActivity
= false;
615 bool isMainActivity
= false;
616 bool isLauncherActivity
= false;
617 bool isSearchable
= false;
618 bool withinApplication
= false;
619 bool withinReceiver
= false;
620 bool withinService
= false;
621 bool withinIntentFilter
= false;
622 bool hasMainActivity
= false;
623 bool hasOtherActivities
= false;
624 bool hasOtherReceivers
= false;
625 bool hasOtherServices
= false;
626 bool hasWallpaperService
= false;
627 bool hasImeService
= false;
628 bool hasWidgetReceivers
= false;
629 bool hasIntentFilter
= false;
630 bool actMainActivity
= false;
631 bool actWidgetReceivers
= false;
632 bool actImeService
= false;
633 bool actWallpaperService
= false;
635 // These two implement the implicit permissions that are granted
636 // to pre-1.6 applications.
637 bool hasWriteExternalStoragePermission
= false;
638 bool hasReadPhoneStatePermission
= false;
640 // If an app requests write storage, they will also get read storage.
641 bool hasReadExternalStoragePermission
= false;
643 // Implement transition to read and write call log.
644 bool hasReadContactsPermission
= false;
645 bool hasWriteContactsPermission
= false;
646 bool hasReadCallLogPermission
= false;
647 bool hasWriteCallLogPermission
= false;
649 // This next group of variables is used to implement a group of
650 // backward-compatibility heuristics necessitated by the addition of
651 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
652 // heuristic is "if an app requests a permission but doesn't explicitly
653 // request the corresponding <uses-feature>, presume it's there anyway".
654 bool specCameraFeature
= false; // camera-related
655 bool specCameraAutofocusFeature
= false;
656 bool reqCameraAutofocusFeature
= false;
657 bool reqCameraFlashFeature
= false;
658 bool hasCameraPermission
= false;
659 bool specLocationFeature
= false; // location-related
660 bool specNetworkLocFeature
= false;
661 bool reqNetworkLocFeature
= false;
662 bool specGpsFeature
= false;
663 bool reqGpsFeature
= false;
664 bool hasMockLocPermission
= false;
665 bool hasCoarseLocPermission
= false;
666 bool hasGpsPermission
= false;
667 bool hasGeneralLocPermission
= false;
668 bool specBluetoothFeature
= false; // Bluetooth API-related
669 bool hasBluetoothPermission
= false;
670 bool specMicrophoneFeature
= false; // microphone-related
671 bool hasRecordAudioPermission
= false;
672 bool specWiFiFeature
= false;
673 bool hasWiFiPermission
= false;
674 bool specTelephonyFeature
= false; // telephony-related
675 bool reqTelephonySubFeature
= false;
676 bool hasTelephonyPermission
= false;
677 bool specTouchscreenFeature
= false; // touchscreen-related
678 bool specMultitouchFeature
= false;
679 bool reqDistinctMultitouchFeature
= false;
680 bool specScreenPortraitFeature
= false;
681 bool specScreenLandscapeFeature
= false;
682 bool reqScreenPortraitFeature
= false;
683 bool reqScreenLandscapeFeature
= false;
684 // 2.2 also added some other features that apps can request, but that
685 // have no corresponding permission, so we cannot implement any
686 // back-compatibility heuristic for them. The below are thus unnecessary
687 // (but are retained here for documentary purposes.)
688 //bool specCompassFeature = false;
689 //bool specAccelerometerFeature = false;
690 //bool specProximityFeature = false;
691 //bool specAmbientLightFeature = false;
692 //bool specLiveWallpaperFeature = false;
696 int normalScreen
= 1;
698 int xlargeScreen
= 1;
700 int requiresSmallestWidthDp
= 0;
701 int compatibleWidthLimitDp
= 0;
702 int largestWidthLimitDp
= 0;
704 String8 activityName
;
705 String8 activityLabel
;
706 String8 activityIcon
;
707 String8 receiverName
;
709 while ((code
=tree
.next()) != ResXMLTree::END_DOCUMENT
&& code
!= ResXMLTree::BAD_DOCUMENT
) {
710 if (code
== ResXMLTree::END_TAG
) {
713 withinApplication
= false;
714 } else if (depth
< 3) {
715 if (withinActivity
&& isMainActivity
&& isLauncherActivity
) {
716 const char *aName
= getComponentName(pkg
, activityName
);
717 printf("launchable-activity:");
719 printf(" name='%s' ", aName
);
721 printf(" label='%s' icon='%s'\n",
722 activityLabel
.string(),
723 activityIcon
.string());
725 if (!hasIntentFilter
) {
726 hasOtherActivities
|= withinActivity
;
727 hasOtherReceivers
|= withinReceiver
;
728 hasOtherServices
|= withinService
;
730 withinActivity
= false;
731 withinService
= false;
732 withinReceiver
= false;
733 hasIntentFilter
= false;
734 isMainActivity
= isLauncherActivity
= false;
735 } else if (depth
< 4) {
736 if (withinIntentFilter
) {
737 if (withinActivity
) {
738 hasMainActivity
|= actMainActivity
;
739 hasOtherActivities
|= !actMainActivity
;
740 } else if (withinReceiver
) {
741 hasWidgetReceivers
|= actWidgetReceivers
;
742 hasOtherReceivers
|= !actWidgetReceivers
;
743 } else if (withinService
) {
744 hasImeService
|= actImeService
;
745 hasWallpaperService
|= actWallpaperService
;
746 hasOtherServices
|= (!actImeService
&& !actWallpaperService
);
749 withinIntentFilter
= false;
753 if (code
!= ResXMLTree::START_TAG
) {
757 String8
tag(tree
.getElementName(&len
));
758 //printf("Depth %d, %s\n", depth, tag.string());
760 if (tag
!= "manifest") {
761 fprintf(stderr
, "ERROR: manifest does not start with <manifest> tag\n");
764 pkg
= getAttribute(tree
, NULL
, "package", NULL
);
765 printf("package: name='%s' ", pkg
.string());
766 int32_t versionCode
= getIntegerAttribute(tree
, VERSION_CODE_ATTR
, &error
);
768 fprintf(stderr
, "ERROR getting 'android:versionCode' attribute: %s\n", error
.string());
771 if (versionCode
> 0) {
772 printf("versionCode='%d' ", versionCode
);
774 printf("versionCode='' ");
776 String8 versionName
= getResolvedAttribute(&res
, tree
, VERSION_NAME_ATTR
, &error
);
778 fprintf(stderr
, "ERROR getting 'android:versionName' attribute: %s\n", error
.string());
781 printf("versionName='%s'\n", versionName
.string());
782 } else if (depth
== 2) {
783 withinApplication
= false;
784 if (tag
== "application") {
785 withinApplication
= true;
788 const size_t NL
= locales
.size();
789 for (size_t i
=0; i
<NL
; i
++) {
790 const char* localeStr
= locales
[i
].string();
791 assets
.setLocale(localeStr
!= NULL
? localeStr
: "");
792 String8 llabel
= getResolvedAttribute(&res
, tree
, LABEL_ATTR
, &error
);
794 if (localeStr
== NULL
|| strlen(localeStr
) == 0) {
796 printf("application-label:'%s'\n", llabel
.string());
801 printf("application-label-%s:'%s'\n", localeStr
,
807 ResTable_config tmpConfig
= config
;
808 const size_t ND
= densities
.size();
809 for (size_t i
=0; i
<ND
; i
++) {
810 tmpConfig
.density
= densities
[i
];
811 assets
.setConfiguration(tmpConfig
);
812 String8 icon
= getResolvedAttribute(&res
, tree
, ICON_ATTR
, &error
);
814 printf("application-icon-%d:'%s'\n", densities
[i
], icon
.string());
817 assets
.setConfiguration(config
);
819 String8 icon
= getResolvedAttribute(&res
, tree
, ICON_ATTR
, &error
);
821 fprintf(stderr
, "ERROR getting 'android:icon' attribute: %s\n", error
.string());
824 int32_t testOnly
= getIntegerAttribute(tree
, TEST_ONLY_ATTR
, &error
, 0);
826 fprintf(stderr
, "ERROR getting 'android:testOnly' attribute: %s\n", error
.string());
829 printf("application: label='%s' ", label
.string());
830 printf("icon='%s'\n", icon
.string());
832 printf("testOnly='%d'\n", testOnly
);
835 int32_t debuggable
= getResolvedIntegerAttribute(&res
, tree
, DEBUGGABLE_ATTR
, &error
, 0);
837 fprintf(stderr
, "ERROR getting 'android:debuggable' attribute: %s\n", error
.string());
840 if (debuggable
!= 0) {
841 printf("application-debuggable\n");
843 } else if (tag
== "uses-sdk") {
844 int32_t code
= getIntegerAttribute(tree
, MIN_SDK_VERSION_ATTR
, &error
);
847 String8 name
= getResolvedAttribute(&res
, tree
, MIN_SDK_VERSION_ATTR
, &error
);
849 fprintf(stderr
, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
853 if (name
== "Donut") targetSdk
= 4;
854 printf("sdkVersion:'%s'\n", name
.string());
855 } else if (code
!= -1) {
857 printf("sdkVersion:'%d'\n", code
);
859 code
= getIntegerAttribute(tree
, MAX_SDK_VERSION_ATTR
, NULL
, -1);
861 printf("maxSdkVersion:'%d'\n", code
);
863 code
= getIntegerAttribute(tree
, TARGET_SDK_VERSION_ATTR
, &error
);
866 String8 name
= getResolvedAttribute(&res
, tree
, TARGET_SDK_VERSION_ATTR
, &error
);
868 fprintf(stderr
, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
872 if (name
== "Donut" && targetSdk
< 4) targetSdk
= 4;
873 printf("targetSdkVersion:'%s'\n", name
.string());
874 } else if (code
!= -1) {
875 if (targetSdk
< code
) {
878 printf("targetSdkVersion:'%d'\n", code
);
880 } else if (tag
== "uses-configuration") {
881 int32_t reqTouchScreen
= getIntegerAttribute(tree
,
882 REQ_TOUCH_SCREEN_ATTR
, NULL
, 0);
883 int32_t reqKeyboardType
= getIntegerAttribute(tree
,
884 REQ_KEYBOARD_TYPE_ATTR
, NULL
, 0);
885 int32_t reqHardKeyboard
= getIntegerAttribute(tree
,
886 REQ_HARD_KEYBOARD_ATTR
, NULL
, 0);
887 int32_t reqNavigation
= getIntegerAttribute(tree
,
888 REQ_NAVIGATION_ATTR
, NULL
, 0);
889 int32_t reqFiveWayNav
= getIntegerAttribute(tree
,
890 REQ_FIVE_WAY_NAV_ATTR
, NULL
, 0);
891 printf("uses-configuration:");
892 if (reqTouchScreen
!= 0) {
893 printf(" reqTouchScreen='%d'", reqTouchScreen
);
895 if (reqKeyboardType
!= 0) {
896 printf(" reqKeyboardType='%d'", reqKeyboardType
);
898 if (reqHardKeyboard
!= 0) {
899 printf(" reqHardKeyboard='%d'", reqHardKeyboard
);
901 if (reqNavigation
!= 0) {
902 printf(" reqNavigation='%d'", reqNavigation
);
904 if (reqFiveWayNav
!= 0) {
905 printf(" reqFiveWayNav='%d'", reqFiveWayNav
);
908 } else if (tag
== "supports-screens") {
909 smallScreen
= getIntegerAttribute(tree
,
910 SMALL_SCREEN_ATTR
, NULL
, 1);
911 normalScreen
= getIntegerAttribute(tree
,
912 NORMAL_SCREEN_ATTR
, NULL
, 1);
913 largeScreen
= getIntegerAttribute(tree
,
914 LARGE_SCREEN_ATTR
, NULL
, 1);
915 xlargeScreen
= getIntegerAttribute(tree
,
916 XLARGE_SCREEN_ATTR
, NULL
, 1);
917 anyDensity
= getIntegerAttribute(tree
,
918 ANY_DENSITY_ATTR
, NULL
, 1);
919 requiresSmallestWidthDp
= getIntegerAttribute(tree
,
920 REQUIRES_SMALLEST_WIDTH_DP_ATTR
, NULL
, 0);
921 compatibleWidthLimitDp
= getIntegerAttribute(tree
,
922 COMPATIBLE_WIDTH_LIMIT_DP_ATTR
, NULL
, 0);
923 largestWidthLimitDp
= getIntegerAttribute(tree
,
924 LARGEST_WIDTH_LIMIT_DP_ATTR
, NULL
, 0);
925 } else if (tag
== "uses-feature") {
926 String8 name
= getAttribute(tree
, NAME_ATTR
, &error
);
928 if (name
!= "" && error
== "") {
929 int req
= getIntegerAttribute(tree
,
930 REQUIRED_ATTR
, NULL
, 1);
932 if (name
== "android.hardware.camera") {
933 specCameraFeature
= true;
934 } else if (name
== "android.hardware.camera.autofocus") {
935 // these have no corresponding permission to check for,
936 // but should imply the foundational camera permission
937 reqCameraAutofocusFeature
= reqCameraAutofocusFeature
|| req
;
938 specCameraAutofocusFeature
= true;
939 } else if (req
&& (name
== "android.hardware.camera.flash")) {
940 // these have no corresponding permission to check for,
941 // but should imply the foundational camera permission
942 reqCameraFlashFeature
= true;
943 } else if (name
== "android.hardware.location") {
944 specLocationFeature
= true;
945 } else if (name
== "android.hardware.location.network") {
946 specNetworkLocFeature
= true;
947 reqNetworkLocFeature
= reqNetworkLocFeature
|| req
;
948 } else if (name
== "android.hardware.location.gps") {
949 specGpsFeature
= true;
950 reqGpsFeature
= reqGpsFeature
|| req
;
951 } else if (name
== "android.hardware.bluetooth") {
952 specBluetoothFeature
= true;
953 } else if (name
== "android.hardware.touchscreen") {
954 specTouchscreenFeature
= true;
955 } else if (name
== "android.hardware.touchscreen.multitouch") {
956 specMultitouchFeature
= true;
957 } else if (name
== "android.hardware.touchscreen.multitouch.distinct") {
958 reqDistinctMultitouchFeature
= reqDistinctMultitouchFeature
|| req
;
959 } else if (name
== "android.hardware.microphone") {
960 specMicrophoneFeature
= true;
961 } else if (name
== "android.hardware.wifi") {
962 specWiFiFeature
= true;
963 } else if (name
== "android.hardware.telephony") {
964 specTelephonyFeature
= true;
965 } else if (req
&& (name
== "android.hardware.telephony.gsm" ||
966 name
== "android.hardware.telephony.cdma")) {
967 // these have no corresponding permission to check for,
968 // but should imply the foundational telephony permission
969 reqTelephonySubFeature
= true;
970 } else if (name
== "android.hardware.screen.portrait") {
971 specScreenPortraitFeature
= true;
972 } else if (name
== "android.hardware.screen.landscape") {
973 specScreenLandscapeFeature
= true;
975 printf("uses-feature%s:'%s'\n",
976 req
? "" : "-not-required", name
.string());
978 int vers
= getIntegerAttribute(tree
,
979 GL_ES_VERSION_ATTR
, &error
);
981 printf("uses-gl-es:'0x%x'\n", vers
);
984 } else if (tag
== "uses-permission") {
985 String8 name
= getAttribute(tree
, NAME_ATTR
, &error
);
986 if (name
!= "" && error
== "") {
987 if (name
== "android.permission.CAMERA") {
988 hasCameraPermission
= true;
989 } else if (name
== "android.permission.ACCESS_FINE_LOCATION") {
990 hasGpsPermission
= true;
991 } else if (name
== "android.permission.ACCESS_MOCK_LOCATION") {
992 hasMockLocPermission
= true;
993 } else if (name
== "android.permission.ACCESS_COARSE_LOCATION") {
994 hasCoarseLocPermission
= true;
995 } else if (name
== "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
996 name
== "android.permission.INSTALL_LOCATION_PROVIDER") {
997 hasGeneralLocPermission
= true;
998 } else if (name
== "android.permission.BLUETOOTH" ||
999 name
== "android.permission.BLUETOOTH_ADMIN") {
1000 hasBluetoothPermission
= true;
1001 } else if (name
== "android.permission.RECORD_AUDIO") {
1002 hasRecordAudioPermission
= true;
1003 } else if (name
== "android.permission.ACCESS_WIFI_STATE" ||
1004 name
== "android.permission.CHANGE_WIFI_STATE" ||
1005 name
== "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1006 hasWiFiPermission
= true;
1007 } else if (name
== "android.permission.CALL_PHONE" ||
1008 name
== "android.permission.CALL_PRIVILEGED" ||
1009 name
== "android.permission.MODIFY_PHONE_STATE" ||
1010 name
== "android.permission.PROCESS_OUTGOING_CALLS" ||
1011 name
== "android.permission.READ_SMS" ||
1012 name
== "android.permission.RECEIVE_SMS" ||
1013 name
== "android.permission.RECEIVE_MMS" ||
1014 name
== "android.permission.RECEIVE_WAP_PUSH" ||
1015 name
== "android.permission.SEND_SMS" ||
1016 name
== "android.permission.WRITE_APN_SETTINGS" ||
1017 name
== "android.permission.WRITE_SMS") {
1018 hasTelephonyPermission
= true;
1019 } else if (name
== "android.permission.WRITE_EXTERNAL_STORAGE") {
1020 hasWriteExternalStoragePermission
= true;
1021 } else if (name
== "android.permission.READ_EXTERNAL_STORAGE") {
1022 hasReadExternalStoragePermission
= true;
1023 } else if (name
== "android.permission.READ_PHONE_STATE") {
1024 hasReadPhoneStatePermission
= true;
1025 } else if (name
== "android.permission.READ_CONTACTS") {
1026 hasReadContactsPermission
= true;
1027 } else if (name
== "android.permission.WRITE_CONTACTS") {
1028 hasWriteContactsPermission
= true;
1029 } else if (name
== "android.permission.READ_CALL_LOG") {
1030 hasReadCallLogPermission
= true;
1031 } else if (name
== "android.permission.WRITE_CALL_LOG") {
1032 hasWriteCallLogPermission
= true;
1034 printf("uses-permission:'%s'\n", name
.string());
1036 fprintf(stderr
, "ERROR getting 'android:name' attribute: %s\n",
1040 } else if (tag
== "uses-package") {
1041 String8 name
= getAttribute(tree
, NAME_ATTR
, &error
);
1042 if (name
!= "" && error
== "") {
1043 printf("uses-package:'%s'\n", name
.string());
1045 fprintf(stderr
, "ERROR getting 'android:name' attribute: %s\n",
1049 } else if (tag
== "original-package") {
1050 String8 name
= getAttribute(tree
, NAME_ATTR
, &error
);
1051 if (name
!= "" && error
== "") {
1052 printf("original-package:'%s'\n", name
.string());
1054 fprintf(stderr
, "ERROR getting 'android:name' attribute: %s\n",
1058 } else if (tag
== "supports-gl-texture") {
1059 String8 name
= getAttribute(tree
, NAME_ATTR
, &error
);
1060 if (name
!= "" && error
== "") {
1061 printf("supports-gl-texture:'%s'\n", name
.string());
1063 fprintf(stderr
, "ERROR getting 'android:name' attribute: %s\n",
1067 } else if (tag
== "compatible-screens") {
1068 printCompatibleScreens(tree
);
1070 } else if (tag
== "package-verifier") {
1071 String8 name
= getAttribute(tree
, NAME_ATTR
, &error
);
1072 if (name
!= "" && error
== "") {
1073 String8 publicKey
= getAttribute(tree
, PUBLIC_KEY_ATTR
, &error
);
1074 if (publicKey
!= "" && error
== "") {
1075 printf("package-verifier: name='%s' publicKey='%s'\n",
1076 name
.string(), publicKey
.string());
1080 } else if (depth
== 3 && withinApplication
) {
1081 withinActivity
= false;
1082 withinReceiver
= false;
1083 withinService
= false;
1084 hasIntentFilter
= false;
1085 if(tag
== "activity") {
1086 withinActivity
= true;
1087 activityName
= getAttribute(tree
, NAME_ATTR
, &error
);
1089 fprintf(stderr
, "ERROR getting 'android:name' attribute: %s\n", error
.string());
1093 activityLabel
= getResolvedAttribute(&res
, tree
, LABEL_ATTR
, &error
);
1095 fprintf(stderr
, "ERROR getting 'android:label' attribute: %s\n", error
.string());
1099 activityIcon
= getResolvedAttribute(&res
, tree
, ICON_ATTR
, &error
);
1101 fprintf(stderr
, "ERROR getting 'android:icon' attribute: %s\n", error
.string());
1105 int32_t orien
= getResolvedIntegerAttribute(&res
, tree
,
1106 SCREEN_ORIENTATION_ATTR
, &error
);
1108 if (orien
== 0 || orien
== 6 || orien
== 8) {
1109 // Requests landscape, sensorLandscape, or reverseLandscape.
1110 reqScreenLandscapeFeature
= true;
1111 } else if (orien
== 1 || orien
== 7 || orien
== 9) {
1112 // Requests portrait, sensorPortrait, or reversePortrait.
1113 reqScreenPortraitFeature
= true;
1116 } else if (tag
== "uses-library") {
1117 String8 libraryName
= getAttribute(tree
, NAME_ATTR
, &error
);
1119 fprintf(stderr
, "ERROR getting 'android:name' attribute for uses-library: %s\n", error
.string());
1122 int req
= getIntegerAttribute(tree
,
1123 REQUIRED_ATTR
, NULL
, 1);
1124 printf("uses-library%s:'%s'\n",
1125 req
? "" : "-not-required", libraryName
.string());
1126 } else if (tag
== "receiver") {
1127 withinReceiver
= true;
1128 receiverName
= getAttribute(tree
, NAME_ATTR
, &error
);
1131 fprintf(stderr
, "ERROR getting 'android:name' attribute for receiver: %s\n", error
.string());
1134 } else if (tag
== "service") {
1135 withinService
= true;
1136 serviceName
= getAttribute(tree
, NAME_ATTR
, &error
);
1139 fprintf(stderr
, "ERROR getting 'android:name' attribute for service: %s\n", error
.string());
1143 } else if ((depth
== 4) && (tag
== "intent-filter")) {
1144 hasIntentFilter
= true;
1145 withinIntentFilter
= true;
1146 actMainActivity
= actWidgetReceivers
= actImeService
= actWallpaperService
= false;
1147 } else if ((depth
== 5) && withinIntentFilter
){
1149 if (tag
== "action") {
1150 action
= getAttribute(tree
, NAME_ATTR
, &error
);
1152 fprintf(stderr
, "ERROR getting 'android:name' attribute: %s\n", error
.string());
1155 if (withinActivity
) {
1156 if (action
== "android.intent.action.MAIN") {
1157 isMainActivity
= true;
1158 actMainActivity
= true;
1160 } else if (withinReceiver
) {
1161 if (action
== "android.appwidget.action.APPWIDGET_UPDATE") {
1162 actWidgetReceivers
= true;
1164 } else if (withinService
) {
1165 if (action
== "android.view.InputMethod") {
1166 actImeService
= true;
1167 } else if (action
== "android.service.wallpaper.WallpaperService") {
1168 actWallpaperService
= true;
1171 if (action
== "android.intent.action.SEARCH") {
1172 isSearchable
= true;
1176 if (tag
== "category") {
1177 String8 category
= getAttribute(tree
, NAME_ATTR
, &error
);
1179 fprintf(stderr
, "ERROR getting 'name' attribute: %s\n", error
.string());
1182 if (withinActivity
) {
1183 if (category
== "android.intent.category.LAUNCHER") {
1184 isLauncherActivity
= true;
1191 // Pre-1.6 implicitly granted permission compatibility logic
1192 if (targetSdk
< 4) {
1193 if (!hasWriteExternalStoragePermission
) {
1194 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
1195 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1196 "'targetSdkVersion < 4'\n");
1197 hasWriteExternalStoragePermission
= true;
1199 if (!hasReadPhoneStatePermission
) {
1200 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
1201 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1202 "'targetSdkVersion < 4'\n");
1206 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1207 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1208 // do this (regardless of target API version) because we can't have
1209 // an app with write permission but not read permission.
1210 if (!hasReadExternalStoragePermission
&& hasWriteExternalStoragePermission
) {
1211 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
1212 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1213 "'requested WRITE_EXTERNAL_STORAGE'\n");
1216 // Pre-JellyBean call log permission compatibility.
1217 if (targetSdk
< 16) {
1218 if (!hasReadCallLogPermission
&& hasReadContactsPermission
) {
1219 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
1220 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1221 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
1223 if (!hasWriteCallLogPermission
&& hasWriteContactsPermission
) {
1224 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
1225 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1226 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
1230 /* The following blocks handle printing "inferred" uses-features, based
1231 * on whether related features or permissions are used by the app.
1232 * Note that the various spec*Feature variables denote whether the
1233 * relevant tag was *present* in the AndroidManfest, not that it was
1234 * present and set to true.
1236 // Camera-related back-compatibility logic
1237 if (!specCameraFeature
) {
1238 if (reqCameraFlashFeature
) {
1239 // if app requested a sub-feature (autofocus or flash) and didn't
1240 // request the base camera feature, we infer that it meant to
1241 printf("uses-feature:'android.hardware.camera'\n");
1242 printf("uses-implied-feature:'android.hardware.camera'," \
1243 "'requested android.hardware.camera.flash feature'\n");
1244 } else if (reqCameraAutofocusFeature
) {
1245 // if app requested a sub-feature (autofocus or flash) and didn't
1246 // request the base camera feature, we infer that it meant to
1247 printf("uses-feature:'android.hardware.camera'\n");
1248 printf("uses-implied-feature:'android.hardware.camera'," \
1249 "'requested android.hardware.camera.autofocus feature'\n");
1250 } else if (hasCameraPermission
) {
1251 // if app wants to use camera but didn't request the feature, we infer
1252 // that it meant to, and further that it wants autofocus
1253 // (which was the 1.0 - 1.5 behavior)
1254 printf("uses-feature:'android.hardware.camera'\n");
1255 if (!specCameraAutofocusFeature
) {
1256 printf("uses-feature:'android.hardware.camera.autofocus'\n");
1257 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1258 "'requested android.permission.CAMERA permission'\n");
1263 // Location-related back-compatibility logic
1264 if (!specLocationFeature
&&
1265 (hasMockLocPermission
|| hasCoarseLocPermission
|| hasGpsPermission
||
1266 hasGeneralLocPermission
|| reqNetworkLocFeature
|| reqGpsFeature
)) {
1267 // if app either takes a location-related permission or requests one of the
1268 // sub-features, we infer that it also meant to request the base location feature
1269 printf("uses-feature:'android.hardware.location'\n");
1270 printf("uses-implied-feature:'android.hardware.location'," \
1271 "'requested a location access permission'\n");
1273 if (!specGpsFeature
&& hasGpsPermission
) {
1274 // if app takes GPS (FINE location) perm but does not request the GPS
1275 // feature, we infer that it meant to
1276 printf("uses-feature:'android.hardware.location.gps'\n");
1277 printf("uses-implied-feature:'android.hardware.location.gps'," \
1278 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
1280 if (!specNetworkLocFeature
&& hasCoarseLocPermission
) {
1281 // if app takes Network location (COARSE location) perm but does not request the
1282 // network location feature, we infer that it meant to
1283 printf("uses-feature:'android.hardware.location.network'\n");
1284 printf("uses-implied-feature:'android.hardware.location.network'," \
1285 "'requested android.permission.ACCESS_COURSE_LOCATION permission'\n");
1288 // Bluetooth-related compatibility logic
1289 if (!specBluetoothFeature
&& hasBluetoothPermission
&& (targetSdk
> 4)) {
1290 // if app takes a Bluetooth permission but does not request the Bluetooth
1291 // feature, we infer that it meant to
1292 printf("uses-feature:'android.hardware.bluetooth'\n");
1293 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1294 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1295 "permission and targetSdkVersion > 4'\n");
1298 // Microphone-related compatibility logic
1299 if (!specMicrophoneFeature
&& hasRecordAudioPermission
) {
1300 // if app takes the record-audio permission but does not request the microphone
1301 // feature, we infer that it meant to
1302 printf("uses-feature:'android.hardware.microphone'\n");
1303 printf("uses-implied-feature:'android.hardware.microphone'," \
1304 "'requested android.permission.RECORD_AUDIO permission'\n");
1307 // WiFi-related compatibility logic
1308 if (!specWiFiFeature
&& hasWiFiPermission
) {
1309 // if app takes one of the WiFi permissions but does not request the WiFi
1310 // feature, we infer that it meant to
1311 printf("uses-feature:'android.hardware.wifi'\n");
1312 printf("uses-implied-feature:'android.hardware.wifi'," \
1313 "'requested android.permission.ACCESS_WIFI_STATE, " \
1314 "android.permission.CHANGE_WIFI_STATE, or " \
1315 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
1318 // Telephony-related compatibility logic
1319 if (!specTelephonyFeature
&& (hasTelephonyPermission
|| reqTelephonySubFeature
)) {
1320 // if app takes one of the telephony permissions or requests a sub-feature but
1321 // does not request the base telephony feature, we infer that it meant to
1322 printf("uses-feature:'android.hardware.telephony'\n");
1323 printf("uses-implied-feature:'android.hardware.telephony'," \
1324 "'requested a telephony-related permission or feature'\n");
1327 // Touchscreen-related back-compatibility logic
1328 if (!specTouchscreenFeature
) { // not a typo!
1329 // all apps are presumed to require a touchscreen, unless they explicitly say
1330 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1331 // Note that specTouchscreenFeature is true if the tag is present, regardless
1332 // of whether its value is true or false, so this is safe
1333 printf("uses-feature:'android.hardware.touchscreen'\n");
1334 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1335 "'assumed you require a touch screen unless explicitly made optional'\n");
1337 if (!specMultitouchFeature
&& reqDistinctMultitouchFeature
) {
1338 // if app takes one of the telephony permissions or requests a sub-feature but
1339 // does not request the base telephony feature, we infer that it meant to
1340 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1341 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1342 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
1345 // Landscape/portrait-related compatibility logic
1346 if (!specScreenLandscapeFeature
&& !specScreenPortraitFeature
) {
1347 // If the app has specified any activities in its manifest
1348 // that request a specific orientation, then assume that
1349 // orientation is required.
1350 if (reqScreenLandscapeFeature
) {
1351 printf("uses-feature:'android.hardware.screen.landscape'\n");
1352 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1353 "'one or more activities have specified a landscape orientation'\n");
1355 if (reqScreenPortraitFeature
) {
1356 printf("uses-feature:'android.hardware.screen.portrait'\n");
1357 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1358 "'one or more activities have specified a portrait orientation'\n");
1362 if (hasMainActivity
) {
1365 if (hasWidgetReceivers
) {
1366 printf("app-widget\n");
1368 if (hasImeService
) {
1371 if (hasWallpaperService
) {
1372 printf("wallpaper\n");
1374 if (hasOtherActivities
) {
1375 printf("other-activities\n");
1380 if (hasOtherReceivers
) {
1381 printf("other-receivers\n");
1383 if (hasOtherServices
) {
1384 printf("other-services\n");
1387 // For modern apps, if screen size buckets haven't been specified
1388 // but the new width ranges have, then infer the buckets from them.
1389 if (smallScreen
> 0 && normalScreen
> 0 && largeScreen
> 0 && xlargeScreen
> 0
1390 && requiresSmallestWidthDp
> 0) {
1391 int compatWidth
= compatibleWidthLimitDp
;
1392 if (compatWidth
<= 0) compatWidth
= requiresSmallestWidthDp
;
1393 if (requiresSmallestWidthDp
<= 240 && compatWidth
>= 240) {
1398 if (requiresSmallestWidthDp
<= 320 && compatWidth
>= 320) {
1403 if (requiresSmallestWidthDp
<= 480 && compatWidth
>= 480) {
1408 if (requiresSmallestWidthDp
<= 720 && compatWidth
>= 720) {
1415 // Determine default values for any unspecified screen sizes,
1416 // based on the target SDK of the package. As of 4 (donut)
1417 // the screen size support was introduced, so all default to
1419 if (smallScreen
> 0) {
1420 smallScreen
= targetSdk
>= 4 ? -1 : 0;
1422 if (normalScreen
> 0) {
1425 if (largeScreen
> 0) {
1426 largeScreen
= targetSdk
>= 4 ? -1 : 0;
1428 if (xlargeScreen
> 0) {
1429 // Introduced in Gingerbread.
1430 xlargeScreen
= targetSdk
>= 9 ? -1 : 0;
1432 if (anyDensity
> 0) {
1433 anyDensity
= (targetSdk
>= 4 || requiresSmallestWidthDp
> 0
1434 || compatibleWidthLimitDp
> 0) ? -1 : 0;
1436 printf("supports-screens:");
1437 if (smallScreen
!= 0) printf(" 'small'");
1438 if (normalScreen
!= 0) printf(" 'normal'");
1439 if (largeScreen
!= 0) printf(" 'large'");
1440 if (xlargeScreen
!= 0) printf(" 'xlarge'");
1442 printf("supports-any-density: '%s'\n", anyDensity
? "true" : "false");
1443 if (requiresSmallestWidthDp
> 0) {
1444 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp
);
1446 if (compatibleWidthLimitDp
> 0) {
1447 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp
);
1449 if (largestWidthLimitDp
> 0) {
1450 printf("largest-width-limit:'%d'\n", largestWidthLimitDp
);
1454 const size_t NL
= locales
.size();
1455 for (size_t i
=0; i
<NL
; i
++) {
1456 const char* localeStr
= locales
[i
].string();
1457 if (localeStr
== NULL
|| strlen(localeStr
) == 0) {
1458 localeStr
= "--_--";
1460 printf(" '%s'", localeStr
);
1464 printf("densities:");
1465 const size_t ND
= densities
.size();
1466 for (size_t i
=0; i
<ND
; i
++) {
1467 printf(" '%d'", densities
[i
]);
1471 AssetDir
* dir
= assets
.openNonAssetDir(assetsCookie
, "lib");
1473 if (dir
->getFileCount() > 0) {
1474 printf("native-code:");
1475 for (size_t i
=0; i
<dir
->getFileCount(); i
++) {
1476 printf(" '%s'", dir
->getFileName(i
).string());
1482 } else if (strcmp("badger", option
) == 0) {
1483 printf("%s", CONSOLE_DATA
);
1484 } else if (strcmp("configurations", option
) == 0) {
1485 Vector
<ResTable_config
> configs
;
1486 res
.getConfigurations(&configs
);
1487 const size_t N
= configs
.size();
1488 for (size_t i
=0; i
<N
; i
++) {
1489 printf("%s\n", configs
[i
].toString().string());
1492 fprintf(stderr
, "ERROR: unknown dump option '%s'\n", option
);
1503 return (result
!= NO_ERROR
);
1508 * Handle the "add" command, which wants to add files to a new or
1509 * pre-existing archive.
1511 int doAdd(Bundle
* bundle
)
1513 ZipFile
* zip
= NULL
;
1514 status_t result
= UNKNOWN_ERROR
;
1515 const char* zipFileName
;
1517 if (bundle
->getUpdate()) {
1518 /* avoid confusion */
1519 fprintf(stderr
, "ERROR: can't use '-u' with add\n");
1523 if (bundle
->getFileSpecCount() < 1) {
1524 fprintf(stderr
, "ERROR: must specify zip file name\n");
1527 zipFileName
= bundle
->getFileSpecEntry(0);
1529 if (bundle
->getFileSpecCount() < 2) {
1530 fprintf(stderr
, "NOTE: nothing to do\n");
1534 zip
= openReadWrite(zipFileName
, true);
1536 fprintf(stderr
, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName
);
1540 for (int i
= 1; i
< bundle
->getFileSpecCount(); i
++) {
1541 const char* fileName
= bundle
->getFileSpecEntry(i
);
1543 if (strcasecmp(String8(fileName
).getPathExtension().string(), ".gz") == 0) {
1544 printf(" '%s'... (from gzip)\n", fileName
);
1545 result
= zip
->addGzip(fileName
, String8(fileName
).getBasePath().string(), NULL
);
1547 if (bundle
->getJunkPath()) {
1548 String8 storageName
= String8(fileName
).getPathLeaf();
1549 printf(" '%s' as '%s'...\n", fileName
, storageName
.string());
1550 result
= zip
->add(fileName
, storageName
.string(),
1551 bundle
->getCompressionMethod(), NULL
);
1553 printf(" '%s'...\n", fileName
);
1554 result
= zip
->add(fileName
, bundle
->getCompressionMethod(), NULL
);
1557 if (result
!= NO_ERROR
) {
1558 fprintf(stderr
, "Unable to add '%s' to '%s'", bundle
->getFileSpecEntry(i
), zipFileName
);
1559 if (result
== NAME_NOT_FOUND
)
1560 fprintf(stderr
, ": file not found\n");
1561 else if (result
== ALREADY_EXISTS
)
1562 fprintf(stderr
, ": already exists in archive\n");
1564 fprintf(stderr
, "\n");
1573 return (result
!= NO_ERROR
);
1578 * Delete files from an existing archive.
1580 int doRemove(Bundle
* bundle
)
1582 ZipFile
* zip
= NULL
;
1583 status_t result
= UNKNOWN_ERROR
;
1584 const char* zipFileName
;
1586 if (bundle
->getFileSpecCount() < 1) {
1587 fprintf(stderr
, "ERROR: must specify zip file name\n");
1590 zipFileName
= bundle
->getFileSpecEntry(0);
1592 if (bundle
->getFileSpecCount() < 2) {
1593 fprintf(stderr
, "NOTE: nothing to do\n");
1597 zip
= openReadWrite(zipFileName
, false);
1599 fprintf(stderr
, "ERROR: failed opening Zip archive '%s'\n",
1604 for (int i
= 1; i
< bundle
->getFileSpecCount(); i
++) {
1605 const char* fileName
= bundle
->getFileSpecEntry(i
);
1608 entry
= zip
->getEntryByName(fileName
);
1609 if (entry
== NULL
) {
1610 printf(" '%s' NOT FOUND\n", fileName
);
1614 result
= zip
->remove(entry
);
1616 if (result
!= NO_ERROR
) {
1617 fprintf(stderr
, "Unable to delete '%s' from '%s'\n",
1618 bundle
->getFileSpecEntry(i
), zipFileName
);
1623 /* update the archive */
1628 return (result
!= NO_ERROR
);
1633 * Package up an asset directory and associated application files.
1635 int doPackage(Bundle
* bundle
)
1637 const char* outputAPKFile
;
1640 sp
<AaptAssets
> assets
;
1643 String8 dependencyFile
;
1645 // -c zz_ZZ means do pseudolocalization
1646 ResourceFilter filter
;
1647 err
= filter
.parse(bundle
->getConfigurations());
1648 if (err
!= NO_ERROR
) {
1651 if (filter
.containsPseudo()) {
1652 bundle
->setPseudolocalize(true);
1655 N
= bundle
->getFileSpecCount();
1656 if (N
< 1 && bundle
->getResourceSourceDirs().size() == 0 && bundle
->getJarFiles().size() == 0
1657 && bundle
->getAndroidManifestFile() == NULL
&& bundle
->getAssetSourceDir() == NULL
) {
1658 fprintf(stderr
, "ERROR: no input files\n");
1662 outputAPKFile
= bundle
->getOutputAPKFile();
1664 // Make sure the filenames provided exist and are of the appropriate type.
1665 if (outputAPKFile
) {
1667 type
= getFileType(outputAPKFile
);
1668 if (type
!= kFileTypeNonexistent
&& type
!= kFileTypeRegular
) {
1670 "ERROR: output file '%s' exists but is not regular file\n",
1677 assets
= new AaptAssets();
1679 // Set up the resource gathering in assets if we're going to generate
1680 // dependency files. Every time we encounter a resource while slurping
1681 // the tree, we'll add it to these stores so we have full resource paths
1682 // to write to a dependency file.
1683 if (bundle
->getGenDependencies()) {
1684 sp
<FilePathStore
> resPathStore
= new FilePathStore
;
1685 assets
->setFullResPaths(resPathStore
);
1686 sp
<FilePathStore
> assetPathStore
= new FilePathStore
;
1687 assets
->setFullAssetPaths(assetPathStore
);
1690 err
= assets
->slurpFromArgs(bundle
);
1695 if (bundle
->getVerbose()) {
1696 assets
->print(String8());
1699 // If they asked for any fileAs that need to be compiled, do so.
1700 if (bundle
->getResourceSourceDirs().size() || bundle
->getAndroidManifestFile()) {
1701 err
= buildResources(bundle
, assets
);
1707 // At this point we've read everything and processed everything. From here
1708 // on out it's just writing output files.
1709 if (SourcePos::hasErrors()) {
1713 // Update symbols with information about which ones are needed as Java symbols.
1714 assets
->applyJavaSymbols();
1715 if (SourcePos::hasErrors()) {
1719 // If we've been asked to generate a dependency file, do that here
1720 if (bundle
->getGenDependencies()) {
1721 // If this is the packaging step, generate the dependency file next to
1722 // the output apk (e.g. bin/resources.ap_.d)
1723 if (outputAPKFile
) {
1724 dependencyFile
= String8(outputAPKFile
);
1725 // Add the .d extension to the dependency file.
1726 dependencyFile
.append(".d");
1728 // Else if this is the R.java dependency generation step,
1729 // generate the dependency file in the R.java package subdirectory
1730 // e.g. gen/com/foo/app/R.java.d
1731 dependencyFile
= String8(bundle
->getRClassDir());
1732 dependencyFile
.appendPath("R.java.d");
1734 // Make sure we have a clean dependency file to start with
1735 fp
= fopen(dependencyFile
, "w");
1739 // Write out R.java constants
1740 if (!assets
->havePrivateSymbols()) {
1741 if (bundle
->getCustomPackage() == NULL
) {
1742 // Write the R.java file into the appropriate class directory
1743 // e.g. gen/com/foo/app/R.java
1744 err
= writeResourceSymbols(bundle
, assets
, assets
->getPackage(), true);
1746 const String8
customPkg(bundle
->getCustomPackage());
1747 err
= writeResourceSymbols(bundle
, assets
, customPkg
, true);
1752 // If we have library files, we're going to write our R.java file into
1753 // the appropriate class directory for those libraries as well.
1754 // e.g. gen/com/foo/app/lib/R.java
1755 if (bundle
->getExtraPackages() != NULL
) {
1757 String8
libs(bundle
->getExtraPackages());
1758 char* packageString
= strtok(libs
.lockBuffer(libs
.length()), ":");
1759 while (packageString
!= NULL
) {
1760 // Write the R.java file out with the correct package name
1761 err
= writeResourceSymbols(bundle
, assets
, String8(packageString
), true);
1765 packageString
= strtok(NULL
, ":");
1767 libs
.unlockBuffer();
1770 err
= writeResourceSymbols(bundle
, assets
, assets
->getPackage(), false);
1774 err
= writeResourceSymbols(bundle
, assets
, assets
->getSymbolsPrivatePackage(), true);
1780 // Write out the ProGuard file
1781 err
= writeProguardFile(bundle
, assets
);
1787 if (outputAPKFile
) {
1788 err
= writeAPK(bundle
, assets
, String8(outputAPKFile
));
1789 if (err
!= NO_ERROR
) {
1790 fprintf(stderr
, "ERROR: packaging of '%s' failed\n", outputAPKFile
);
1795 // If we've been asked to generate a dependency file, we need to finish up here.
1796 // the writeResourceSymbols and writeAPK functions have already written the target
1797 // half of the dependency file, now we need to write the prerequisites. (files that
1798 // the R.java file or .ap_ file depend on)
1799 if (bundle
->getGenDependencies()) {
1800 // Now that writeResourceSymbols or writeAPK has taken care of writing
1801 // the targets to our dependency file, we'll write the prereqs
1802 fp
= fopen(dependencyFile
, "a+");
1804 bool includeRaw
= (outputAPKFile
!= NULL
);
1805 err
= writeDependencyPreReqs(bundle
, assets
, fp
, includeRaw
);
1806 // Also manually add the AndroidManifeset since it's not under res/ or assets/
1807 // and therefore was not added to our pathstores during slurping
1808 fprintf(fp
, "%s \\\n", bundle
->getAndroidManifestFile());
1814 if (SourcePos::hasErrors()) {
1815 SourcePos::printErrors(stderr
);
1823 * -S flag points to a source directory containing drawable* folders
1824 * -C flag points to destination directory. The folder structure in the
1825 * source directory will be mirrored to the destination (cache) directory
1828 * Destination directory will be updated to match the PNG files in
1829 * the source directory.
1831 int doCrunch(Bundle
* bundle
)
1833 fprintf(stdout
, "Crunching PNG Files in ");
1834 fprintf(stdout
, "source dir: %s\n", bundle
->getResourceSourceDirs()[0]);
1835 fprintf(stdout
, "To destination dir: %s\n", bundle
->getCrunchedOutputDir());
1837 updatePreProcessedCache(bundle
);
1843 * Do PNG Crunching on a single flag
1844 * -i points to a single png file
1845 * -o points to a single png output file
1847 int doSingleCrunch(Bundle
* bundle
)
1849 fprintf(stdout
, "Crunching single PNG file: %s\n", bundle
->getSingleCrunchInputFile());
1850 fprintf(stdout
, "\tOutput file: %s\n", bundle
->getSingleCrunchOutputFile());
1852 String8
input(bundle
->getSingleCrunchInputFile());
1853 String8
output(bundle
->getSingleCrunchOutputFile());
1854 if (preProcessImageToCache(bundle
, input
, output
) != NO_ERROR
) {
1855 // we can't return the status_t as it gets truncate to the lower 8 bits.
1861 char CONSOLE_DATA
[2925] = {
1862 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1863 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1864 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1865 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1866 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1867 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1868 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1869 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1870 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1871 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1872 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1873 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1874 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1875 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1876 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1877 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1878 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1879 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1880 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1881 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1882 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1883 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1884 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1885 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1886 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1887 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1888 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1889 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1890 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1891 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1892 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1893 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1894 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1895 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1896 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1897 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1898 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1899 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1900 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1901 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1902 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1903 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
1904 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1905 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1906 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
1907 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
1908 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1909 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
1910 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
1911 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1912 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
1913 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
1914 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
1915 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1916 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1917 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
1918 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
1919 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
1920 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1921 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
1922 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
1923 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
1924 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
1925 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1926 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1927 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
1928 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
1929 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
1930 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
1931 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
1932 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
1933 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
1934 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1935 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
1936 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1937 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
1938 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1939 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
1940 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
1941 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1942 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
1943 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
1944 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
1945 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1946 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1947 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
1948 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1949 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
1950 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
1951 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1952 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1953 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
1954 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
1955 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1956 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1957 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1958 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1959 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1960 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
1961 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
1962 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1963 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1964 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1965 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
1966 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1967 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1968 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
1969 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1970 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1971 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
1972 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
1973 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1974 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1975 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1976 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1977 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1978 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
1979 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
1980 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1981 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1982 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
1983 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
1984 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1985 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1986 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
1987 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
1988 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
1989 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
1990 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
1991 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1992 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1993 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
1994 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
1995 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
1996 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
1997 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
1998 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1999 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2000 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2001 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2002 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2003 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2004 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2005 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2006 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2007 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2008 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2009 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2010 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2011 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2012 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2013 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2014 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2015 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2016 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2017 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2018 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2019 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2020 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2021 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2022 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2023 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2024 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10