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