]> git.saurik.com Git - android/aapt.git/blame_incremental - Command.cpp
Apply --extra-packages in case --custom-pacakge is also specified.
[android/aapt.git] / Command.cpp
... / ...
CommitLineData
1//
2// Copyright 2006 The Android Open Source Project
3//
4// Android Asset Packaging Tool main entry point.
5//
6#include "Main.h"
7#include "Bundle.h"
8#include "ResourceFilter.h"
9#include "ResourceTable.h"
10#include "XMLNode.h"
11
12#include <utils/Log.h>
13#include <utils/threads.h>
14#include <utils/List.h>
15#include <utils/Errors.h>
16
17#include <fcntl.h>
18#include <errno.h>
19
20using namespace android;
21
22/*
23 * Show version info. All the cool kids do it.
24 */
25int doVersion(Bundle* bundle)
26{
27 if (bundle->getFileSpecCount() != 0)
28 printf("(ignoring extra arguments)\n");
29 printf("Android Asset Packaging Tool, v0.2\n");
30
31 return 0;
32}
33
34
35/*
36 * Open the file read only. The call fails if the file doesn't exist.
37 *
38 * Returns NULL on failure.
39 */
40ZipFile* openReadOnly(const char* fileName)
41{
42 ZipFile* zip;
43 status_t result;
44
45 zip = new ZipFile;
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);
52 else
53 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
54 fileName);
55 delete zip;
56 return NULL;
57 }
58
59 return zip;
60}
61
62/*
63 * Open the file read-write. The file will be created if it doesn't
64 * already exist and "okayToCreate" is set.
65 *
66 * Returns NULL on failure.
67 */
68ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
69{
70 ZipFile* zip = NULL;
71 status_t result;
72 int flags;
73
74 flags = ZipFile::kOpenReadWrite;
75 if (okayToCreate)
76 flags |= ZipFile::kOpenCreate;
77
78 zip = new ZipFile;
79 result = zip->open(fileName, flags);
80 if (result != NO_ERROR) {
81 delete zip;
82 zip = NULL;
83 goto bail;
84 }
85
86bail:
87 return zip;
88}
89
90
91/*
92 * Return a short string describing the compression method.
93 */
94const char* compressionName(int method)
95{
96 if (method == ZipEntry::kCompressStored)
97 return "Stored";
98 else if (method == ZipEntry::kCompressDeflated)
99 return "Deflated";
100 else
101 return "Unknown";
102}
103
104/*
105 * Return the percent reduction in size (0% == no compression).
106 */
107int calcPercent(long uncompressedLen, long compressedLen)
108{
109 if (!uncompressedLen)
110 return 0;
111 else
112 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
113}
114
115/*
116 * Handle the "list" command, which can be a simple file dump or
117 * a verbose listing.
118 *
119 * The verbose listing closely matches the output of the Info-ZIP "unzip"
120 * command.
121 */
122int doList(Bundle* bundle)
123{
124 int result = 1;
125 ZipFile* zip = NULL;
126 const ZipEntry* entry;
127 long totalUncLen, totalCompLen;
128 const char* zipFileName;
129
130 if (bundle->getFileSpecCount() != 1) {
131 fprintf(stderr, "ERROR: specify zip file name (only)\n");
132 goto bail;
133 }
134 zipFileName = bundle->getFileSpecEntry(0);
135
136 zip = openReadOnly(zipFileName);
137 if (zip == NULL)
138 goto bail;
139
140 int count, i;
141
142 if (bundle->getVerbose()) {
143 printf("Archive: %s\n", zipFileName);
144 printf(
145 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
146 printf(
147 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
148 }
149
150 totalUncLen = totalCompLen = 0;
151
152 count = zip->getNumEntries();
153 for (i = 0; i < count; i++) {
154 entry = zip->getEntryByIndex(i);
155 if (bundle->getVerbose()) {
156 char dateBuf[32];
157 time_t when;
158
159 when = entry->getModWhen();
160 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
161 localtime(&when));
162
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(),
170 dateBuf,
171 entry->getCRC32(),
172 entry->getFileName());
173 } else {
174 printf("%s\n", entry->getFileName());
175 }
176
177 totalUncLen += entry->getUncompressedLen();
178 totalCompLen += entry->getCompressedLen();
179 }
180
181 if (bundle->getVerbose()) {
182 printf(
183 "-------- ------- --- -------\n");
184 printf("%8ld %7ld %2d%% %d files\n",
185 totalUncLen,
186 totalCompLen,
187 calcPercent(totalUncLen, totalCompLen),
188 zip->getNumEntries());
189 }
190
191 if (bundle->getAndroidList()) {
192 AssetManager assets;
193 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
194 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
195 goto bail;
196 }
197
198 const ResTable& res = assets.getResources(false);
199 if (&res == NULL) {
200 printf("\nNo resource table found.\n");
201 } else {
202#ifndef HAVE_ANDROID_OS
203 printf("\nResource table:\n");
204 res.print(false);
205#endif
206 }
207
208 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
209 Asset::ACCESS_BUFFER);
210 if (manifestAsset == NULL) {
211 printf("\nNo AndroidManifest.xml found.\n");
212 } else {
213 printf("\nAndroid manifest:\n");
214 ResXMLTree tree;
215 tree.setTo(manifestAsset->getBuffer(true),
216 manifestAsset->getLength());
217 printXMLBlock(&tree);
218 }
219 delete manifestAsset;
220 }
221
222 result = 0;
223
224bail:
225 delete zip;
226 return result;
227}
228
229static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
230{
231 size_t N = tree.getAttributeCount();
232 for (size_t i=0; i<N; i++) {
233 if (tree.getAttributeNameResID(i) == attrRes) {
234 return (ssize_t)i;
235 }
236 }
237 return -1;
238}
239
240String8 getAttribute(const ResXMLTree& tree, const char* ns,
241 const char* attr, String8* outError)
242{
243 ssize_t idx = tree.indexOfAttribute(ns, attr);
244 if (idx < 0) {
245 return String8();
246 }
247 Res_value value;
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";
251 return String8();
252 }
253 }
254 size_t len;
255 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
256 return str ? String8(str, len) : String8();
257}
258
259static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
260{
261 ssize_t idx = indexOfAttribute(tree, attrRes);
262 if (idx < 0) {
263 return String8();
264 }
265 Res_value value;
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";
269 return String8();
270 }
271 }
272 size_t len;
273 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
274 return str ? String8(str, len) : String8();
275}
276
277static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
278 String8* outError, int32_t defValue = -1)
279{
280 ssize_t idx = indexOfAttribute(tree, attrRes);
281 if (idx < 0) {
282 return defValue;
283 }
284 Res_value value;
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";
289 return defValue;
290 }
291 }
292 return value.data;
293}
294
295static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
296 uint32_t attrRes, String8* outError, int32_t defValue = -1)
297{
298 ssize_t idx = indexOfAttribute(tree, attrRes);
299 if (idx < 0) {
300 return defValue;
301 }
302 Res_value value;
303 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
304 if (value.dataType == Res_value::TYPE_REFERENCE) {
305 resTable->resolveReference(&value, 0);
306 }
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";
310 return defValue;
311 }
312 }
313 return value.data;
314}
315
316static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
317 uint32_t attrRes, String8* outError)
318{
319 ssize_t idx = indexOfAttribute(tree, attrRes);
320 if (idx < 0) {
321 return String8();
322 }
323 Res_value value;
324 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
325 if (value.dataType == Res_value::TYPE_STRING) {
326 size_t len;
327 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
328 return str ? String8(str, len) : String8();
329 }
330 resTable->resolveReference(&value, 0);
331 if (value.dataType != Res_value::TYPE_STRING) {
332 if (outError != NULL) *outError = "attribute is not a string value";
333 return String8();
334 }
335 }
336 size_t len;
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();
340}
341
342// These are attribute resource constants for the platform, as found
343// in android.R.attr
344enum {
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,
374};
375
376const char *getComponentName(String8 &pkgName, String8 &componentName) {
377 ssize_t idx = componentName.find(".");
378 String8 retStr(pkgName);
379 if (idx == 0) {
380 retStr += componentName;
381 } else if (idx < 0) {
382 retStr += ".";
383 retStr += componentName;
384 } else {
385 return componentName.string();
386 }
387 return retStr.string();
388}
389
390static void printCompatibleScreens(ResXMLTree& tree) {
391 size_t len;
392 ResXMLTree::event_code_t code;
393 int depth = 0;
394 bool first = true;
395 printf("compatible-screens:");
396 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
397 if (code == ResXMLTree::END_TAG) {
398 depth--;
399 if (depth < 0) {
400 break;
401 }
402 continue;
403 }
404 if (code != ResXMLTree::START_TAG) {
405 continue;
406 }
407 depth++;
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) {
415 if (!first) {
416 printf(",");
417 }
418 first = false;
419 printf("'%d/%d'", screenSize, screenDensity);
420 }
421 }
422 }
423 printf("\n");
424}
425
426/*
427 * Handle the "dump" command, to extract select data from an archive.
428 */
429extern char CONSOLE_DATA[2925]; // see EOF
430int doDump(Bundle* bundle)
431{
432 status_t result = UNKNOWN_ERROR;
433 Asset* asset = NULL;
434
435 if (bundle->getFileSpecCount() < 1) {
436 fprintf(stderr, "ERROR: no dump option specified\n");
437 return 1;
438 }
439
440 if (bundle->getFileSpecCount() < 2) {
441 fprintf(stderr, "ERROR: no dump file specified\n");
442 return 1;
443 }
444
445 const char* option = bundle->getFileSpecEntry(0);
446 const char* filename = bundle->getFileSpecEntry(1);
447
448 AssetManager assets;
449 void* assetsCookie;
450 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
451 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
452 return 1;
453 }
454
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);
472
473 const ResTable& res = assets.getResources(false);
474 if (&res == NULL) {
475 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
476 goto bail;
477 }
478
479 if (strcmp("resources", option) == 0) {
480#ifndef HAVE_ANDROID_OS
481 res.print(bundle->getValues());
482#endif
483
484 } else if (strcmp("strings", option) == 0) {
485 const ResStringPool* pool = res.getTableStringBlock(0);
486 printStringPool(pool);
487
488 } else if (strcmp("xmltree", option) == 0) {
489 if (bundle->getFileSpecCount() < 3) {
490 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
491 goto bail;
492 }
493
494 for (int i=2; i<bundle->getFileSpecCount(); i++) {
495 const char* resname = bundle->getFileSpecEntry(i);
496 ResXMLTree tree;
497 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
498 if (asset == NULL) {
499 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
500 goto bail;
501 }
502
503 if (tree.setTo(asset->getBuffer(true),
504 asset->getLength()) != NO_ERROR) {
505 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
506 goto bail;
507 }
508 tree.restart();
509 printXMLBlock(&tree);
510 tree.uninit();
511 delete asset;
512 asset = NULL;
513 }
514
515 } else if (strcmp("xmlstrings", option) == 0) {
516 if (bundle->getFileSpecCount() < 3) {
517 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
518 goto bail;
519 }
520
521 for (int i=2; i<bundle->getFileSpecCount(); i++) {
522 const char* resname = bundle->getFileSpecEntry(i);
523 ResXMLTree tree;
524 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
525 if (asset == NULL) {
526 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
527 goto bail;
528 }
529
530 if (tree.setTo(asset->getBuffer(true),
531 asset->getLength()) != NO_ERROR) {
532 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
533 goto bail;
534 }
535 printStringPool(&tree.getStrings());
536 delete asset;
537 asset = NULL;
538 }
539
540 } else {
541 ResXMLTree tree;
542 asset = assets.openNonAsset("AndroidManifest.xml",
543 Asset::ACCESS_BUFFER);
544 if (asset == NULL) {
545 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
546 goto bail;
547 }
548
549 if (tree.setTo(asset->getBuffer(true),
550 asset->getLength()) != NO_ERROR) {
551 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
552 goto bail;
553 }
554 tree.restart();
555
556 if (strcmp("permissions", option) == 0) {
557 size_t len;
558 ResXMLTree::event_code_t code;
559 int depth = 0;
560 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
561 if (code == ResXMLTree::END_TAG) {
562 depth--;
563 continue;
564 }
565 if (code != ResXMLTree::START_TAG) {
566 continue;
567 }
568 depth++;
569 String8 tag(tree.getElementName(&len));
570 //printf("Depth %d tag %s\n", depth, tag.string());
571 if (depth == 1) {
572 if (tag != "manifest") {
573 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
574 goto bail;
575 }
576 String8 pkg = getAttribute(tree, NULL, "package", NULL);
577 printf("package: %s\n", pkg.string());
578 } else if (depth == 2 && tag == "permission") {
579 String8 error;
580 String8 name = getAttribute(tree, NAME_ATTR, &error);
581 if (error != "") {
582 fprintf(stderr, "ERROR: %s\n", error.string());
583 goto bail;
584 }
585 printf("permission: %s\n", name.string());
586 } else if (depth == 2 && tag == "uses-permission") {
587 String8 error;
588 String8 name = getAttribute(tree, NAME_ATTR, &error);
589 if (error != "") {
590 fprintf(stderr, "ERROR: %s\n", error.string());
591 goto bail;
592 }
593 printf("uses-permission: %s\n", name.string());
594 }
595 }
596 } else if (strcmp("badging", option) == 0) {
597 Vector<String8> locales;
598 res.getLocales(&locales);
599
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;
607 densities.add(dens);
608 }
609
610 size_t len;
611 ResXMLTree::event_code_t code;
612 int depth = 0;
613 String8 error;
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;
634
635 // These two implement the implicit permissions that are granted
636 // to pre-1.6 applications.
637 bool hasWriteExternalStoragePermission = false;
638 bool hasReadPhoneStatePermission = false;
639
640 // If an app requests write storage, they will also get read storage.
641 bool hasReadExternalStoragePermission = false;
642
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;
648
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;
693
694 int targetSdk = 0;
695 int smallScreen = 1;
696 int normalScreen = 1;
697 int largeScreen = 1;
698 int xlargeScreen = 1;
699 int anyDensity = 1;
700 int requiresSmallestWidthDp = 0;
701 int compatibleWidthLimitDp = 0;
702 int largestWidthLimitDp = 0;
703 String8 pkg;
704 String8 activityName;
705 String8 activityLabel;
706 String8 activityIcon;
707 String8 receiverName;
708 String8 serviceName;
709 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
710 if (code == ResXMLTree::END_TAG) {
711 depth--;
712 if (depth < 2) {
713 withinApplication = false;
714 } else if (depth < 3) {
715 if (withinActivity && isMainActivity && isLauncherActivity) {
716 const char *aName = getComponentName(pkg, activityName);
717 printf("launchable-activity:");
718 if (aName != NULL) {
719 printf(" name='%s' ", aName);
720 }
721 printf(" label='%s' icon='%s'\n",
722 activityLabel.string(),
723 activityIcon.string());
724 }
725 if (!hasIntentFilter) {
726 hasOtherActivities |= withinActivity;
727 hasOtherReceivers |= withinReceiver;
728 hasOtherServices |= withinService;
729 }
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);
747 }
748 }
749 withinIntentFilter = false;
750 }
751 continue;
752 }
753 if (code != ResXMLTree::START_TAG) {
754 continue;
755 }
756 depth++;
757 String8 tag(tree.getElementName(&len));
758 //printf("Depth %d, %s\n", depth, tag.string());
759 if (depth == 1) {
760 if (tag != "manifest") {
761 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
762 goto bail;
763 }
764 pkg = getAttribute(tree, NULL, "package", NULL);
765 printf("package: name='%s' ", pkg.string());
766 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
767 if (error != "") {
768 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
769 goto bail;
770 }
771 if (versionCode > 0) {
772 printf("versionCode='%d' ", versionCode);
773 } else {
774 printf("versionCode='' ");
775 }
776 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
777 if (error != "") {
778 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
779 goto bail;
780 }
781 printf("versionName='%s'\n", versionName.string());
782 } else if (depth == 2) {
783 withinApplication = false;
784 if (tag == "application") {
785 withinApplication = true;
786
787 String8 label;
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);
793 if (llabel != "") {
794 if (localeStr == NULL || strlen(localeStr) == 0) {
795 label = llabel;
796 printf("application-label:'%s'\n", llabel.string());
797 } else {
798 if (label == "") {
799 label = llabel;
800 }
801 printf("application-label-%s:'%s'\n", localeStr,
802 llabel.string());
803 }
804 }
805 }
806
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);
813 if (icon != "") {
814 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
815 }
816 }
817 assets.setConfiguration(config);
818
819 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
820 if (error != "") {
821 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
822 goto bail;
823 }
824 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
825 if (error != "") {
826 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
827 goto bail;
828 }
829 printf("application: label='%s' ", label.string());
830 printf("icon='%s'\n", icon.string());
831 if (testOnly != 0) {
832 printf("testOnly='%d'\n", testOnly);
833 }
834
835 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
836 if (error != "") {
837 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
838 goto bail;
839 }
840 if (debuggable != 0) {
841 printf("application-debuggable\n");
842 }
843 } else if (tag == "uses-sdk") {
844 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
845 if (error != "") {
846 error = "";
847 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
848 if (error != "") {
849 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
850 error.string());
851 goto bail;
852 }
853 if (name == "Donut") targetSdk = 4;
854 printf("sdkVersion:'%s'\n", name.string());
855 } else if (code != -1) {
856 targetSdk = code;
857 printf("sdkVersion:'%d'\n", code);
858 }
859 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
860 if (code != -1) {
861 printf("maxSdkVersion:'%d'\n", code);
862 }
863 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
864 if (error != "") {
865 error = "";
866 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
867 if (error != "") {
868 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
869 error.string());
870 goto bail;
871 }
872 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
873 printf("targetSdkVersion:'%s'\n", name.string());
874 } else if (code != -1) {
875 if (targetSdk < code) {
876 targetSdk = code;
877 }
878 printf("targetSdkVersion:'%d'\n", code);
879 }
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);
894 }
895 if (reqKeyboardType != 0) {
896 printf(" reqKeyboardType='%d'", reqKeyboardType);
897 }
898 if (reqHardKeyboard != 0) {
899 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
900 }
901 if (reqNavigation != 0) {
902 printf(" reqNavigation='%d'", reqNavigation);
903 }
904 if (reqFiveWayNav != 0) {
905 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
906 }
907 printf("\n");
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);
927
928 if (name != "" && error == "") {
929 int req = getIntegerAttribute(tree,
930 REQUIRED_ATTR, NULL, 1);
931
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;
974 }
975 printf("uses-feature%s:'%s'\n",
976 req ? "" : "-not-required", name.string());
977 } else {
978 int vers = getIntegerAttribute(tree,
979 GL_ES_VERSION_ATTR, &error);
980 if (error == "") {
981 printf("uses-gl-es:'0x%x'\n", vers);
982 }
983 }
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;
1033 }
1034 printf("uses-permission:'%s'\n", name.string());
1035 } else {
1036 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1037 error.string());
1038 goto bail;
1039 }
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());
1044 } else {
1045 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1046 error.string());
1047 goto bail;
1048 }
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());
1053 } else {
1054 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1055 error.string());
1056 goto bail;
1057 }
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());
1062 } else {
1063 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1064 error.string());
1065 goto bail;
1066 }
1067 } else if (tag == "compatible-screens") {
1068 printCompatibleScreens(tree);
1069 depth--;
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());
1077 }
1078 }
1079 }
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);
1088 if (error != "") {
1089 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1090 goto bail;
1091 }
1092
1093 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1094 if (error != "") {
1095 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
1096 goto bail;
1097 }
1098
1099 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1100 if (error != "") {
1101 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1102 goto bail;
1103 }
1104
1105 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1106 SCREEN_ORIENTATION_ATTR, &error);
1107 if (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;
1114 }
1115 }
1116 } else if (tag == "uses-library") {
1117 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1118 if (error != "") {
1119 fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
1120 goto bail;
1121 }
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);
1129
1130 if (error != "") {
1131 fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
1132 goto bail;
1133 }
1134 } else if (tag == "service") {
1135 withinService = true;
1136 serviceName = getAttribute(tree, NAME_ATTR, &error);
1137
1138 if (error != "") {
1139 fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
1140 goto bail;
1141 }
1142 }
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){
1148 String8 action;
1149 if (tag == "action") {
1150 action = getAttribute(tree, NAME_ATTR, &error);
1151 if (error != "") {
1152 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
1153 goto bail;
1154 }
1155 if (withinActivity) {
1156 if (action == "android.intent.action.MAIN") {
1157 isMainActivity = true;
1158 actMainActivity = true;
1159 }
1160 } else if (withinReceiver) {
1161 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1162 actWidgetReceivers = true;
1163 }
1164 } else if (withinService) {
1165 if (action == "android.view.InputMethod") {
1166 actImeService = true;
1167 } else if (action == "android.service.wallpaper.WallpaperService") {
1168 actWallpaperService = true;
1169 }
1170 }
1171 if (action == "android.intent.action.SEARCH") {
1172 isSearchable = true;
1173 }
1174 }
1175
1176 if (tag == "category") {
1177 String8 category = getAttribute(tree, NAME_ATTR, &error);
1178 if (error != "") {
1179 fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
1180 goto bail;
1181 }
1182 if (withinActivity) {
1183 if (category == "android.intent.category.LAUNCHER") {
1184 isLauncherActivity = true;
1185 }
1186 }
1187 }
1188 }
1189 }
1190
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;
1198 }
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");
1203 }
1204 }
1205
1206 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1207 // force them to always take READ_EXTERNAL_STORAGE as well.
1208 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1209 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
1210 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1211 "'requested WRITE_EXTERNAL_STORAGE'\n");
1212 }
1213
1214 // Pre-JellyBean call log permission compatibility.
1215 if (targetSdk < 16) {
1216 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1217 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
1218 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1219 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
1220 }
1221 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1222 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
1223 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1224 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
1225 }
1226 }
1227
1228 /* The following blocks handle printing "inferred" uses-features, based
1229 * on whether related features or permissions are used by the app.
1230 * Note that the various spec*Feature variables denote whether the
1231 * relevant tag was *present* in the AndroidManfest, not that it was
1232 * present and set to true.
1233 */
1234 // Camera-related back-compatibility logic
1235 if (!specCameraFeature) {
1236 if (reqCameraFlashFeature) {
1237 // if app requested a sub-feature (autofocus or flash) and didn't
1238 // request the base camera feature, we infer that it meant to
1239 printf("uses-feature:'android.hardware.camera'\n");
1240 printf("uses-implied-feature:'android.hardware.camera'," \
1241 "'requested android.hardware.camera.flash feature'\n");
1242 } else if (reqCameraAutofocusFeature) {
1243 // if app requested a sub-feature (autofocus or flash) and didn't
1244 // request the base camera feature, we infer that it meant to
1245 printf("uses-feature:'android.hardware.camera'\n");
1246 printf("uses-implied-feature:'android.hardware.camera'," \
1247 "'requested android.hardware.camera.autofocus feature'\n");
1248 } else if (hasCameraPermission) {
1249 // if app wants to use camera but didn't request the feature, we infer
1250 // that it meant to, and further that it wants autofocus
1251 // (which was the 1.0 - 1.5 behavior)
1252 printf("uses-feature:'android.hardware.camera'\n");
1253 if (!specCameraAutofocusFeature) {
1254 printf("uses-feature:'android.hardware.camera.autofocus'\n");
1255 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1256 "'requested android.permission.CAMERA permission'\n");
1257 }
1258 }
1259 }
1260
1261 // Location-related back-compatibility logic
1262 if (!specLocationFeature &&
1263 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1264 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1265 // if app either takes a location-related permission or requests one of the
1266 // sub-features, we infer that it also meant to request the base location feature
1267 printf("uses-feature:'android.hardware.location'\n");
1268 printf("uses-implied-feature:'android.hardware.location'," \
1269 "'requested a location access permission'\n");
1270 }
1271 if (!specGpsFeature && hasGpsPermission) {
1272 // if app takes GPS (FINE location) perm but does not request the GPS
1273 // feature, we infer that it meant to
1274 printf("uses-feature:'android.hardware.location.gps'\n");
1275 printf("uses-implied-feature:'android.hardware.location.gps'," \
1276 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
1277 }
1278 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1279 // if app takes Network location (COARSE location) perm but does not request the
1280 // network location feature, we infer that it meant to
1281 printf("uses-feature:'android.hardware.location.network'\n");
1282 printf("uses-implied-feature:'android.hardware.location.network'," \
1283 "'requested android.permission.ACCESS_COURSE_LOCATION permission'\n");
1284 }
1285
1286 // Bluetooth-related compatibility logic
1287 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
1288 // if app takes a Bluetooth permission but does not request the Bluetooth
1289 // feature, we infer that it meant to
1290 printf("uses-feature:'android.hardware.bluetooth'\n");
1291 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1292 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1293 "permission and targetSdkVersion > 4'\n");
1294 }
1295
1296 // Microphone-related compatibility logic
1297 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1298 // if app takes the record-audio permission but does not request the microphone
1299 // feature, we infer that it meant to
1300 printf("uses-feature:'android.hardware.microphone'\n");
1301 printf("uses-implied-feature:'android.hardware.microphone'," \
1302 "'requested android.permission.RECORD_AUDIO permission'\n");
1303 }
1304
1305 // WiFi-related compatibility logic
1306 if (!specWiFiFeature && hasWiFiPermission) {
1307 // if app takes one of the WiFi permissions but does not request the WiFi
1308 // feature, we infer that it meant to
1309 printf("uses-feature:'android.hardware.wifi'\n");
1310 printf("uses-implied-feature:'android.hardware.wifi'," \
1311 "'requested android.permission.ACCESS_WIFI_STATE, " \
1312 "android.permission.CHANGE_WIFI_STATE, or " \
1313 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
1314 }
1315
1316 // Telephony-related compatibility logic
1317 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1318 // if app takes one of the telephony permissions or requests a sub-feature but
1319 // does not request the base telephony feature, we infer that it meant to
1320 printf("uses-feature:'android.hardware.telephony'\n");
1321 printf("uses-implied-feature:'android.hardware.telephony'," \
1322 "'requested a telephony-related permission or feature'\n");
1323 }
1324
1325 // Touchscreen-related back-compatibility logic
1326 if (!specTouchscreenFeature) { // not a typo!
1327 // all apps are presumed to require a touchscreen, unless they explicitly say
1328 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1329 // Note that specTouchscreenFeature is true if the tag is present, regardless
1330 // of whether its value is true or false, so this is safe
1331 printf("uses-feature:'android.hardware.touchscreen'\n");
1332 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1333 "'assumed you require a touch screen unless explicitly made optional'\n");
1334 }
1335 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1336 // if app takes one of the telephony permissions or requests a sub-feature but
1337 // does not request the base telephony feature, we infer that it meant to
1338 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1339 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1340 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
1341 }
1342
1343 // Landscape/portrait-related compatibility logic
1344 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1345 // If the app has specified any activities in its manifest
1346 // that request a specific orientation, then assume that
1347 // orientation is required.
1348 if (reqScreenLandscapeFeature) {
1349 printf("uses-feature:'android.hardware.screen.landscape'\n");
1350 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1351 "'one or more activities have specified a landscape orientation'\n");
1352 }
1353 if (reqScreenPortraitFeature) {
1354 printf("uses-feature:'android.hardware.screen.portrait'\n");
1355 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1356 "'one or more activities have specified a portrait orientation'\n");
1357 }
1358 }
1359
1360 if (hasMainActivity) {
1361 printf("main\n");
1362 }
1363 if (hasWidgetReceivers) {
1364 printf("app-widget\n");
1365 }
1366 if (hasImeService) {
1367 printf("ime\n");
1368 }
1369 if (hasWallpaperService) {
1370 printf("wallpaper\n");
1371 }
1372 if (hasOtherActivities) {
1373 printf("other-activities\n");
1374 }
1375 if (isSearchable) {
1376 printf("search\n");
1377 }
1378 if (hasOtherReceivers) {
1379 printf("other-receivers\n");
1380 }
1381 if (hasOtherServices) {
1382 printf("other-services\n");
1383 }
1384
1385 // For modern apps, if screen size buckets haven't been specified
1386 // but the new width ranges have, then infer the buckets from them.
1387 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1388 && requiresSmallestWidthDp > 0) {
1389 int compatWidth = compatibleWidthLimitDp;
1390 if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
1391 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1392 smallScreen = -1;
1393 } else {
1394 smallScreen = 0;
1395 }
1396 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1397 normalScreen = -1;
1398 } else {
1399 normalScreen = 0;
1400 }
1401 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1402 largeScreen = -1;
1403 } else {
1404 largeScreen = 0;
1405 }
1406 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1407 xlargeScreen = -1;
1408 } else {
1409 xlargeScreen = 0;
1410 }
1411 }
1412
1413 // Determine default values for any unspecified screen sizes,
1414 // based on the target SDK of the package. As of 4 (donut)
1415 // the screen size support was introduced, so all default to
1416 // enabled.
1417 if (smallScreen > 0) {
1418 smallScreen = targetSdk >= 4 ? -1 : 0;
1419 }
1420 if (normalScreen > 0) {
1421 normalScreen = -1;
1422 }
1423 if (largeScreen > 0) {
1424 largeScreen = targetSdk >= 4 ? -1 : 0;
1425 }
1426 if (xlargeScreen > 0) {
1427 // Introduced in Gingerbread.
1428 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1429 }
1430 if (anyDensity > 0) {
1431 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1432 || compatibleWidthLimitDp > 0) ? -1 : 0;
1433 }
1434 printf("supports-screens:");
1435 if (smallScreen != 0) printf(" 'small'");
1436 if (normalScreen != 0) printf(" 'normal'");
1437 if (largeScreen != 0) printf(" 'large'");
1438 if (xlargeScreen != 0) printf(" 'xlarge'");
1439 printf("\n");
1440 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1441 if (requiresSmallestWidthDp > 0) {
1442 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1443 }
1444 if (compatibleWidthLimitDp > 0) {
1445 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1446 }
1447 if (largestWidthLimitDp > 0) {
1448 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1449 }
1450
1451 printf("locales:");
1452 const size_t NL = locales.size();
1453 for (size_t i=0; i<NL; i++) {
1454 const char* localeStr = locales[i].string();
1455 if (localeStr == NULL || strlen(localeStr) == 0) {
1456 localeStr = "--_--";
1457 }
1458 printf(" '%s'", localeStr);
1459 }
1460 printf("\n");
1461
1462 printf("densities:");
1463 const size_t ND = densities.size();
1464 for (size_t i=0; i<ND; i++) {
1465 printf(" '%d'", densities[i]);
1466 }
1467 printf("\n");
1468
1469 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1470 if (dir != NULL) {
1471 if (dir->getFileCount() > 0) {
1472 printf("native-code:");
1473 for (size_t i=0; i<dir->getFileCount(); i++) {
1474 printf(" '%s'", dir->getFileName(i).string());
1475 }
1476 printf("\n");
1477 }
1478 delete dir;
1479 }
1480 } else if (strcmp("badger", option) == 0) {
1481 printf("%s", CONSOLE_DATA);
1482 } else if (strcmp("configurations", option) == 0) {
1483 Vector<ResTable_config> configs;
1484 res.getConfigurations(&configs);
1485 const size_t N = configs.size();
1486 for (size_t i=0; i<N; i++) {
1487 printf("%s\n", configs[i].toString().string());
1488 }
1489 } else {
1490 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1491 goto bail;
1492 }
1493 }
1494
1495 result = NO_ERROR;
1496
1497bail:
1498 if (asset) {
1499 delete asset;
1500 }
1501 return (result != NO_ERROR);
1502}
1503
1504
1505/*
1506 * Handle the "add" command, which wants to add files to a new or
1507 * pre-existing archive.
1508 */
1509int doAdd(Bundle* bundle)
1510{
1511 ZipFile* zip = NULL;
1512 status_t result = UNKNOWN_ERROR;
1513 const char* zipFileName;
1514
1515 if (bundle->getUpdate()) {
1516 /* avoid confusion */
1517 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1518 goto bail;
1519 }
1520
1521 if (bundle->getFileSpecCount() < 1) {
1522 fprintf(stderr, "ERROR: must specify zip file name\n");
1523 goto bail;
1524 }
1525 zipFileName = bundle->getFileSpecEntry(0);
1526
1527 if (bundle->getFileSpecCount() < 2) {
1528 fprintf(stderr, "NOTE: nothing to do\n");
1529 goto bail;
1530 }
1531
1532 zip = openReadWrite(zipFileName, true);
1533 if (zip == NULL) {
1534 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1535 goto bail;
1536 }
1537
1538 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1539 const char* fileName = bundle->getFileSpecEntry(i);
1540
1541 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1542 printf(" '%s'... (from gzip)\n", fileName);
1543 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1544 } else {
1545 if (bundle->getJunkPath()) {
1546 String8 storageName = String8(fileName).getPathLeaf();
1547 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1548 result = zip->add(fileName, storageName.string(),
1549 bundle->getCompressionMethod(), NULL);
1550 } else {
1551 printf(" '%s'...\n", fileName);
1552 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1553 }
1554 }
1555 if (result != NO_ERROR) {
1556 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1557 if (result == NAME_NOT_FOUND)
1558 fprintf(stderr, ": file not found\n");
1559 else if (result == ALREADY_EXISTS)
1560 fprintf(stderr, ": already exists in archive\n");
1561 else
1562 fprintf(stderr, "\n");
1563 goto bail;
1564 }
1565 }
1566
1567 result = NO_ERROR;
1568
1569bail:
1570 delete zip;
1571 return (result != NO_ERROR);
1572}
1573
1574
1575/*
1576 * Delete files from an existing archive.
1577 */
1578int doRemove(Bundle* bundle)
1579{
1580 ZipFile* zip = NULL;
1581 status_t result = UNKNOWN_ERROR;
1582 const char* zipFileName;
1583
1584 if (bundle->getFileSpecCount() < 1) {
1585 fprintf(stderr, "ERROR: must specify zip file name\n");
1586 goto bail;
1587 }
1588 zipFileName = bundle->getFileSpecEntry(0);
1589
1590 if (bundle->getFileSpecCount() < 2) {
1591 fprintf(stderr, "NOTE: nothing to do\n");
1592 goto bail;
1593 }
1594
1595 zip = openReadWrite(zipFileName, false);
1596 if (zip == NULL) {
1597 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1598 zipFileName);
1599 goto bail;
1600 }
1601
1602 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1603 const char* fileName = bundle->getFileSpecEntry(i);
1604 ZipEntry* entry;
1605
1606 entry = zip->getEntryByName(fileName);
1607 if (entry == NULL) {
1608 printf(" '%s' NOT FOUND\n", fileName);
1609 continue;
1610 }
1611
1612 result = zip->remove(entry);
1613
1614 if (result != NO_ERROR) {
1615 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1616 bundle->getFileSpecEntry(i), zipFileName);
1617 goto bail;
1618 }
1619 }
1620
1621 /* update the archive */
1622 zip->flush();
1623
1624bail:
1625 delete zip;
1626 return (result != NO_ERROR);
1627}
1628
1629
1630/*
1631 * Package up an asset directory and associated application files.
1632 */
1633int doPackage(Bundle* bundle)
1634{
1635 const char* outputAPKFile;
1636 int retVal = 1;
1637 status_t err;
1638 sp<AaptAssets> assets;
1639 int N;
1640 FILE* fp;
1641 String8 dependencyFile;
1642
1643 // -c zz_ZZ means do pseudolocalization
1644 ResourceFilter filter;
1645 err = filter.parse(bundle->getConfigurations());
1646 if (err != NO_ERROR) {
1647 goto bail;
1648 }
1649 if (filter.containsPseudo()) {
1650 bundle->setPseudolocalize(true);
1651 }
1652
1653 N = bundle->getFileSpecCount();
1654 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1655 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1656 fprintf(stderr, "ERROR: no input files\n");
1657 goto bail;
1658 }
1659
1660 outputAPKFile = bundle->getOutputAPKFile();
1661
1662 // Make sure the filenames provided exist and are of the appropriate type.
1663 if (outputAPKFile) {
1664 FileType type;
1665 type = getFileType(outputAPKFile);
1666 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1667 fprintf(stderr,
1668 "ERROR: output file '%s' exists but is not regular file\n",
1669 outputAPKFile);
1670 goto bail;
1671 }
1672 }
1673
1674 // Load the assets.
1675 assets = new AaptAssets();
1676
1677 // Set up the resource gathering in assets if we're going to generate
1678 // dependency files. Every time we encounter a resource while slurping
1679 // the tree, we'll add it to these stores so we have full resource paths
1680 // to write to a dependency file.
1681 if (bundle->getGenDependencies()) {
1682 sp<FilePathStore> resPathStore = new FilePathStore;
1683 assets->setFullResPaths(resPathStore);
1684 sp<FilePathStore> assetPathStore = new FilePathStore;
1685 assets->setFullAssetPaths(assetPathStore);
1686 }
1687
1688 err = assets->slurpFromArgs(bundle);
1689 if (err < 0) {
1690 goto bail;
1691 }
1692
1693 if (bundle->getVerbose()) {
1694 assets->print(String8());
1695 }
1696
1697 // If they asked for any fileAs that need to be compiled, do so.
1698 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1699 err = buildResources(bundle, assets);
1700 if (err != 0) {
1701 goto bail;
1702 }
1703 }
1704
1705 // At this point we've read everything and processed everything. From here
1706 // on out it's just writing output files.
1707 if (SourcePos::hasErrors()) {
1708 goto bail;
1709 }
1710
1711 // Update symbols with information about which ones are needed as Java symbols.
1712 assets->applyJavaSymbols();
1713 if (SourcePos::hasErrors()) {
1714 goto bail;
1715 }
1716
1717 // If we've been asked to generate a dependency file, do that here
1718 if (bundle->getGenDependencies()) {
1719 // If this is the packaging step, generate the dependency file next to
1720 // the output apk (e.g. bin/resources.ap_.d)
1721 if (outputAPKFile) {
1722 dependencyFile = String8(outputAPKFile);
1723 // Add the .d extension to the dependency file.
1724 dependencyFile.append(".d");
1725 } else {
1726 // Else if this is the R.java dependency generation step,
1727 // generate the dependency file in the R.java package subdirectory
1728 // e.g. gen/com/foo/app/R.java.d
1729 dependencyFile = String8(bundle->getRClassDir());
1730 dependencyFile.appendPath("R.java.d");
1731 }
1732 // Make sure we have a clean dependency file to start with
1733 fp = fopen(dependencyFile, "w");
1734 fclose(fp);
1735 }
1736
1737 // Write out R.java constants
1738 if (!assets->havePrivateSymbols()) {
1739 if (bundle->getCustomPackage() == NULL) {
1740 // Write the R.java file into the appropriate class directory
1741 // e.g. gen/com/foo/app/R.java
1742 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1743 } else {
1744 const String8 customPkg(bundle->getCustomPackage());
1745 err = writeResourceSymbols(bundle, assets, customPkg, true);
1746 }
1747 if (err < 0) {
1748 goto bail;
1749 }
1750 // If we have library files, we're going to write our R.java file into
1751 // the appropriate class directory for those libraries as well.
1752 // e.g. gen/com/foo/app/lib/R.java
1753 if (bundle->getExtraPackages() != NULL) {
1754 // Split on colon
1755 String8 libs(bundle->getExtraPackages());
1756 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
1757 while (packageString != NULL) {
1758 // Write the R.java file out with the correct package name
1759 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
1760 if (err < 0) {
1761 goto bail;
1762 }
1763 packageString = strtok(NULL, ":");
1764 }
1765 libs.unlockBuffer();
1766 }
1767 } else {
1768 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1769 if (err < 0) {
1770 goto bail;
1771 }
1772 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1773 if (err < 0) {
1774 goto bail;
1775 }
1776 }
1777
1778 // Write out the ProGuard file
1779 err = writeProguardFile(bundle, assets);
1780 if (err < 0) {
1781 goto bail;
1782 }
1783
1784 // Write the apk
1785 if (outputAPKFile) {
1786 err = writeAPK(bundle, assets, String8(outputAPKFile));
1787 if (err != NO_ERROR) {
1788 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1789 goto bail;
1790 }
1791 }
1792
1793 // If we've been asked to generate a dependency file, we need to finish up here.
1794 // the writeResourceSymbols and writeAPK functions have already written the target
1795 // half of the dependency file, now we need to write the prerequisites. (files that
1796 // the R.java file or .ap_ file depend on)
1797 if (bundle->getGenDependencies()) {
1798 // Now that writeResourceSymbols or writeAPK has taken care of writing
1799 // the targets to our dependency file, we'll write the prereqs
1800 fp = fopen(dependencyFile, "a+");
1801 fprintf(fp, " : ");
1802 bool includeRaw = (outputAPKFile != NULL);
1803 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
1804 // Also manually add the AndroidManifeset since it's not under res/ or assets/
1805 // and therefore was not added to our pathstores during slurping
1806 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
1807 fclose(fp);
1808 }
1809
1810 retVal = 0;
1811bail:
1812 if (SourcePos::hasErrors()) {
1813 SourcePos::printErrors(stderr);
1814 }
1815 return retVal;
1816}
1817
1818/*
1819 * Do PNG Crunching
1820 * PRECONDITIONS
1821 * -S flag points to a source directory containing drawable* folders
1822 * -C flag points to destination directory. The folder structure in the
1823 * source directory will be mirrored to the destination (cache) directory
1824 *
1825 * POSTCONDITIONS
1826 * Destination directory will be updated to match the PNG files in
1827 * the source directory.
1828 */
1829int doCrunch(Bundle* bundle)
1830{
1831 fprintf(stdout, "Crunching PNG Files in ");
1832 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
1833 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
1834
1835 updatePreProcessedCache(bundle);
1836
1837 return NO_ERROR;
1838}
1839
1840char CONSOLE_DATA[2925] = {
1841 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1842 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1843 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1844 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1845 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
1846 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
1847 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1848 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1849 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
1850 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
1851 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1852 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1853 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
1854 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1855 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1856 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
1857 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
1858 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1859 10, 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, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
1861 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
1862 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1863 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1864 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
1865 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1866 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1867 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
1868 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
1869 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 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, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
1872 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1873 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1874 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
1875 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
1876 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1877 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1878 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
1879 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
1880 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1881 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1882 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
1883 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1884 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1885 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
1886 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
1887 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1888 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
1889 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
1890 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1891 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
1892 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
1893 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
1894 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1895 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1896 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
1897 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
1898 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
1899 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1900 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
1901 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
1902 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
1903 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
1904 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
1905 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1906 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
1907 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
1908 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
1909 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
1910 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
1911 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
1912 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
1913 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1914 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
1915 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
1916 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
1917 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1918 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
1919 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
1920 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1921 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
1922 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
1923 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
1924 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1925 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1926 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
1927 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1928 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
1929 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
1930 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1931 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1932 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
1933 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
1934 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1935 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1936 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1937 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1938 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
1939 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
1940 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
1941 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1942 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1943 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
1944 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
1945 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
1946 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1947 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
1948 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1949 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1950 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
1951 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
1952 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1953 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1954 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
1955 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1956 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
1957 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
1958 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
1959 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1960 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1961 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
1962 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
1963 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
1964 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
1965 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
1966 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
1967 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
1968 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
1969 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
1970 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1971 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1972 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
1973 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
1974 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
1975 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
1976 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
1977 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1978 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1979 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1980 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
1981 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
1982 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1983 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
1984 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1985 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
1986 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
1987 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
1988 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1989 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1990 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
1991 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
1992 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
1993 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
1994 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
1995 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1996 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1997 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
1998 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
1999 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2000 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2001 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2002 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2003 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2004 };