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