blob: 7a40ca48825b8e0873ff602b07e1c2831e624e64 [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 Lesinski282e1812014-01-23 18:17:42 -0800388 DEBUGGABLE_ATTR = 0x0101000f,
389 VALUE_ATTR = 0x01010024,
390 VERSION_CODE_ATTR = 0x0101021b,
391 VERSION_NAME_ATTR = 0x0101021c,
392 SCREEN_ORIENTATION_ATTR = 0x0101001e,
393 MIN_SDK_VERSION_ATTR = 0x0101020c,
394 MAX_SDK_VERSION_ATTR = 0x01010271,
395 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
396 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
397 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
398 REQ_NAVIGATION_ATTR = 0x0101022a,
399 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
400 TARGET_SDK_VERSION_ATTR = 0x01010270,
401 TEST_ONLY_ATTR = 0x01010272,
402 ANY_DENSITY_ATTR = 0x0101026c,
403 GL_ES_VERSION_ATTR = 0x01010281,
404 SMALL_SCREEN_ATTR = 0x01010284,
405 NORMAL_SCREEN_ATTR = 0x01010285,
406 LARGE_SCREEN_ATTR = 0x01010286,
407 XLARGE_SCREEN_ATTR = 0x010102bf,
408 REQUIRED_ATTR = 0x0101028e,
409 SCREEN_SIZE_ATTR = 0x010102ca,
410 SCREEN_DENSITY_ATTR = 0x010102cb,
411 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
412 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
413 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
414 PUBLIC_KEY_ATTR = 0x010103a6,
415};
416
417const char *getComponentName(String8 &pkgName, String8 &componentName) {
418 ssize_t idx = componentName.find(".");
419 String8 retStr(pkgName);
420 if (idx == 0) {
421 retStr += componentName;
422 } else if (idx < 0) {
423 retStr += ".";
424 retStr += componentName;
425 } else {
426 return componentName.string();
427 }
428 return retStr.string();
429}
430
431static void printCompatibleScreens(ResXMLTree& tree) {
432 size_t len;
433 ResXMLTree::event_code_t code;
434 int depth = 0;
435 bool first = true;
436 printf("compatible-screens:");
437 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
438 if (code == ResXMLTree::END_TAG) {
439 depth--;
440 if (depth < 0) {
441 break;
442 }
443 continue;
444 }
445 if (code != ResXMLTree::START_TAG) {
446 continue;
447 }
448 depth++;
449 String8 tag(tree.getElementName(&len));
450 if (tag == "screen") {
451 int32_t screenSize = getIntegerAttribute(tree,
452 SCREEN_SIZE_ATTR, NULL, -1);
453 int32_t screenDensity = getIntegerAttribute(tree,
454 SCREEN_DENSITY_ATTR, NULL, -1);
455 if (screenSize > 0 && screenDensity > 0) {
456 if (!first) {
457 printf(",");
458 }
459 first = false;
460 printf("'%d/%d'", screenSize, screenDensity);
461 }
462 }
463 }
464 printf("\n");
465}
466
467/*
468 * Handle the "dump" command, to extract select data from an archive.
469 */
470extern char CONSOLE_DATA[2925]; // see EOF
471int doDump(Bundle* bundle)
472{
473 status_t result = UNKNOWN_ERROR;
474 Asset* asset = NULL;
475
476 if (bundle->getFileSpecCount() < 1) {
477 fprintf(stderr, "ERROR: no dump option specified\n");
478 return 1;
479 }
480
481 if (bundle->getFileSpecCount() < 2) {
482 fprintf(stderr, "ERROR: no dump file specified\n");
483 return 1;
484 }
485
486 const char* option = bundle->getFileSpecEntry(0);
487 const char* filename = bundle->getFileSpecEntry(1);
488
489 AssetManager assets;
490 void* assetsCookie;
491 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
492 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
493 return 1;
494 }
495
496 // Make a dummy config for retrieving resources... we need to supply
497 // non-default values for some configs so that we can retrieve resources
498 // in the app that don't have a default. The most important of these is
499 // the API version because key resources like icons will have an implicit
500 // version if they are using newer config types like density.
501 ResTable_config config;
502 config.language[0] = 'e';
503 config.language[1] = 'n';
504 config.country[0] = 'U';
505 config.country[1] = 'S';
506 config.orientation = ResTable_config::ORIENTATION_PORT;
507 config.density = ResTable_config::DENSITY_MEDIUM;
508 config.sdkVersion = 10000; // Very high.
509 config.screenWidthDp = 320;
510 config.screenHeightDp = 480;
511 config.smallestScreenWidthDp = 320;
512 assets.setConfiguration(config);
513
514 const ResTable& res = assets.getResources(false);
515 if (&res == NULL) {
516 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
517 goto bail;
518 }
519
520 if (strcmp("resources", option) == 0) {
521#ifndef HAVE_ANDROID_OS
522 res.print(bundle->getValues());
523#endif
524
525 } else if (strcmp("strings", option) == 0) {
526 const ResStringPool* pool = res.getTableStringBlock(0);
527 printStringPool(pool);
528
529 } else if (strcmp("xmltree", option) == 0) {
530 if (bundle->getFileSpecCount() < 3) {
531 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
532 goto bail;
533 }
534
535 for (int i=2; i<bundle->getFileSpecCount(); i++) {
536 const char* resname = bundle->getFileSpecEntry(i);
537 ResXMLTree tree;
538 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
539 if (asset == NULL) {
540 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
541 goto bail;
542 }
543
544 if (tree.setTo(asset->getBuffer(true),
545 asset->getLength()) != NO_ERROR) {
546 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
547 goto bail;
548 }
549 tree.restart();
550 printXMLBlock(&tree);
551 tree.uninit();
552 delete asset;
553 asset = NULL;
554 }
555
556 } else if (strcmp("xmlstrings", option) == 0) {
557 if (bundle->getFileSpecCount() < 3) {
558 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
559 goto bail;
560 }
561
562 for (int i=2; i<bundle->getFileSpecCount(); i++) {
563 const char* resname = bundle->getFileSpecEntry(i);
564 ResXMLTree tree;
565 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
566 if (asset == NULL) {
567 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
568 goto bail;
569 }
570
571 if (tree.setTo(asset->getBuffer(true),
572 asset->getLength()) != NO_ERROR) {
573 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
574 goto bail;
575 }
576 printStringPool(&tree.getStrings());
577 delete asset;
578 asset = NULL;
579 }
580
581 } else {
582 ResXMLTree tree;
583 asset = assets.openNonAsset("AndroidManifest.xml",
584 Asset::ACCESS_BUFFER);
585 if (asset == NULL) {
586 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
587 goto bail;
588 }
589
590 if (tree.setTo(asset->getBuffer(true),
591 asset->getLength()) != NO_ERROR) {
592 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
593 goto bail;
594 }
595 tree.restart();
596
597 if (strcmp("permissions", option) == 0) {
598 size_t len;
599 ResXMLTree::event_code_t code;
600 int depth = 0;
601 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
602 if (code == ResXMLTree::END_TAG) {
603 depth--;
604 continue;
605 }
606 if (code != ResXMLTree::START_TAG) {
607 continue;
608 }
609 depth++;
610 String8 tag(tree.getElementName(&len));
611 //printf("Depth %d tag %s\n", depth, tag.string());
612 if (depth == 1) {
613 if (tag != "manifest") {
614 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
615 goto bail;
616 }
617 String8 pkg = getAttribute(tree, NULL, "package", NULL);
618 printf("package: %s\n", pkg.string());
619 } else if (depth == 2 && tag == "permission") {
620 String8 error;
621 String8 name = getAttribute(tree, NAME_ATTR, &error);
622 if (error != "") {
623 fprintf(stderr, "ERROR: %s\n", error.string());
624 goto bail;
625 }
626 printf("permission: %s\n", name.string());
627 } else if (depth == 2 && tag == "uses-permission") {
628 String8 error;
629 String8 name = getAttribute(tree, NAME_ATTR, &error);
630 if (error != "") {
631 fprintf(stderr, "ERROR: %s\n", error.string());
632 goto bail;
633 }
634 printf("uses-permission: %s\n", name.string());
635 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
636 if (!req) {
637 printf("optional-permission: %s\n", name.string());
638 }
639 }
640 }
641 } else if (strcmp("badging", option) == 0) {
642 Vector<String8> locales;
643 res.getLocales(&locales);
644
645 Vector<ResTable_config> configs;
646 res.getConfigurations(&configs);
647 SortedVector<int> densities;
648 const size_t NC = configs.size();
649 for (size_t i=0; i<NC; i++) {
650 int dens = configs[i].density;
651 if (dens == 0) {
652 dens = 160;
653 }
654 densities.add(dens);
655 }
656
657 size_t len;
658 ResXMLTree::event_code_t code;
659 int depth = 0;
660 String8 error;
661 bool withinActivity = false;
662 bool isMainActivity = false;
663 bool isLauncherActivity = false;
664 bool isSearchable = false;
665 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700666 bool withinSupportsInput = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800667 bool withinReceiver = false;
668 bool withinService = false;
669 bool withinIntentFilter = false;
670 bool hasMainActivity = false;
671 bool hasOtherActivities = false;
672 bool hasOtherReceivers = false;
673 bool hasOtherServices = false;
674 bool hasWallpaperService = false;
675 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700676 bool hasAccessibilityService = false;
677 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800678 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700679 bool hasDeviceAdminReceiver = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800680 bool hasIntentFilter = false;
681 bool actMainActivity = false;
682 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700683 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800684 bool actImeService = false;
685 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700686 bool actAccessibilityService = false;
687 bool actPrintService = false;
688
689 // These permissions are required by services implementing services
690 // the system binds to (IME, Accessibility, PrintServices, etc.)
691 bool hasBindDeviceAdminPermission = false;
692 bool hasBindInputMethodPermission = false;
693 bool hasBindAccessibilityServicePermission = false;
694 bool hasBindPrintServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800695
696 // These two implement the implicit permissions that are granted
697 // to pre-1.6 applications.
698 bool hasWriteExternalStoragePermission = false;
699 bool hasReadPhoneStatePermission = false;
700
701 // If an app requests write storage, they will also get read storage.
702 bool hasReadExternalStoragePermission = false;
703
704 // Implement transition to read and write call log.
705 bool hasReadContactsPermission = false;
706 bool hasWriteContactsPermission = false;
707 bool hasReadCallLogPermission = false;
708 bool hasWriteCallLogPermission = false;
709
710 // This next group of variables is used to implement a group of
711 // backward-compatibility heuristics necessitated by the addition of
712 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
713 // heuristic is "if an app requests a permission but doesn't explicitly
714 // request the corresponding <uses-feature>, presume it's there anyway".
715 bool specCameraFeature = false; // camera-related
716 bool specCameraAutofocusFeature = false;
717 bool reqCameraAutofocusFeature = false;
718 bool reqCameraFlashFeature = false;
719 bool hasCameraPermission = false;
720 bool specLocationFeature = false; // location-related
721 bool specNetworkLocFeature = false;
722 bool reqNetworkLocFeature = false;
723 bool specGpsFeature = false;
724 bool reqGpsFeature = false;
725 bool hasMockLocPermission = false;
726 bool hasCoarseLocPermission = false;
727 bool hasGpsPermission = false;
728 bool hasGeneralLocPermission = false;
729 bool specBluetoothFeature = false; // Bluetooth API-related
730 bool hasBluetoothPermission = false;
731 bool specMicrophoneFeature = false; // microphone-related
732 bool hasRecordAudioPermission = false;
733 bool specWiFiFeature = false;
734 bool hasWiFiPermission = false;
735 bool specTelephonyFeature = false; // telephony-related
736 bool reqTelephonySubFeature = false;
737 bool hasTelephonyPermission = false;
738 bool specTouchscreenFeature = false; // touchscreen-related
739 bool specMultitouchFeature = false;
740 bool reqDistinctMultitouchFeature = false;
741 bool specScreenPortraitFeature = false;
742 bool specScreenLandscapeFeature = false;
743 bool reqScreenPortraitFeature = false;
744 bool reqScreenLandscapeFeature = false;
745 // 2.2 also added some other features that apps can request, but that
746 // have no corresponding permission, so we cannot implement any
747 // back-compatibility heuristic for them. The below are thus unnecessary
748 // (but are retained here for documentary purposes.)
749 //bool specCompassFeature = false;
750 //bool specAccelerometerFeature = false;
751 //bool specProximityFeature = false;
752 //bool specAmbientLightFeature = false;
753 //bool specLiveWallpaperFeature = false;
754
755 int targetSdk = 0;
756 int smallScreen = 1;
757 int normalScreen = 1;
758 int largeScreen = 1;
759 int xlargeScreen = 1;
760 int anyDensity = 1;
761 int requiresSmallestWidthDp = 0;
762 int compatibleWidthLimitDp = 0;
763 int largestWidthLimitDp = 0;
764 String8 pkg;
765 String8 activityName;
766 String8 activityLabel;
767 String8 activityIcon;
768 String8 receiverName;
769 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700770 Vector<String8> supportedInput;
Adam Lesinski282e1812014-01-23 18:17:42 -0800771 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
772 if (code == ResXMLTree::END_TAG) {
773 depth--;
774 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -0700775 if (withinSupportsInput && !supportedInput.isEmpty()) {
776 printf("supports-input: '");
777 const size_t N = supportedInput.size();
778 for (size_t i=0; i<N; i++) {
779 printf("%s", supportedInput[i].string());
780 if (i != N - 1) {
781 printf("' '");
782 } else {
783 printf("'\n");
784 }
785 }
786 supportedInput.clear();
787 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800788 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700789 withinSupportsInput = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800790 } else if (depth < 3) {
791 if (withinActivity && isMainActivity && isLauncherActivity) {
792 const char *aName = getComponentName(pkg, activityName);
793 printf("launchable-activity:");
794 if (aName != NULL) {
795 printf(" name='%s' ", aName);
796 }
797 printf(" label='%s' icon='%s'\n",
798 activityLabel.string(),
799 activityIcon.string());
800 }
801 if (!hasIntentFilter) {
802 hasOtherActivities |= withinActivity;
803 hasOtherReceivers |= withinReceiver;
804 hasOtherServices |= withinService;
805 }
806 withinActivity = false;
807 withinService = false;
808 withinReceiver = false;
809 hasIntentFilter = false;
810 isMainActivity = isLauncherActivity = false;
811 } else if (depth < 4) {
812 if (withinIntentFilter) {
813 if (withinActivity) {
814 hasMainActivity |= actMainActivity;
815 hasOtherActivities |= !actMainActivity;
816 } else if (withinReceiver) {
817 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700818 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
819 hasBindDeviceAdminPermission);
820 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -0800821 } else if (withinService) {
822 hasImeService |= actImeService;
823 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700824 hasAccessibilityService |= (actAccessibilityService &&
825 hasBindAccessibilityServicePermission);
826 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
827 hasOtherServices |= (!actImeService && !actWallpaperService &&
828 !actAccessibilityService && !actPrintService);
Adam Lesinski282e1812014-01-23 18:17:42 -0800829 }
830 }
831 withinIntentFilter = false;
832 }
833 continue;
834 }
835 if (code != ResXMLTree::START_TAG) {
836 continue;
837 }
838 depth++;
839 String8 tag(tree.getElementName(&len));
840 //printf("Depth %d, %s\n", depth, tag.string());
841 if (depth == 1) {
842 if (tag != "manifest") {
843 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
844 goto bail;
845 }
846 pkg = getAttribute(tree, NULL, "package", NULL);
847 printf("package: name='%s' ", pkg.string());
848 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
849 if (error != "") {
850 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
851 goto bail;
852 }
853 if (versionCode > 0) {
854 printf("versionCode='%d' ", versionCode);
855 } else {
856 printf("versionCode='' ");
857 }
858 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
859 if (error != "") {
860 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
861 goto bail;
862 }
863 printf("versionName='%s'\n", versionName.string());
864 } else if (depth == 2) {
865 withinApplication = false;
866 if (tag == "application") {
867 withinApplication = true;
868
869 String8 label;
870 const size_t NL = locales.size();
871 for (size_t i=0; i<NL; i++) {
872 const char* localeStr = locales[i].string();
873 assets.setLocale(localeStr != NULL ? localeStr : "");
874 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
875 if (llabel != "") {
876 if (localeStr == NULL || strlen(localeStr) == 0) {
877 label = llabel;
878 printf("application-label:'%s'\n", llabel.string());
879 } else {
880 if (label == "") {
881 label = llabel;
882 }
883 printf("application-label-%s:'%s'\n", localeStr,
884 llabel.string());
885 }
886 }
887 }
888
889 ResTable_config tmpConfig = config;
890 const size_t ND = densities.size();
891 for (size_t i=0; i<ND; i++) {
892 tmpConfig.density = densities[i];
893 assets.setConfiguration(tmpConfig);
894 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
895 if (icon != "") {
896 printf("application-icon-%d:'%s'\n", densities[i], icon.string());
897 }
898 }
899 assets.setConfiguration(config);
900
901 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
902 if (error != "") {
903 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
904 goto bail;
905 }
906 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
907 if (error != "") {
908 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
909 goto bail;
910 }
911 printf("application: label='%s' ", label.string());
912 printf("icon='%s'\n", icon.string());
913 if (testOnly != 0) {
914 printf("testOnly='%d'\n", testOnly);
915 }
916
917 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
918 if (error != "") {
919 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
920 goto bail;
921 }
922 if (debuggable != 0) {
923 printf("application-debuggable\n");
924 }
925 } else if (tag == "uses-sdk") {
926 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
927 if (error != "") {
928 error = "";
929 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
930 if (error != "") {
931 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
932 error.string());
933 goto bail;
934 }
935 if (name == "Donut") targetSdk = 4;
936 printf("sdkVersion:'%s'\n", name.string());
937 } else if (code != -1) {
938 targetSdk = code;
939 printf("sdkVersion:'%d'\n", code);
940 }
941 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
942 if (code != -1) {
943 printf("maxSdkVersion:'%d'\n", code);
944 }
945 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
946 if (error != "") {
947 error = "";
948 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
949 if (error != "") {
950 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
951 error.string());
952 goto bail;
953 }
954 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
955 printf("targetSdkVersion:'%s'\n", name.string());
956 } else if (code != -1) {
957 if (targetSdk < code) {
958 targetSdk = code;
959 }
960 printf("targetSdkVersion:'%d'\n", code);
961 }
962 } else if (tag == "uses-configuration") {
963 int32_t reqTouchScreen = getIntegerAttribute(tree,
964 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
965 int32_t reqKeyboardType = getIntegerAttribute(tree,
966 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
967 int32_t reqHardKeyboard = getIntegerAttribute(tree,
968 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
969 int32_t reqNavigation = getIntegerAttribute(tree,
970 REQ_NAVIGATION_ATTR, NULL, 0);
971 int32_t reqFiveWayNav = getIntegerAttribute(tree,
972 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
973 printf("uses-configuration:");
974 if (reqTouchScreen != 0) {
975 printf(" reqTouchScreen='%d'", reqTouchScreen);
976 }
977 if (reqKeyboardType != 0) {
978 printf(" reqKeyboardType='%d'", reqKeyboardType);
979 }
980 if (reqHardKeyboard != 0) {
981 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
982 }
983 if (reqNavigation != 0) {
984 printf(" reqNavigation='%d'", reqNavigation);
985 }
986 if (reqFiveWayNav != 0) {
987 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
988 }
989 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -0700990 } else if (tag == "supports-input") {
991 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -0800992 } else if (tag == "supports-screens") {
993 smallScreen = getIntegerAttribute(tree,
994 SMALL_SCREEN_ATTR, NULL, 1);
995 normalScreen = getIntegerAttribute(tree,
996 NORMAL_SCREEN_ATTR, NULL, 1);
997 largeScreen = getIntegerAttribute(tree,
998 LARGE_SCREEN_ATTR, NULL, 1);
999 xlargeScreen = getIntegerAttribute(tree,
1000 XLARGE_SCREEN_ATTR, NULL, 1);
1001 anyDensity = getIntegerAttribute(tree,
1002 ANY_DENSITY_ATTR, NULL, 1);
1003 requiresSmallestWidthDp = getIntegerAttribute(tree,
1004 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1005 compatibleWidthLimitDp = getIntegerAttribute(tree,
1006 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1007 largestWidthLimitDp = getIntegerAttribute(tree,
1008 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1009 } else if (tag == "uses-feature") {
1010 String8 name = getAttribute(tree, NAME_ATTR, &error);
1011
1012 if (name != "" && error == "") {
1013 int req = getIntegerAttribute(tree,
1014 REQUIRED_ATTR, NULL, 1);
1015
1016 if (name == "android.hardware.camera") {
1017 specCameraFeature = true;
1018 } else if (name == "android.hardware.camera.autofocus") {
1019 // these have no corresponding permission to check for,
1020 // but should imply the foundational camera permission
1021 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
1022 specCameraAutofocusFeature = true;
1023 } else if (req && (name == "android.hardware.camera.flash")) {
1024 // these have no corresponding permission to check for,
1025 // but should imply the foundational camera permission
1026 reqCameraFlashFeature = true;
1027 } else if (name == "android.hardware.location") {
1028 specLocationFeature = true;
1029 } else if (name == "android.hardware.location.network") {
1030 specNetworkLocFeature = true;
1031 reqNetworkLocFeature = reqNetworkLocFeature || req;
1032 } else if (name == "android.hardware.location.gps") {
1033 specGpsFeature = true;
1034 reqGpsFeature = reqGpsFeature || req;
1035 } else if (name == "android.hardware.bluetooth") {
1036 specBluetoothFeature = true;
1037 } else if (name == "android.hardware.touchscreen") {
1038 specTouchscreenFeature = true;
1039 } else if (name == "android.hardware.touchscreen.multitouch") {
1040 specMultitouchFeature = true;
1041 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
1042 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
1043 } else if (name == "android.hardware.microphone") {
1044 specMicrophoneFeature = true;
1045 } else if (name == "android.hardware.wifi") {
1046 specWiFiFeature = true;
1047 } else if (name == "android.hardware.telephony") {
1048 specTelephonyFeature = true;
1049 } else if (req && (name == "android.hardware.telephony.gsm" ||
1050 name == "android.hardware.telephony.cdma")) {
1051 // these have no corresponding permission to check for,
1052 // but should imply the foundational telephony permission
1053 reqTelephonySubFeature = true;
1054 } else if (name == "android.hardware.screen.portrait") {
1055 specScreenPortraitFeature = true;
1056 } else if (name == "android.hardware.screen.landscape") {
1057 specScreenLandscapeFeature = true;
1058 }
1059 printf("uses-feature%s:'%s'\n",
1060 req ? "" : "-not-required", name.string());
1061 } else {
1062 int vers = getIntegerAttribute(tree,
1063 GL_ES_VERSION_ATTR, &error);
1064 if (error == "") {
1065 printf("uses-gl-es:'0x%x'\n", vers);
1066 }
1067 }
1068 } else if (tag == "uses-permission") {
1069 String8 name = getAttribute(tree, NAME_ATTR, &error);
1070 if (name != "" && error == "") {
1071 if (name == "android.permission.CAMERA") {
1072 hasCameraPermission = true;
1073 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1074 hasGpsPermission = true;
1075 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1076 hasMockLocPermission = true;
1077 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1078 hasCoarseLocPermission = true;
1079 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1080 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1081 hasGeneralLocPermission = true;
1082 } else if (name == "android.permission.BLUETOOTH" ||
1083 name == "android.permission.BLUETOOTH_ADMIN") {
1084 hasBluetoothPermission = true;
1085 } else if (name == "android.permission.RECORD_AUDIO") {
1086 hasRecordAudioPermission = true;
1087 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1088 name == "android.permission.CHANGE_WIFI_STATE" ||
1089 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1090 hasWiFiPermission = true;
1091 } else if (name == "android.permission.CALL_PHONE" ||
1092 name == "android.permission.CALL_PRIVILEGED" ||
1093 name == "android.permission.MODIFY_PHONE_STATE" ||
1094 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1095 name == "android.permission.READ_SMS" ||
1096 name == "android.permission.RECEIVE_SMS" ||
1097 name == "android.permission.RECEIVE_MMS" ||
1098 name == "android.permission.RECEIVE_WAP_PUSH" ||
1099 name == "android.permission.SEND_SMS" ||
1100 name == "android.permission.WRITE_APN_SETTINGS" ||
1101 name == "android.permission.WRITE_SMS") {
1102 hasTelephonyPermission = true;
1103 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1104 hasWriteExternalStoragePermission = true;
1105 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1106 hasReadExternalStoragePermission = true;
1107 } else if (name == "android.permission.READ_PHONE_STATE") {
1108 hasReadPhoneStatePermission = true;
1109 } else if (name == "android.permission.READ_CONTACTS") {
1110 hasReadContactsPermission = true;
1111 } else if (name == "android.permission.WRITE_CONTACTS") {
1112 hasWriteContactsPermission = true;
1113 } else if (name == "android.permission.READ_CALL_LOG") {
1114 hasReadCallLogPermission = true;
1115 } else if (name == "android.permission.WRITE_CALL_LOG") {
1116 hasWriteCallLogPermission = true;
1117 }
1118 printf("uses-permission:'%s'\n", name.string());
1119 int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
1120 if (!req) {
1121 printf("optional-permission:'%s'\n", name.string());
1122 }
1123 } else {
1124 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1125 error.string());
1126 goto bail;
1127 }
1128 } else if (tag == "uses-package") {
1129 String8 name = getAttribute(tree, NAME_ATTR, &error);
1130 if (name != "" && error == "") {
1131 printf("uses-package:'%s'\n", name.string());
1132 } else {
1133 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1134 error.string());
1135 goto bail;
1136 }
1137 } else if (tag == "original-package") {
1138 String8 name = getAttribute(tree, NAME_ATTR, &error);
1139 if (name != "" && error == "") {
1140 printf("original-package:'%s'\n", name.string());
1141 } else {
1142 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1143 error.string());
1144 goto bail;
1145 }
1146 } else if (tag == "supports-gl-texture") {
1147 String8 name = getAttribute(tree, NAME_ATTR, &error);
1148 if (name != "" && error == "") {
1149 printf("supports-gl-texture:'%s'\n", name.string());
1150 } else {
1151 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1152 error.string());
1153 goto bail;
1154 }
1155 } else if (tag == "compatible-screens") {
1156 printCompatibleScreens(tree);
1157 depth--;
1158 } else if (tag == "package-verifier") {
1159 String8 name = getAttribute(tree, NAME_ATTR, &error);
1160 if (name != "" && error == "") {
1161 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1162 if (publicKey != "" && error == "") {
1163 printf("package-verifier: name='%s' publicKey='%s'\n",
1164 name.string(), publicKey.string());
1165 }
1166 }
1167 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001168 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001169 withinActivity = false;
1170 withinReceiver = false;
1171 withinService = false;
1172 hasIntentFilter = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001173 if (withinApplication) {
1174 if(tag == "activity") {
1175 withinActivity = true;
1176 activityName = getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001177 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001178 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1179 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001180 goto bail;
1181 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001182
Michael Wrightec4fdec2013-09-06 16:50:52 -07001183 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1184 if (error != "") {
1185 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1186 error.string());
1187 goto bail;
1188 }
1189
1190 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1191 if (error != "") {
1192 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1193 error.string());
1194 goto bail;
1195 }
1196
1197 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1198 SCREEN_ORIENTATION_ATTR, &error);
1199 if (error == "") {
1200 if (orien == 0 || orien == 6 || orien == 8) {
1201 // Requests landscape, sensorLandscape, or reverseLandscape.
1202 reqScreenLandscapeFeature = true;
1203 } else if (orien == 1 || orien == 7 || orien == 9) {
1204 // Requests portrait, sensorPortrait, or reversePortrait.
1205 reqScreenPortraitFeature = true;
1206 }
1207 }
1208 } else if (tag == "uses-library") {
1209 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1210 if (error != "") {
1211 fprintf(stderr,
1212 "ERROR getting 'android:name' attribute for uses-library"
1213 " %s\n", error.string());
1214 goto bail;
1215 }
1216 int req = getIntegerAttribute(tree,
1217 REQUIRED_ATTR, NULL, 1);
1218 printf("uses-library%s:'%s'\n",
1219 req ? "" : "-not-required", libraryName.string());
1220 } else if (tag == "receiver") {
1221 withinReceiver = true;
1222 receiverName = getAttribute(tree, NAME_ATTR, &error);
1223
1224 if (error != "") {
1225 fprintf(stderr,
1226 "ERROR getting 'android:name' attribute for receiver:"
1227 " %s\n", error.string());
1228 goto bail;
1229 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001230
1231 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1232 if (error == "") {
1233 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1234 hasBindDeviceAdminPermission = true;
1235 }
1236 } else {
1237 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1238 " receiver '%s': %s\n", receiverName.string(), error.string());
1239 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001240 } else if (tag == "service") {
1241 withinService = true;
1242 serviceName = getAttribute(tree, NAME_ATTR, &error);
1243
1244 if (error != "") {
1245 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1246 "service:%s\n", error.string());
1247 goto bail;
1248 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001249
1250 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1251 if (error == "") {
1252 if (permission == "android.permission.BIND_INPUT_METHOD") {
1253 hasBindInputMethodPermission = true;
1254 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1255 hasBindAccessibilityServicePermission = true;
1256 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1257 hasBindPrintServicePermission = true;
1258 }
1259 } else {
1260 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1261 " service '%s': %s\n", serviceName.string(), error.string());
1262 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001263 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
1264 String8 metaDataName = getAttribute(tree, NAME_ATTR, &error);
1265 if (error != "") {
1266 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1267 "meta-data:%s\n", error.string());
1268 goto bail;
1269 }
1270 printf("meta-data: name='%s' ", metaDataName.string());
1271 Res_value value;
1272 getResolvedResourceAttribute(&value, &res, tree, VALUE_ATTR, &error);
1273 if (error != "") {
1274 fprintf(stderr, "ERROR getting 'android:value' attribute for "
1275 "meta-data:%s\n", error.string());
1276 goto bail;
1277 }
1278 if (value.dataType == Res_value::TYPE_STRING) {
1279 String8 metaDataValue = getAttribute(tree, value.data, &error);
1280 if (error != "") {
1281 fprintf(stderr, "ERROR getting 'android:value' attribute for "
1282 "meta-data: %s\n", error.string());
1283 goto bail;
1284 }
1285 printf("value='%s'\n", metaDataValue.string());
1286 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
1287 value.dataType <= Res_value::TYPE_LAST_INT) {
1288 printf("value='%d'\n", value.data);
1289 } else {
1290 printf("value=(type 0x%x)0x%x",
1291 (int)value.dataType, (int)value.data);
1292 }
1293 } else if (withinSupportsInput && tag == "input-type") {
1294 String8 name = getAttribute(tree, NAME_ATTR, &error);
1295 if (name != "" && error == "") {
1296 supportedInput.add(name);
1297 } else {
1298 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1299 error.string());
1300 goto bail;
1301 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001302 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001303 }
1304 } else if ((depth == 4) && (tag == "intent-filter")) {
1305 hasIntentFilter = true;
1306 withinIntentFilter = true;
1307 actMainActivity = false;
1308 actWidgetReceivers = false;
1309 actImeService = false;
1310 actWallpaperService = false;
1311 actAccessibilityService = false;
1312 actPrintService = false;
1313 actDeviceAdminEnabled = false;
1314 } else if ((depth == 5) && withinIntentFilter) {
1315 String8 action;
1316 if (tag == "action") {
1317 action = getAttribute(tree, NAME_ATTR, &error);
1318 if (error != "") {
1319 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1320 error.string());
1321 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001322 }
1323
Adam Lesinskia5018c92013-09-30 16:23:15 -07001324 if (withinActivity) {
1325 if (action == "android.intent.action.MAIN") {
1326 isMainActivity = true;
1327 actMainActivity = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001328 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001329 } else if (withinReceiver) {
1330 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1331 actWidgetReceivers = true;
1332 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1333 actDeviceAdminEnabled = true;
1334 }
1335 } else if (withinService) {
1336 if (action == "android.view.InputMethod") {
1337 actImeService = true;
1338 } else if (action == "android.service.wallpaper.WallpaperService") {
1339 actWallpaperService = true;
1340 } else if (action == "android.accessibilityservice.AccessibilityService") {
1341 actAccessibilityService = true;
1342 } else if (action == "android.printservice.PrintService") {
1343 actPrintService = true;
1344 }
1345 }
1346 if (action == "android.intent.action.SEARCH") {
1347 isSearchable = true;
1348 }
1349 }
1350
1351 if (tag == "category") {
1352 String8 category = getAttribute(tree, NAME_ATTR, &error);
1353 if (error != "") {
1354 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1355 error.string());
1356 goto bail;
1357 }
1358 if (withinActivity) {
1359 if (category == "android.intent.category.LAUNCHER") {
1360 isLauncherActivity = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001361 }
1362 }
1363 }
1364 }
1365 }
1366
1367 // Pre-1.6 implicitly granted permission compatibility logic
1368 if (targetSdk < 4) {
1369 if (!hasWriteExternalStoragePermission) {
1370 printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
1371 printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \
1372 "'targetSdkVersion < 4'\n");
1373 hasWriteExternalStoragePermission = true;
1374 }
1375 if (!hasReadPhoneStatePermission) {
1376 printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
1377 printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \
1378 "'targetSdkVersion < 4'\n");
1379 }
1380 }
1381
1382 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1383 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1384 // do this (regardless of target API version) because we can't have
1385 // an app with write permission but not read permission.
1386 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
1387 printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n");
1388 printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \
1389 "'requested WRITE_EXTERNAL_STORAGE'\n");
1390 }
1391
1392 // Pre-JellyBean call log permission compatibility.
1393 if (targetSdk < 16) {
1394 if (!hasReadCallLogPermission && hasReadContactsPermission) {
1395 printf("uses-permission:'android.permission.READ_CALL_LOG'\n");
1396 printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \
1397 "'targetSdkVersion < 16 and requested READ_CONTACTS'\n");
1398 }
1399 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
1400 printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n");
1401 printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \
1402 "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n");
1403 }
1404 }
1405
1406 /* The following blocks handle printing "inferred" uses-features, based
1407 * on whether related features or permissions are used by the app.
1408 * Note that the various spec*Feature variables denote whether the
1409 * relevant tag was *present* in the AndroidManfest, not that it was
1410 * present and set to true.
1411 */
1412 // Camera-related back-compatibility logic
1413 if (!specCameraFeature) {
1414 if (reqCameraFlashFeature) {
1415 // if app requested a sub-feature (autofocus or flash) and didn't
1416 // request the base camera feature, we infer that it meant to
1417 printf("uses-feature:'android.hardware.camera'\n");
1418 printf("uses-implied-feature:'android.hardware.camera'," \
1419 "'requested android.hardware.camera.flash feature'\n");
1420 } else if (reqCameraAutofocusFeature) {
1421 // if app requested a sub-feature (autofocus or flash) and didn't
1422 // request the base camera feature, we infer that it meant to
1423 printf("uses-feature:'android.hardware.camera'\n");
1424 printf("uses-implied-feature:'android.hardware.camera'," \
1425 "'requested android.hardware.camera.autofocus feature'\n");
1426 } else if (hasCameraPermission) {
1427 // if app wants to use camera but didn't request the feature, we infer
1428 // that it meant to, and further that it wants autofocus
1429 // (which was the 1.0 - 1.5 behavior)
1430 printf("uses-feature:'android.hardware.camera'\n");
1431 if (!specCameraAutofocusFeature) {
1432 printf("uses-feature:'android.hardware.camera.autofocus'\n");
1433 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1434 "'requested android.permission.CAMERA permission'\n");
1435 }
1436 }
1437 }
1438
1439 // Location-related back-compatibility logic
1440 if (!specLocationFeature &&
1441 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1442 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1443 // if app either takes a location-related permission or requests one of the
1444 // sub-features, we infer that it also meant to request the base location feature
1445 printf("uses-feature:'android.hardware.location'\n");
1446 printf("uses-implied-feature:'android.hardware.location'," \
1447 "'requested a location access permission'\n");
1448 }
1449 if (!specGpsFeature && hasGpsPermission) {
1450 // if app takes GPS (FINE location) perm but does not request the GPS
1451 // feature, we infer that it meant to
1452 printf("uses-feature:'android.hardware.location.gps'\n");
1453 printf("uses-implied-feature:'android.hardware.location.gps'," \
1454 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
1455 }
1456 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1457 // if app takes Network location (COARSE location) perm but does not request the
1458 // network location feature, we infer that it meant to
1459 printf("uses-feature:'android.hardware.location.network'\n");
1460 printf("uses-implied-feature:'android.hardware.location.network'," \
1461 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
1462 }
1463
1464 // Bluetooth-related compatibility logic
1465 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
1466 // if app takes a Bluetooth permission but does not request the Bluetooth
1467 // feature, we infer that it meant to
1468 printf("uses-feature:'android.hardware.bluetooth'\n");
1469 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1470 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1471 "permission and targetSdkVersion > 4'\n");
1472 }
1473
1474 // Microphone-related compatibility logic
1475 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1476 // if app takes the record-audio permission but does not request the microphone
1477 // feature, we infer that it meant to
1478 printf("uses-feature:'android.hardware.microphone'\n");
1479 printf("uses-implied-feature:'android.hardware.microphone'," \
1480 "'requested android.permission.RECORD_AUDIO permission'\n");
1481 }
1482
1483 // WiFi-related compatibility logic
1484 if (!specWiFiFeature && hasWiFiPermission) {
1485 // if app takes one of the WiFi permissions but does not request the WiFi
1486 // feature, we infer that it meant to
1487 printf("uses-feature:'android.hardware.wifi'\n");
1488 printf("uses-implied-feature:'android.hardware.wifi'," \
1489 "'requested android.permission.ACCESS_WIFI_STATE, " \
1490 "android.permission.CHANGE_WIFI_STATE, or " \
1491 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
1492 }
1493
1494 // Telephony-related compatibility logic
1495 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1496 // if app takes one of the telephony permissions or requests a sub-feature but
1497 // does not request the base telephony feature, we infer that it meant to
1498 printf("uses-feature:'android.hardware.telephony'\n");
1499 printf("uses-implied-feature:'android.hardware.telephony'," \
1500 "'requested a telephony-related permission or feature'\n");
1501 }
1502
1503 // Touchscreen-related back-compatibility logic
1504 if (!specTouchscreenFeature) { // not a typo!
1505 // all apps are presumed to require a touchscreen, unless they explicitly say
1506 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1507 // Note that specTouchscreenFeature is true if the tag is present, regardless
1508 // of whether its value is true or false, so this is safe
1509 printf("uses-feature:'android.hardware.touchscreen'\n");
1510 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1511 "'assumed you require a touch screen unless explicitly made optional'\n");
1512 }
1513 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1514 // if app takes one of the telephony permissions or requests a sub-feature but
1515 // does not request the base telephony feature, we infer that it meant to
1516 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1517 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1518 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
1519 }
1520
1521 // Landscape/portrait-related compatibility logic
1522 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1523 // If the app has specified any activities in its manifest
1524 // that request a specific orientation, then assume that
1525 // orientation is required.
1526 if (reqScreenLandscapeFeature) {
1527 printf("uses-feature:'android.hardware.screen.landscape'\n");
1528 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1529 "'one or more activities have specified a landscape orientation'\n");
1530 }
1531 if (reqScreenPortraitFeature) {
1532 printf("uses-feature:'android.hardware.screen.portrait'\n");
1533 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1534 "'one or more activities have specified a portrait orientation'\n");
1535 }
1536 }
1537
1538 if (hasMainActivity) {
1539 printf("main\n");
1540 }
1541 if (hasWidgetReceivers) {
1542 printf("app-widget\n");
1543 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001544 if (hasDeviceAdminReceiver) {
1545 printf("device-admin\n");
1546 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001547 if (hasImeService) {
1548 printf("ime\n");
1549 }
1550 if (hasWallpaperService) {
1551 printf("wallpaper\n");
1552 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001553 if (hasAccessibilityService) {
1554 printf("accessibility\n");
1555 }
1556 if (hasPrintService) {
1557 printf("print\n");
1558 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001559 if (hasOtherActivities) {
1560 printf("other-activities\n");
1561 }
1562 if (isSearchable) {
1563 printf("search\n");
1564 }
1565 if (hasOtherReceivers) {
1566 printf("other-receivers\n");
1567 }
1568 if (hasOtherServices) {
1569 printf("other-services\n");
1570 }
1571
1572 // For modern apps, if screen size buckets haven't been specified
1573 // but the new width ranges have, then infer the buckets from them.
1574 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1575 && requiresSmallestWidthDp > 0) {
1576 int compatWidth = compatibleWidthLimitDp;
1577 if (compatWidth <= 0) {
1578 compatWidth = requiresSmallestWidthDp;
1579 }
1580 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1581 smallScreen = -1;
1582 } else {
1583 smallScreen = 0;
1584 }
1585 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1586 normalScreen = -1;
1587 } else {
1588 normalScreen = 0;
1589 }
1590 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1591 largeScreen = -1;
1592 } else {
1593 largeScreen = 0;
1594 }
1595 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1596 xlargeScreen = -1;
1597 } else {
1598 xlargeScreen = 0;
1599 }
1600 }
1601
1602 // Determine default values for any unspecified screen sizes,
1603 // based on the target SDK of the package. As of 4 (donut)
1604 // the screen size support was introduced, so all default to
1605 // enabled.
1606 if (smallScreen > 0) {
1607 smallScreen = targetSdk >= 4 ? -1 : 0;
1608 }
1609 if (normalScreen > 0) {
1610 normalScreen = -1;
1611 }
1612 if (largeScreen > 0) {
1613 largeScreen = targetSdk >= 4 ? -1 : 0;
1614 }
1615 if (xlargeScreen > 0) {
1616 // Introduced in Gingerbread.
1617 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1618 }
1619 if (anyDensity > 0) {
1620 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1621 || compatibleWidthLimitDp > 0) ? -1 : 0;
1622 }
1623 printf("supports-screens:");
1624 if (smallScreen != 0) {
1625 printf(" 'small'");
1626 }
1627 if (normalScreen != 0) {
1628 printf(" 'normal'");
1629 }
1630 if (largeScreen != 0) {
1631 printf(" 'large'");
1632 }
1633 if (xlargeScreen != 0) {
1634 printf(" 'xlarge'");
1635 }
1636 printf("\n");
1637 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1638 if (requiresSmallestWidthDp > 0) {
1639 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1640 }
1641 if (compatibleWidthLimitDp > 0) {
1642 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1643 }
1644 if (largestWidthLimitDp > 0) {
1645 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1646 }
1647
1648 printf("locales:");
1649 const size_t NL = locales.size();
1650 for (size_t i=0; i<NL; i++) {
1651 const char* localeStr = locales[i].string();
1652 if (localeStr == NULL || strlen(localeStr) == 0) {
1653 localeStr = "--_--";
1654 }
1655 printf(" '%s'", localeStr);
1656 }
1657 printf("\n");
1658
1659 printf("densities:");
1660 const size_t ND = densities.size();
1661 for (size_t i=0; i<ND; i++) {
1662 printf(" '%d'", densities[i]);
1663 }
1664 printf("\n");
1665
1666 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1667 if (dir != NULL) {
1668 if (dir->getFileCount() > 0) {
1669 printf("native-code:");
1670 for (size_t i=0; i<dir->getFileCount(); i++) {
1671 printf(" '%s'", dir->getFileName(i).string());
1672 }
1673 printf("\n");
1674 }
1675 delete dir;
1676 }
1677 } else if (strcmp("badger", option) == 0) {
1678 printf("%s", CONSOLE_DATA);
1679 } else if (strcmp("configurations", option) == 0) {
1680 Vector<ResTable_config> configs;
1681 res.getConfigurations(&configs);
1682 const size_t N = configs.size();
1683 for (size_t i=0; i<N; i++) {
1684 printf("%s\n", configs[i].toString().string());
1685 }
1686 } else {
1687 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
1688 goto bail;
1689 }
1690 }
1691
1692 result = NO_ERROR;
1693
1694bail:
1695 if (asset) {
1696 delete asset;
1697 }
1698 return (result != NO_ERROR);
1699}
1700
1701
1702/*
1703 * Handle the "add" command, which wants to add files to a new or
1704 * pre-existing archive.
1705 */
1706int doAdd(Bundle* bundle)
1707{
1708 ZipFile* zip = NULL;
1709 status_t result = UNKNOWN_ERROR;
1710 const char* zipFileName;
1711
1712 if (bundle->getUpdate()) {
1713 /* avoid confusion */
1714 fprintf(stderr, "ERROR: can't use '-u' with add\n");
1715 goto bail;
1716 }
1717
1718 if (bundle->getFileSpecCount() < 1) {
1719 fprintf(stderr, "ERROR: must specify zip file name\n");
1720 goto bail;
1721 }
1722 zipFileName = bundle->getFileSpecEntry(0);
1723
1724 if (bundle->getFileSpecCount() < 2) {
1725 fprintf(stderr, "NOTE: nothing to do\n");
1726 goto bail;
1727 }
1728
1729 zip = openReadWrite(zipFileName, true);
1730 if (zip == NULL) {
1731 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
1732 goto bail;
1733 }
1734
1735 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1736 const char* fileName = bundle->getFileSpecEntry(i);
1737
1738 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
1739 printf(" '%s'... (from gzip)\n", fileName);
1740 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
1741 } else {
1742 if (bundle->getJunkPath()) {
1743 String8 storageName = String8(fileName).getPathLeaf();
1744 printf(" '%s' as '%s'...\n", fileName, storageName.string());
1745 result = zip->add(fileName, storageName.string(),
1746 bundle->getCompressionMethod(), NULL);
1747 } else {
1748 printf(" '%s'...\n", fileName);
1749 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
1750 }
1751 }
1752 if (result != NO_ERROR) {
1753 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
1754 if (result == NAME_NOT_FOUND) {
1755 fprintf(stderr, ": file not found\n");
1756 } else if (result == ALREADY_EXISTS) {
1757 fprintf(stderr, ": already exists in archive\n");
1758 } else {
1759 fprintf(stderr, "\n");
1760 }
1761 goto bail;
1762 }
1763 }
1764
1765 result = NO_ERROR;
1766
1767bail:
1768 delete zip;
1769 return (result != NO_ERROR);
1770}
1771
1772
1773/*
1774 * Delete files from an existing archive.
1775 */
1776int doRemove(Bundle* bundle)
1777{
1778 ZipFile* zip = NULL;
1779 status_t result = UNKNOWN_ERROR;
1780 const char* zipFileName;
1781
1782 if (bundle->getFileSpecCount() < 1) {
1783 fprintf(stderr, "ERROR: must specify zip file name\n");
1784 goto bail;
1785 }
1786 zipFileName = bundle->getFileSpecEntry(0);
1787
1788 if (bundle->getFileSpecCount() < 2) {
1789 fprintf(stderr, "NOTE: nothing to do\n");
1790 goto bail;
1791 }
1792
1793 zip = openReadWrite(zipFileName, false);
1794 if (zip == NULL) {
1795 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
1796 zipFileName);
1797 goto bail;
1798 }
1799
1800 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
1801 const char* fileName = bundle->getFileSpecEntry(i);
1802 ZipEntry* entry;
1803
1804 entry = zip->getEntryByName(fileName);
1805 if (entry == NULL) {
1806 printf(" '%s' NOT FOUND\n", fileName);
1807 continue;
1808 }
1809
1810 result = zip->remove(entry);
1811
1812 if (result != NO_ERROR) {
1813 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
1814 bundle->getFileSpecEntry(i), zipFileName);
1815 goto bail;
1816 }
1817 }
1818
1819 /* update the archive */
1820 zip->flush();
1821
1822bail:
1823 delete zip;
1824 return (result != NO_ERROR);
1825}
1826
1827
1828/*
1829 * Package up an asset directory and associated application files.
1830 */
1831int doPackage(Bundle* bundle)
1832{
1833 const char* outputAPKFile;
1834 int retVal = 1;
1835 status_t err;
1836 sp<AaptAssets> assets;
1837 int N;
1838 FILE* fp;
1839 String8 dependencyFile;
1840
1841 // -c zz_ZZ means do pseudolocalization
1842 ResourceFilter filter;
1843 err = filter.parse(bundle->getConfigurations());
1844 if (err != NO_ERROR) {
1845 goto bail;
1846 }
1847 if (filter.containsPseudo()) {
1848 bundle->setPseudolocalize(true);
1849 }
1850
1851 N = bundle->getFileSpecCount();
1852 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
1853 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDir() == NULL) {
1854 fprintf(stderr, "ERROR: no input files\n");
1855 goto bail;
1856 }
1857
1858 outputAPKFile = bundle->getOutputAPKFile();
1859
1860 // Make sure the filenames provided exist and are of the appropriate type.
1861 if (outputAPKFile) {
1862 FileType type;
1863 type = getFileType(outputAPKFile);
1864 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
1865 fprintf(stderr,
1866 "ERROR: output file '%s' exists but is not regular file\n",
1867 outputAPKFile);
1868 goto bail;
1869 }
1870 }
1871
1872 // Load the assets.
1873 assets = new AaptAssets();
1874
1875 // Set up the resource gathering in assets if we're going to generate
1876 // dependency files. Every time we encounter a resource while slurping
1877 // the tree, we'll add it to these stores so we have full resource paths
1878 // to write to a dependency file.
1879 if (bundle->getGenDependencies()) {
1880 sp<FilePathStore> resPathStore = new FilePathStore;
1881 assets->setFullResPaths(resPathStore);
1882 sp<FilePathStore> assetPathStore = new FilePathStore;
1883 assets->setFullAssetPaths(assetPathStore);
1884 }
1885
1886 err = assets->slurpFromArgs(bundle);
1887 if (err < 0) {
1888 goto bail;
1889 }
1890
1891 if (bundle->getVerbose()) {
1892 assets->print(String8());
1893 }
1894
1895 // If they asked for any fileAs that need to be compiled, do so.
1896 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
1897 err = buildResources(bundle, assets);
1898 if (err != 0) {
1899 goto bail;
1900 }
1901 }
1902
1903 // At this point we've read everything and processed everything. From here
1904 // on out it's just writing output files.
1905 if (SourcePos::hasErrors()) {
1906 goto bail;
1907 }
1908
1909 // Update symbols with information about which ones are needed as Java symbols.
1910 assets->applyJavaSymbols();
1911 if (SourcePos::hasErrors()) {
1912 goto bail;
1913 }
1914
1915 // If we've been asked to generate a dependency file, do that here
1916 if (bundle->getGenDependencies()) {
1917 // If this is the packaging step, generate the dependency file next to
1918 // the output apk (e.g. bin/resources.ap_.d)
1919 if (outputAPKFile) {
1920 dependencyFile = String8(outputAPKFile);
1921 // Add the .d extension to the dependency file.
1922 dependencyFile.append(".d");
1923 } else {
1924 // Else if this is the R.java dependency generation step,
1925 // generate the dependency file in the R.java package subdirectory
1926 // e.g. gen/com/foo/app/R.java.d
1927 dependencyFile = String8(bundle->getRClassDir());
1928 dependencyFile.appendPath("R.java.d");
1929 }
1930 // Make sure we have a clean dependency file to start with
1931 fp = fopen(dependencyFile, "w");
1932 fclose(fp);
1933 }
1934
1935 // Write out R.java constants
1936 if (!assets->havePrivateSymbols()) {
1937 if (bundle->getCustomPackage() == NULL) {
1938 // Write the R.java file into the appropriate class directory
1939 // e.g. gen/com/foo/app/R.java
1940 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
1941 } else {
1942 const String8 customPkg(bundle->getCustomPackage());
1943 err = writeResourceSymbols(bundle, assets, customPkg, true);
1944 }
1945 if (err < 0) {
1946 goto bail;
1947 }
1948 // If we have library files, we're going to write our R.java file into
1949 // the appropriate class directory for those libraries as well.
1950 // e.g. gen/com/foo/app/lib/R.java
1951 if (bundle->getExtraPackages() != NULL) {
1952 // Split on colon
1953 String8 libs(bundle->getExtraPackages());
1954 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
1955 while (packageString != NULL) {
1956 // Write the R.java file out with the correct package name
1957 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
1958 if (err < 0) {
1959 goto bail;
1960 }
1961 packageString = strtok(NULL, ":");
1962 }
1963 libs.unlockBuffer();
1964 }
1965 } else {
1966 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
1967 if (err < 0) {
1968 goto bail;
1969 }
1970 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
1971 if (err < 0) {
1972 goto bail;
1973 }
1974 }
1975
1976 // Write out the ProGuard file
1977 err = writeProguardFile(bundle, assets);
1978 if (err < 0) {
1979 goto bail;
1980 }
1981
1982 // Write the apk
1983 if (outputAPKFile) {
1984 err = writeAPK(bundle, assets, String8(outputAPKFile));
1985 if (err != NO_ERROR) {
1986 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
1987 goto bail;
1988 }
1989 }
1990
1991 // If we've been asked to generate a dependency file, we need to finish up here.
1992 // the writeResourceSymbols and writeAPK functions have already written the target
1993 // half of the dependency file, now we need to write the prerequisites. (files that
1994 // the R.java file or .ap_ file depend on)
1995 if (bundle->getGenDependencies()) {
1996 // Now that writeResourceSymbols or writeAPK has taken care of writing
1997 // the targets to our dependency file, we'll write the prereqs
1998 fp = fopen(dependencyFile, "a+");
1999 fprintf(fp, " : ");
2000 bool includeRaw = (outputAPKFile != NULL);
2001 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2002 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2003 // and therefore was not added to our pathstores during slurping
2004 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2005 fclose(fp);
2006 }
2007
2008 retVal = 0;
2009bail:
2010 if (SourcePos::hasErrors()) {
2011 SourcePos::printErrors(stderr);
2012 }
2013 return retVal;
2014}
2015
2016/*
2017 * Do PNG Crunching
2018 * PRECONDITIONS
2019 * -S flag points to a source directory containing drawable* folders
2020 * -C flag points to destination directory. The folder structure in the
2021 * source directory will be mirrored to the destination (cache) directory
2022 *
2023 * POSTCONDITIONS
2024 * Destination directory will be updated to match the PNG files in
2025 * the source directory.
2026 */
2027int doCrunch(Bundle* bundle)
2028{
2029 fprintf(stdout, "Crunching PNG Files in ");
2030 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2031 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2032
2033 updatePreProcessedCache(bundle);
2034
2035 return NO_ERROR;
2036}
2037
2038/*
2039 * Do PNG Crunching on a single flag
2040 * -i points to a single png file
2041 * -o points to a single png output file
2042 */
2043int doSingleCrunch(Bundle* bundle)
2044{
2045 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2046 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2047
2048 String8 input(bundle->getSingleCrunchInputFile());
2049 String8 output(bundle->getSingleCrunchOutputFile());
2050
2051 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2052 // we can't return the status_t as it gets truncate to the lower 8 bits.
2053 return 42;
2054 }
2055
2056 return NO_ERROR;
2057}
2058
2059char CONSOLE_DATA[2925] = {
2060 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2061 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2062 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2063 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2064 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2065 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2066 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2067 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2068 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2069 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2070 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2071 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2072 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2073 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2074 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2075 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2076 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2077 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2078 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2079 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2080 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2081 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2082 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2083 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2084 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2085 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2086 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2087 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2088 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2089 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2090 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2091 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2092 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2093 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2094 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2095 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2096 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2097 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2098 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2099 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2100 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2101 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2102 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2103 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2104 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2105 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2106 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2107 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2108 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2109 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2110 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2111 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2112 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2113 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2114 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2115 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2116 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2117 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2118 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2119 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2120 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2121 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2122 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2123 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2124 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2125 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2126 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2127 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2128 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2129 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2130 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2131 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2132 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2133 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2134 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2135 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2136 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2137 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2138 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2139 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2140 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2141 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2142 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2143 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2144 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2145 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2146 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2147 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2148 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2149 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2150 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2151 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2152 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2153 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2154 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2155 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2156 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2157 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2158 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2159 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2160 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2161 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2162 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2163 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2164 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2165 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2166 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2167 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2168 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2169 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2170 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2171 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2172 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2173 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2174 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2175 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2176 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2177 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2178 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2179 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2180 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2181 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2182 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2183 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2184 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2185 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2186 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2187 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2188 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2189 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2190 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2191 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2192 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2193 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2194 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2195 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2196 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2197 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2198 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2199 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2200 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2201 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2202 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2203 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2204 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2205 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2206 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2207 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2208 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2209 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2210 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2211 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2212 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2213 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2214 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2215 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2216 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2217 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2218 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2219 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2220 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2221 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2222 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2223 };