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