blob: 816033ed5ab9349c653ff1daea29585784392c6a [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//
Adam Lesinskifab50872014-04-16 14:40:42 -07006#include "ApkBuilder.h"
Adam Lesinski282e1812014-01-23 18:17:42 -08007#include "Main.h"
8#include "Bundle.h"
9#include "ResourceFilter.h"
10#include "ResourceTable.h"
11#include "Images.h"
12#include "XMLNode.h"
13
14#include <utils/Log.h>
15#include <utils/threads.h>
16#include <utils/List.h>
17#include <utils/Errors.h>
18
19#include <fcntl.h>
20#include <errno.h>
21
22using namespace android;
23
24/*
25 * Show version info. All the cool kids do it.
26 */
27int doVersion(Bundle* bundle)
28{
29 if (bundle->getFileSpecCount() != 0) {
30 printf("(ignoring extra arguments)\n");
31 }
32 printf("Android Asset Packaging Tool, v0.2\n");
33
34 return 0;
35}
36
37
38/*
39 * Open the file read only. The call fails if the file doesn't exist.
40 *
41 * Returns NULL on failure.
42 */
43ZipFile* openReadOnly(const char* fileName)
44{
45 ZipFile* zip;
46 status_t result;
47
48 zip = new ZipFile;
49 result = zip->open(fileName, ZipFile::kOpenReadOnly);
50 if (result != NO_ERROR) {
51 if (result == NAME_NOT_FOUND) {
52 fprintf(stderr, "ERROR: '%s' not found\n", fileName);
53 } else if (result == PERMISSION_DENIED) {
54 fprintf(stderr, "ERROR: '%s' access denied\n", fileName);
55 } else {
56 fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n",
57 fileName);
58 }
59 delete zip;
60 return NULL;
61 }
62
63 return zip;
64}
65
66/*
67 * Open the file read-write. The file will be created if it doesn't
68 * already exist and "okayToCreate" is set.
69 *
70 * Returns NULL on failure.
71 */
72ZipFile* openReadWrite(const char* fileName, bool okayToCreate)
73{
74 ZipFile* zip = NULL;
75 status_t result;
76 int flags;
77
78 flags = ZipFile::kOpenReadWrite;
79 if (okayToCreate) {
80 flags |= ZipFile::kOpenCreate;
81 }
82
83 zip = new ZipFile;
84 result = zip->open(fileName, flags);
85 if (result != NO_ERROR) {
86 delete zip;
87 zip = NULL;
88 goto bail;
89 }
90
91bail:
92 return zip;
93}
94
95
96/*
97 * Return a short string describing the compression method.
98 */
99const char* compressionName(int method)
100{
101 if (method == ZipEntry::kCompressStored) {
102 return "Stored";
103 } else if (method == ZipEntry::kCompressDeflated) {
104 return "Deflated";
105 } else {
106 return "Unknown";
107 }
108}
109
110/*
111 * Return the percent reduction in size (0% == no compression).
112 */
113int calcPercent(long uncompressedLen, long compressedLen)
114{
115 if (!uncompressedLen) {
116 return 0;
117 } else {
118 return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5);
119 }
120}
121
122/*
123 * Handle the "list" command, which can be a simple file dump or
124 * a verbose listing.
125 *
126 * The verbose listing closely matches the output of the Info-ZIP "unzip"
127 * command.
128 */
129int doList(Bundle* bundle)
130{
131 int result = 1;
132 ZipFile* zip = NULL;
133 const ZipEntry* entry;
134 long totalUncLen, totalCompLen;
135 const char* zipFileName;
136
137 if (bundle->getFileSpecCount() != 1) {
138 fprintf(stderr, "ERROR: specify zip file name (only)\n");
139 goto bail;
140 }
141 zipFileName = bundle->getFileSpecEntry(0);
142
143 zip = openReadOnly(zipFileName);
144 if (zip == NULL) {
145 goto bail;
146 }
147
148 int count, i;
149
150 if (bundle->getVerbose()) {
151 printf("Archive: %s\n", zipFileName);
152 printf(
153 " Length Method Size Ratio Offset Date Time CRC-32 Name\n");
154 printf(
155 "-------- ------ ------- ----- ------- ---- ---- ------ ----\n");
156 }
157
158 totalUncLen = totalCompLen = 0;
159
160 count = zip->getNumEntries();
161 for (i = 0; i < count; i++) {
162 entry = zip->getEntryByIndex(i);
163 if (bundle->getVerbose()) {
164 char dateBuf[32];
165 time_t when;
166
167 when = entry->getModWhen();
168 strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
169 localtime(&when));
170
171 printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n",
172 (long) entry->getUncompressedLen(),
173 compressionName(entry->getCompressionMethod()),
174 (long) entry->getCompressedLen(),
175 calcPercent(entry->getUncompressedLen(),
176 entry->getCompressedLen()),
177 (size_t) entry->getLFHOffset(),
178 dateBuf,
179 entry->getCRC32(),
180 entry->getFileName());
181 } else {
182 printf("%s\n", entry->getFileName());
183 }
184
185 totalUncLen += entry->getUncompressedLen();
186 totalCompLen += entry->getCompressedLen();
187 }
188
189 if (bundle->getVerbose()) {
190 printf(
191 "-------- ------- --- -------\n");
192 printf("%8ld %7ld %2d%% %d files\n",
193 totalUncLen,
194 totalCompLen,
195 calcPercent(totalUncLen, totalCompLen),
196 zip->getNumEntries());
197 }
198
199 if (bundle->getAndroidList()) {
200 AssetManager assets;
201 if (!assets.addAssetPath(String8(zipFileName), NULL)) {
202 fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
203 goto bail;
204 }
205
206 const ResTable& res = assets.getResources(false);
207 if (&res == NULL) {
208 printf("\nNo resource table found.\n");
209 } else {
210#ifndef HAVE_ANDROID_OS
211 printf("\nResource table:\n");
212 res.print(false);
213#endif
214 }
215
216 Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
217 Asset::ACCESS_BUFFER);
218 if (manifestAsset == NULL) {
219 printf("\nNo AndroidManifest.xml found.\n");
220 } else {
221 printf("\nAndroid manifest:\n");
222 ResXMLTree tree;
223 tree.setTo(manifestAsset->getBuffer(true),
224 manifestAsset->getLength());
225 printXMLBlock(&tree);
226 }
227 delete manifestAsset;
228 }
229
230 result = 0;
231
232bail:
233 delete zip;
234 return result;
235}
236
237static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
238{
239 size_t N = tree.getAttributeCount();
240 for (size_t i=0; i<N; i++) {
241 if (tree.getAttributeNameResID(i) == attrRes) {
242 return (ssize_t)i;
243 }
244 }
245 return -1;
246}
247
248String8 getAttribute(const ResXMLTree& tree, const char* ns,
249 const char* attr, String8* outError)
250{
251 ssize_t idx = tree.indexOfAttribute(ns, attr);
252 if (idx < 0) {
253 return String8();
254 }
255 Res_value value;
256 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
257 if (value.dataType != Res_value::TYPE_STRING) {
258 if (outError != NULL) {
259 *outError = "attribute is not a string value";
260 }
261 return String8();
262 }
263 }
264 size_t len;
265 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
266 return str ? String8(str, len) : String8();
267}
268
269static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
270{
271 ssize_t idx = indexOfAttribute(tree, attrRes);
272 if (idx < 0) {
273 return String8();
274 }
275 Res_value value;
276 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
277 if (value.dataType != Res_value::TYPE_STRING) {
278 if (outError != NULL) {
279 *outError = "attribute is not a string value";
280 }
281 return String8();
282 }
283 }
284 size_t len;
285 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
286 return str ? String8(str, len) : String8();
287}
288
289static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
290 String8* outError, int32_t defValue = -1)
291{
292 ssize_t idx = indexOfAttribute(tree, attrRes);
293 if (idx < 0) {
294 return defValue;
295 }
296 Res_value value;
297 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
298 if (value.dataType < Res_value::TYPE_FIRST_INT
299 || value.dataType > Res_value::TYPE_LAST_INT) {
300 if (outError != NULL) {
301 *outError = "attribute is not an integer value";
302 }
303 return defValue;
304 }
305 }
306 return value.data;
307}
308
309static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
310 uint32_t attrRes, String8* outError, int32_t defValue = -1)
311{
312 ssize_t idx = indexOfAttribute(tree, attrRes);
313 if (idx < 0) {
314 return defValue;
315 }
316 Res_value value;
317 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
318 if (value.dataType == Res_value::TYPE_REFERENCE) {
319 resTable->resolveReference(&value, 0);
320 }
321 if (value.dataType < Res_value::TYPE_FIRST_INT
322 || value.dataType > Res_value::TYPE_LAST_INT) {
323 if (outError != NULL) {
324 *outError = "attribute is not an integer value";
325 }
326 return defValue;
327 }
328 }
329 return value.data;
330}
331
332static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
333 uint32_t attrRes, String8* outError)
334{
335 ssize_t idx = indexOfAttribute(tree, attrRes);
336 if (idx < 0) {
337 return String8();
338 }
339 Res_value value;
340 if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
341 if (value.dataType == Res_value::TYPE_STRING) {
342 size_t len;
343 const uint16_t* str = tree.getAttributeStringValue(idx, &len);
344 return str ? String8(str, len) : String8();
345 }
346 resTable->resolveReference(&value, 0);
347 if (value.dataType != Res_value::TYPE_STRING) {
348 if (outError != NULL) {
349 *outError = "attribute is not a string value";
350 }
351 return String8();
352 }
353 }
354 size_t len;
355 const Res_value* value2 = &value;
356 const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
357 return str ? String8(str, len) : String8();
358}
359
360static void getResolvedResourceAttribute(Res_value* value, const ResTable* resTable,
361 const ResXMLTree& tree, uint32_t attrRes, String8* outError)
362{
363 ssize_t idx = indexOfAttribute(tree, attrRes);
364 if (idx < 0) {
365 if (outError != NULL) {
366 *outError = "attribute could not be found";
367 }
368 return;
369 }
370 if (tree.getAttributeValue(idx, value) != NO_ERROR) {
371 if (value->dataType == Res_value::TYPE_REFERENCE) {
372 resTable->resolveReference(value, 0);
373 }
374 // The attribute was found and was resolved if need be.
375 return;
376 }
377 if (outError != NULL) {
378 *outError = "error getting resolved resource attribute";
379 }
380}
381
Maurice Chu76327312013-10-16 18:28:46 -0700382static void printResolvedResourceAttribute(const ResTable* resTable, const ResXMLTree& tree,
383 uint32_t attrRes, String8 attrLabel, String8* outError)
384{
385 Res_value value;
386 getResolvedResourceAttribute(&value, resTable, tree, attrRes, outError);
387 if (*outError != "") {
388 *outError = "error print resolved resource attribute";
389 return;
390 }
391 if (value.dataType == Res_value::TYPE_STRING) {
392 String8 result = getResolvedAttribute(resTable, tree, attrRes, outError);
Maurice Chu2675f762013-10-22 17:33:11 -0700393 printf("%s='%s'", attrLabel.string(),
394 ResTable::normalizeForOutput(result.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -0700395 } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
396 value.dataType <= Res_value::TYPE_LAST_INT) {
397 printf("%s='%d'", attrLabel.string(), value.data);
398 } else {
399 printf("%s='0x%x'", attrLabel.string(), (int)value.data);
400 }
401}
402
Adam Lesinski282e1812014-01-23 18:17:42 -0800403// These are attribute resource constants for the platform, as found
404// in android.R.attr
405enum {
406 LABEL_ATTR = 0x01010001,
407 ICON_ATTR = 0x01010002,
408 NAME_ATTR = 0x01010003,
Adam Lesinskia5018c92013-09-30 16:23:15 -0700409 PERMISSION_ATTR = 0x01010006,
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700410 EXPORTED_ATTR = 0x01010010,
411 GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700412 RESOURCE_ATTR = 0x01010025,
Adam Lesinski282e1812014-01-23 18:17:42 -0800413 DEBUGGABLE_ATTR = 0x0101000f,
414 VALUE_ATTR = 0x01010024,
415 VERSION_CODE_ATTR = 0x0101021b,
416 VERSION_NAME_ATTR = 0x0101021c,
417 SCREEN_ORIENTATION_ATTR = 0x0101001e,
418 MIN_SDK_VERSION_ATTR = 0x0101020c,
419 MAX_SDK_VERSION_ATTR = 0x01010271,
420 REQ_TOUCH_SCREEN_ATTR = 0x01010227,
421 REQ_KEYBOARD_TYPE_ATTR = 0x01010228,
422 REQ_HARD_KEYBOARD_ATTR = 0x01010229,
423 REQ_NAVIGATION_ATTR = 0x0101022a,
424 REQ_FIVE_WAY_NAV_ATTR = 0x01010232,
425 TARGET_SDK_VERSION_ATTR = 0x01010270,
426 TEST_ONLY_ATTR = 0x01010272,
427 ANY_DENSITY_ATTR = 0x0101026c,
428 GL_ES_VERSION_ATTR = 0x01010281,
429 SMALL_SCREEN_ATTR = 0x01010284,
430 NORMAL_SCREEN_ATTR = 0x01010285,
431 LARGE_SCREEN_ATTR = 0x01010286,
432 XLARGE_SCREEN_ATTR = 0x010102bf,
433 REQUIRED_ATTR = 0x0101028e,
434 SCREEN_SIZE_ATTR = 0x010102ca,
435 SCREEN_DENSITY_ATTR = 0x010102cb,
436 REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
437 COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
438 LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
439 PUBLIC_KEY_ATTR = 0x010103a6,
Adam Lesinski94fc9122013-09-30 17:16:09 -0700440 CATEGORY_ATTR = 0x010103e8,
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800441 BANNER_ATTR = 0x10103f2,
Adam Lesinski282e1812014-01-23 18:17:42 -0800442};
443
Maurice Chu2675f762013-10-22 17:33:11 -0700444String8 getComponentName(String8 &pkgName, String8 &componentName) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800445 ssize_t idx = componentName.find(".");
446 String8 retStr(pkgName);
447 if (idx == 0) {
448 retStr += componentName;
449 } else if (idx < 0) {
450 retStr += ".";
451 retStr += componentName;
452 } else {
Maurice Chu2675f762013-10-22 17:33:11 -0700453 return componentName;
Adam Lesinski282e1812014-01-23 18:17:42 -0800454 }
Maurice Chu2675f762013-10-22 17:33:11 -0700455 return retStr;
Adam Lesinski282e1812014-01-23 18:17:42 -0800456}
457
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700458static void printCompatibleScreens(ResXMLTree& tree, String8* outError) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800459 size_t len;
460 ResXMLTree::event_code_t code;
461 int depth = 0;
462 bool first = true;
463 printf("compatible-screens:");
464 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
465 if (code == ResXMLTree::END_TAG) {
466 depth--;
467 if (depth < 0) {
468 break;
469 }
470 continue;
471 }
472 if (code != ResXMLTree::START_TAG) {
473 continue;
474 }
475 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700476 const char16_t* ctag16 = tree.getElementName(&len);
477 if (ctag16 == NULL) {
478 *outError = "failed to get XML element name (bad string pool)";
479 return;
480 }
481 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800482 if (tag == "screen") {
483 int32_t screenSize = getIntegerAttribute(tree,
484 SCREEN_SIZE_ATTR, NULL, -1);
485 int32_t screenDensity = getIntegerAttribute(tree,
486 SCREEN_DENSITY_ATTR, NULL, -1);
487 if (screenSize > 0 && screenDensity > 0) {
488 if (!first) {
489 printf(",");
490 }
491 first = false;
492 printf("'%d/%d'", screenSize, screenDensity);
493 }
494 }
495 }
496 printf("\n");
497}
498
Adam Lesinski58f1f362013-11-12 12:59:08 -0800499static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
500 printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
501 if (maxSdkVersion != -1) {
502 printf(" maxSdkVersion='%d'", maxSdkVersion);
503 }
504 printf("\n");
505
506 if (optional) {
507 printf("optional-permission: name='%s'",
508 ResTable::normalizeForOutput(name.string()).string());
509 if (maxSdkVersion != -1) {
510 printf(" maxSdkVersion='%d'", maxSdkVersion);
511 }
512 printf("\n");
513 }
514}
515
516static void printUsesImpliedPermission(const String8& name, const String8& reason) {
517 printf("uses-implied-permission: name='%s' reason='%s'\n",
518 ResTable::normalizeForOutput(name.string()).string(),
519 ResTable::normalizeForOutput(reason.string()).string());
520}
521
Adam Lesinski94fc9122013-09-30 17:16:09 -0700522Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
523 String8 *outError = NULL)
524{
525 Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
526 if (aidAsset == NULL) {
527 if (outError != NULL) *outError = "xml resource does not exist";
528 return Vector<String8>();
529 }
530
531 const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
532
533 bool withinApduService = false;
534 Vector<String8> categories;
535
536 String8 error;
537 ResXMLTree tree;
538 tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
539
540 size_t len;
541 int depth = 0;
542 ResXMLTree::event_code_t code;
543 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
544 if (code == ResXMLTree::END_TAG) {
545 depth--;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700546 const char16_t* ctag16 = tree.getElementName(&len);
547 if (ctag16 == NULL) {
548 *outError = "failed to get XML element name (bad string pool)";
549 return Vector<String8>();
550 }
551 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700552
553 if (depth == 0 && tag == serviceTagName) {
554 withinApduService = false;
555 }
556
557 } else if (code == ResXMLTree::START_TAG) {
558 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700559 const char16_t* ctag16 = tree.getElementName(&len);
560 if (ctag16 == NULL) {
561 *outError = "failed to get XML element name (bad string pool)";
562 return Vector<String8>();
563 }
564 String8 tag(ctag16);
Adam Lesinski94fc9122013-09-30 17:16:09 -0700565
566 if (depth == 1) {
567 if (tag == serviceTagName) {
568 withinApduService = true;
569 }
570 } else if (depth == 2 && withinApduService) {
571 if (tag == "aid-group") {
572 String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
573 if (error != "") {
574 if (outError != NULL) *outError = error;
575 return Vector<String8>();
576 }
577
578 categories.add(category);
579 }
580 }
581 }
582 }
583 aidAsset->close();
584 return categories;
585}
586
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700587static void printComponentPresence(const char* componentName) {
588 printf("provides-component:'%s'\n", componentName);
589}
590
Adam Lesinski282e1812014-01-23 18:17:42 -0800591/*
592 * Handle the "dump" command, to extract select data from an archive.
593 */
594extern char CONSOLE_DATA[2925]; // see EOF
595int doDump(Bundle* bundle)
596{
597 status_t result = UNKNOWN_ERROR;
598 Asset* asset = NULL;
599
600 if (bundle->getFileSpecCount() < 1) {
601 fprintf(stderr, "ERROR: no dump option specified\n");
602 return 1;
603 }
604
605 if (bundle->getFileSpecCount() < 2) {
606 fprintf(stderr, "ERROR: no dump file specified\n");
607 return 1;
608 }
609
610 const char* option = bundle->getFileSpecEntry(0);
611 const char* filename = bundle->getFileSpecEntry(1);
612
613 AssetManager assets;
Narayan Kamathf85e41f2014-01-24 14:14:30 +0000614 int32_t assetsCookie;
Adam Lesinski282e1812014-01-23 18:17:42 -0800615 if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
616 fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
617 return 1;
618 }
619
620 // Make a dummy config for retrieving resources... we need to supply
621 // non-default values for some configs so that we can retrieve resources
622 // in the app that don't have a default. The most important of these is
623 // the API version because key resources like icons will have an implicit
624 // version if they are using newer config types like density.
625 ResTable_config config;
Narayan Kamath91447d82014-01-21 15:32:36 +0000626 memset(&config, 0, sizeof(ResTable_config));
Adam Lesinski282e1812014-01-23 18:17:42 -0800627 config.language[0] = 'e';
628 config.language[1] = 'n';
629 config.country[0] = 'U';
630 config.country[1] = 'S';
631 config.orientation = ResTable_config::ORIENTATION_PORT;
632 config.density = ResTable_config::DENSITY_MEDIUM;
633 config.sdkVersion = 10000; // Very high.
634 config.screenWidthDp = 320;
635 config.screenHeightDp = 480;
636 config.smallestScreenWidthDp = 320;
637 assets.setConfiguration(config);
638
639 const ResTable& res = assets.getResources(false);
640 if (&res == NULL) {
641 fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
642 goto bail;
Adam Lesinski25e9d552014-05-19 15:01:43 -0700643 } else if (res.getError() != NO_ERROR) {
644 fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
645 goto bail;
Adam Lesinski282e1812014-01-23 18:17:42 -0800646 }
647
648 if (strcmp("resources", option) == 0) {
649#ifndef HAVE_ANDROID_OS
650 res.print(bundle->getValues());
651#endif
652
653 } else if (strcmp("strings", option) == 0) {
654 const ResStringPool* pool = res.getTableStringBlock(0);
655 printStringPool(pool);
656
657 } else if (strcmp("xmltree", option) == 0) {
658 if (bundle->getFileSpecCount() < 3) {
659 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
660 goto bail;
661 }
662
663 for (int i=2; i<bundle->getFileSpecCount(); i++) {
664 const char* resname = bundle->getFileSpecEntry(i);
665 ResXMLTree tree;
666 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
667 if (asset == NULL) {
668 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
669 goto bail;
670 }
671
672 if (tree.setTo(asset->getBuffer(true),
673 asset->getLength()) != NO_ERROR) {
674 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
675 goto bail;
676 }
677 tree.restart();
678 printXMLBlock(&tree);
679 tree.uninit();
680 delete asset;
681 asset = NULL;
682 }
683
684 } else if (strcmp("xmlstrings", option) == 0) {
685 if (bundle->getFileSpecCount() < 3) {
686 fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
687 goto bail;
688 }
689
690 for (int i=2; i<bundle->getFileSpecCount(); i++) {
691 const char* resname = bundle->getFileSpecEntry(i);
692 ResXMLTree tree;
693 asset = assets.openNonAsset(resname, Asset::ACCESS_BUFFER);
694 if (asset == NULL) {
695 fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
696 goto bail;
697 }
698
699 if (tree.setTo(asset->getBuffer(true),
700 asset->getLength()) != NO_ERROR) {
701 fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
702 goto bail;
703 }
704 printStringPool(&tree.getStrings());
705 delete asset;
706 asset = NULL;
707 }
708
709 } else {
710 ResXMLTree tree;
711 asset = assets.openNonAsset("AndroidManifest.xml",
712 Asset::ACCESS_BUFFER);
713 if (asset == NULL) {
714 fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
715 goto bail;
716 }
717
718 if (tree.setTo(asset->getBuffer(true),
719 asset->getLength()) != NO_ERROR) {
720 fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
721 goto bail;
722 }
723 tree.restart();
724
725 if (strcmp("permissions", option) == 0) {
726 size_t len;
727 ResXMLTree::event_code_t code;
728 int depth = 0;
729 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
730 if (code == ResXMLTree::END_TAG) {
731 depth--;
732 continue;
733 }
734 if (code != ResXMLTree::START_TAG) {
735 continue;
736 }
737 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -0700738 const char16_t* ctag16 = tree.getElementName(&len);
739 if (ctag16 == NULL) {
740 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
741 goto bail;
742 }
743 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -0800744 //printf("Depth %d tag %s\n", depth, tag.string());
745 if (depth == 1) {
746 if (tag != "manifest") {
747 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
748 goto bail;
749 }
750 String8 pkg = getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -0700751 printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800752 } else if (depth == 2 && tag == "permission") {
753 String8 error;
754 String8 name = getAttribute(tree, NAME_ATTR, &error);
755 if (error != "") {
756 fprintf(stderr, "ERROR: %s\n", error.string());
757 goto bail;
758 }
Maurice Chu2675f762013-10-22 17:33:11 -0700759 printf("permission: %s\n",
760 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800761 } else if (depth == 2 && tag == "uses-permission") {
762 String8 error;
763 String8 name = getAttribute(tree, NAME_ATTR, &error);
764 if (error != "") {
765 fprintf(stderr, "ERROR: %s\n", error.string());
766 goto bail;
767 }
Adam Lesinski58f1f362013-11-12 12:59:08 -0800768 printUsesPermission(name,
769 getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
770 getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
Adam Lesinski282e1812014-01-23 18:17:42 -0800771 }
772 }
773 } else if (strcmp("badging", option) == 0) {
774 Vector<String8> locales;
775 res.getLocales(&locales);
776
777 Vector<ResTable_config> configs;
778 res.getConfigurations(&configs);
779 SortedVector<int> densities;
780 const size_t NC = configs.size();
781 for (size_t i=0; i<NC; i++) {
782 int dens = configs[i].density;
783 if (dens == 0) {
784 dens = 160;
785 }
786 densities.add(dens);
787 }
788
789 size_t len;
790 ResXMLTree::event_code_t code;
791 int depth = 0;
792 String8 error;
793 bool withinActivity = false;
794 bool isMainActivity = false;
795 bool isLauncherActivity = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800796 bool isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800797 bool isSearchable = false;
798 bool withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700799 bool withinSupportsInput = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800800 bool withinReceiver = false;
801 bool withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700802 bool withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800803 bool withinIntentFilter = false;
804 bool hasMainActivity = false;
805 bool hasOtherActivities = false;
806 bool hasOtherReceivers = false;
807 bool hasOtherServices = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700808 bool hasIntentFilter = false;
809
Adam Lesinski282e1812014-01-23 18:17:42 -0800810 bool hasWallpaperService = false;
811 bool hasImeService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700812 bool hasAccessibilityService = false;
813 bool hasPrintService = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800814 bool hasWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700815 bool hasDeviceAdminReceiver = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700816 bool hasPaymentService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700817 bool hasDocumentsProvider = false;
818 bool hasCameraActivity = false;
819 bool hasCameraSecureActivity = false;
820 bool hasLauncher = false;
821 bool hasNotificationListenerService = false;
822
Adam Lesinski282e1812014-01-23 18:17:42 -0800823 bool actMainActivity = false;
824 bool actWidgetReceivers = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700825 bool actDeviceAdminEnabled = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800826 bool actImeService = false;
827 bool actWallpaperService = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700828 bool actAccessibilityService = false;
829 bool actPrintService = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700830 bool actHostApduService = false;
831 bool actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700832 bool actDocumentsProvider = false;
833 bool actNotificationListenerService = false;
834 bool actCamera = false;
835 bool actCameraSecure = false;
836 bool catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700837 bool hasMetaHostPaymentCategory = false;
838 bool hasMetaOffHostPaymentCategory = false;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700839
840 // These permissions are required by services implementing services
841 // the system binds to (IME, Accessibility, PrintServices, etc.)
842 bool hasBindDeviceAdminPermission = false;
843 bool hasBindInputMethodPermission = false;
844 bool hasBindAccessibilityServicePermission = false;
845 bool hasBindPrintServicePermission = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700846 bool hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700847 bool hasRequiredSafAttributes = false;
848 bool hasBindNotificationListenerServicePermission = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800849
850 // These two implement the implicit permissions that are granted
851 // to pre-1.6 applications.
852 bool hasWriteExternalStoragePermission = false;
853 bool hasReadPhoneStatePermission = false;
854
855 // If an app requests write storage, they will also get read storage.
856 bool hasReadExternalStoragePermission = false;
857
858 // Implement transition to read and write call log.
859 bool hasReadContactsPermission = false;
860 bool hasWriteContactsPermission = false;
861 bool hasReadCallLogPermission = false;
862 bool hasWriteCallLogPermission = false;
863
864 // This next group of variables is used to implement a group of
865 // backward-compatibility heuristics necessitated by the addition of
866 // some new uses-feature constants in 2.1 and 2.2. In most cases, the
867 // heuristic is "if an app requests a permission but doesn't explicitly
868 // request the corresponding <uses-feature>, presume it's there anyway".
869 bool specCameraFeature = false; // camera-related
870 bool specCameraAutofocusFeature = false;
871 bool reqCameraAutofocusFeature = false;
872 bool reqCameraFlashFeature = false;
873 bool hasCameraPermission = false;
874 bool specLocationFeature = false; // location-related
875 bool specNetworkLocFeature = false;
876 bool reqNetworkLocFeature = false;
877 bool specGpsFeature = false;
878 bool reqGpsFeature = false;
879 bool hasMockLocPermission = false;
880 bool hasCoarseLocPermission = false;
881 bool hasGpsPermission = false;
882 bool hasGeneralLocPermission = false;
883 bool specBluetoothFeature = false; // Bluetooth API-related
884 bool hasBluetoothPermission = false;
885 bool specMicrophoneFeature = false; // microphone-related
886 bool hasRecordAudioPermission = false;
887 bool specWiFiFeature = false;
888 bool hasWiFiPermission = false;
889 bool specTelephonyFeature = false; // telephony-related
890 bool reqTelephonySubFeature = false;
891 bool hasTelephonyPermission = false;
892 bool specTouchscreenFeature = false; // touchscreen-related
893 bool specMultitouchFeature = false;
894 bool reqDistinctMultitouchFeature = false;
895 bool specScreenPortraitFeature = false;
896 bool specScreenLandscapeFeature = false;
897 bool reqScreenPortraitFeature = false;
898 bool reqScreenLandscapeFeature = false;
899 // 2.2 also added some other features that apps can request, but that
900 // have no corresponding permission, so we cannot implement any
901 // back-compatibility heuristic for them. The below are thus unnecessary
902 // (but are retained here for documentary purposes.)
903 //bool specCompassFeature = false;
904 //bool specAccelerometerFeature = false;
905 //bool specProximityFeature = false;
906 //bool specAmbientLightFeature = false;
907 //bool specLiveWallpaperFeature = false;
908
909 int targetSdk = 0;
910 int smallScreen = 1;
911 int normalScreen = 1;
912 int largeScreen = 1;
913 int xlargeScreen = 1;
914 int anyDensity = 1;
915 int requiresSmallestWidthDp = 0;
916 int compatibleWidthLimitDp = 0;
917 int largestWidthLimitDp = 0;
918 String8 pkg;
919 String8 activityName;
920 String8 activityLabel;
921 String8 activityIcon;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800922 String8 activityBanner;
Adam Lesinski282e1812014-01-23 18:17:42 -0800923 String8 receiverName;
924 String8 serviceName;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700925 Vector<String8> supportedInput;
Adam Lesinski282e1812014-01-23 18:17:42 -0800926 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
927 if (code == ResXMLTree::END_TAG) {
928 depth--;
929 if (depth < 2) {
Michael Wrightec4fdec2013-09-06 16:50:52 -0700930 if (withinSupportsInput && !supportedInput.isEmpty()) {
931 printf("supports-input: '");
932 const size_t N = supportedInput.size();
933 for (size_t i=0; i<N; i++) {
Maurice Chu2675f762013-10-22 17:33:11 -0700934 printf("%s", ResTable::normalizeForOutput(
935 supportedInput[i].string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -0700936 if (i != N - 1) {
937 printf("' '");
938 } else {
939 printf("'\n");
940 }
941 }
942 supportedInput.clear();
943 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800944 withinApplication = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -0700945 withinSupportsInput = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800946 } else if (depth < 3) {
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800947 if (withinActivity && isMainActivity) {
Maurice Chu2675f762013-10-22 17:33:11 -0700948 String8 aName(getComponentName(pkg, activityName));
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800949 if (isLauncherActivity) {
950 printf("launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800951 if (aName.length() > 0) {
952 printf(" name='%s' ",
953 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800954 }
955 printf(" label='%s' icon='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800956 ResTable::normalizeForOutput(activityLabel.string()).string(),
957 ResTable::normalizeForOutput(activityIcon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -0800958 }
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800959 if (isLeanbackLauncherActivity) {
960 printf("leanback-launchable-activity:");
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800961 if (aName.length() > 0) {
962 printf(" name='%s' ",
963 ResTable::normalizeForOutput(aName.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800964 }
965 printf(" label='%s' icon='%s' banner='%s'\n",
Tim Kilbourn9eaaaf02014-03-07 23:04:03 -0800966 ResTable::normalizeForOutput(activityLabel.string()).string(),
967 ResTable::normalizeForOutput(activityIcon.string()).string(),
968 ResTable::normalizeForOutput(activityBanner.string()).string());
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800969 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800970 }
971 if (!hasIntentFilter) {
972 hasOtherActivities |= withinActivity;
973 hasOtherReceivers |= withinReceiver;
974 hasOtherServices |= withinService;
Adam Lesinski94fc9122013-09-30 17:16:09 -0700975 } else {
976 if (withinService) {
977 hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
978 hasBindNfcServicePermission);
979 hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
980 hasBindNfcServicePermission);
981 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800982 }
983 withinActivity = false;
984 withinService = false;
985 withinReceiver = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700986 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800987 hasIntentFilter = false;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -0800988 isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
Adam Lesinski282e1812014-01-23 18:17:42 -0800989 } else if (depth < 4) {
990 if (withinIntentFilter) {
991 if (withinActivity) {
992 hasMainActivity |= actMainActivity;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -0700993 hasLauncher |= catLauncher;
994 hasCameraActivity |= actCamera;
995 hasCameraSecureActivity |= actCameraSecure;
996 hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
Adam Lesinski282e1812014-01-23 18:17:42 -0800997 } else if (withinReceiver) {
998 hasWidgetReceivers |= actWidgetReceivers;
Adam Lesinskia5018c92013-09-30 16:23:15 -0700999 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
1000 hasBindDeviceAdminPermission);
1001 hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
Adam Lesinski282e1812014-01-23 18:17:42 -08001002 } else if (withinService) {
1003 hasImeService |= actImeService;
1004 hasWallpaperService |= actWallpaperService;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001005 hasAccessibilityService |= (actAccessibilityService &&
1006 hasBindAccessibilityServicePermission);
1007 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001008 hasNotificationListenerService |= actNotificationListenerService &&
1009 hasBindNotificationListenerServicePermission;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001010 hasOtherServices |= (!actImeService && !actWallpaperService &&
Adam Lesinski94fc9122013-09-30 17:16:09 -07001011 !actAccessibilityService && !actPrintService &&
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001012 !actHostApduService && !actOffHostApduService &&
1013 !actNotificationListenerService);
1014 } else if (withinProvider) {
1015 hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
Adam Lesinski282e1812014-01-23 18:17:42 -08001016 }
1017 }
1018 withinIntentFilter = false;
1019 }
1020 continue;
1021 }
1022 if (code != ResXMLTree::START_TAG) {
1023 continue;
1024 }
1025 depth++;
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001026
1027 const char16_t* ctag16 = tree.getElementName(&len);
1028 if (ctag16 == NULL) {
1029 fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
1030 goto bail;
1031 }
1032 String8 tag(ctag16);
Adam Lesinski282e1812014-01-23 18:17:42 -08001033 //printf("Depth %d, %s\n", depth, tag.string());
1034 if (depth == 1) {
1035 if (tag != "manifest") {
1036 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
1037 goto bail;
1038 }
1039 pkg = getAttribute(tree, NULL, "package", NULL);
Maurice Chu2675f762013-10-22 17:33:11 -07001040 printf("package: name='%s' ",
1041 ResTable::normalizeForOutput(pkg.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001042 int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
1043 if (error != "") {
1044 fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
1045 goto bail;
1046 }
1047 if (versionCode > 0) {
1048 printf("versionCode='%d' ", versionCode);
1049 } else {
1050 printf("versionCode='' ");
1051 }
1052 String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
1053 if (error != "") {
1054 fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
1055 goto bail;
1056 }
Maurice Chu2675f762013-10-22 17:33:11 -07001057 printf("versionName='%s'\n",
1058 ResTable::normalizeForOutput(versionName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001059 } else if (depth == 2) {
1060 withinApplication = false;
1061 if (tag == "application") {
1062 withinApplication = true;
1063
1064 String8 label;
1065 const size_t NL = locales.size();
1066 for (size_t i=0; i<NL; i++) {
1067 const char* localeStr = locales[i].string();
1068 assets.setLocale(localeStr != NULL ? localeStr : "");
1069 String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1070 if (llabel != "") {
1071 if (localeStr == NULL || strlen(localeStr) == 0) {
1072 label = llabel;
Maurice Chu2675f762013-10-22 17:33:11 -07001073 printf("application-label:'%s'\n",
1074 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001075 } else {
1076 if (label == "") {
1077 label = llabel;
1078 }
1079 printf("application-label-%s:'%s'\n", localeStr,
Maurice Chu2675f762013-10-22 17:33:11 -07001080 ResTable::normalizeForOutput(llabel.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001081 }
1082 }
1083 }
1084
1085 ResTable_config tmpConfig = config;
1086 const size_t ND = densities.size();
1087 for (size_t i=0; i<ND; i++) {
1088 tmpConfig.density = densities[i];
1089 assets.setConfiguration(tmpConfig);
1090 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1091 if (icon != "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001092 printf("application-icon-%d:'%s'\n", densities[i],
1093 ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001094 }
1095 }
1096 assets.setConfiguration(config);
1097
1098 String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1099 if (error != "") {
1100 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
1101 goto bail;
1102 }
1103 int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
1104 if (error != "") {
1105 fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
1106 goto bail;
1107 }
Maurice Chu2675f762013-10-22 17:33:11 -07001108 printf("application: label='%s' ",
1109 ResTable::normalizeForOutput(label.string()).string());
1110 printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001111 if (testOnly != 0) {
1112 printf("testOnly='%d'\n", testOnly);
1113 }
1114
1115 int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
1116 if (error != "") {
1117 fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
1118 goto bail;
1119 }
1120 if (debuggable != 0) {
1121 printf("application-debuggable\n");
1122 }
1123 } else if (tag == "uses-sdk") {
1124 int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
1125 if (error != "") {
1126 error = "";
1127 String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
1128 if (error != "") {
1129 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
1130 error.string());
1131 goto bail;
1132 }
1133 if (name == "Donut") targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001134 printf("sdkVersion:'%s'\n",
1135 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001136 } else if (code != -1) {
1137 targetSdk = code;
1138 printf("sdkVersion:'%d'\n", code);
1139 }
1140 code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
1141 if (code != -1) {
1142 printf("maxSdkVersion:'%d'\n", code);
1143 }
1144 code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
1145 if (error != "") {
1146 error = "";
1147 String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
1148 if (error != "") {
1149 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
1150 error.string());
1151 goto bail;
1152 }
1153 if (name == "Donut" && targetSdk < 4) targetSdk = 4;
Maurice Chu2675f762013-10-22 17:33:11 -07001154 printf("targetSdkVersion:'%s'\n",
1155 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001156 } else if (code != -1) {
1157 if (targetSdk < code) {
1158 targetSdk = code;
1159 }
1160 printf("targetSdkVersion:'%d'\n", code);
1161 }
1162 } else if (tag == "uses-configuration") {
1163 int32_t reqTouchScreen = getIntegerAttribute(tree,
1164 REQ_TOUCH_SCREEN_ATTR, NULL, 0);
1165 int32_t reqKeyboardType = getIntegerAttribute(tree,
1166 REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
1167 int32_t reqHardKeyboard = getIntegerAttribute(tree,
1168 REQ_HARD_KEYBOARD_ATTR, NULL, 0);
1169 int32_t reqNavigation = getIntegerAttribute(tree,
1170 REQ_NAVIGATION_ATTR, NULL, 0);
1171 int32_t reqFiveWayNav = getIntegerAttribute(tree,
1172 REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
1173 printf("uses-configuration:");
1174 if (reqTouchScreen != 0) {
1175 printf(" reqTouchScreen='%d'", reqTouchScreen);
1176 }
1177 if (reqKeyboardType != 0) {
1178 printf(" reqKeyboardType='%d'", reqKeyboardType);
1179 }
1180 if (reqHardKeyboard != 0) {
1181 printf(" reqHardKeyboard='%d'", reqHardKeyboard);
1182 }
1183 if (reqNavigation != 0) {
1184 printf(" reqNavigation='%d'", reqNavigation);
1185 }
1186 if (reqFiveWayNav != 0) {
1187 printf(" reqFiveWayNav='%d'", reqFiveWayNav);
1188 }
1189 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001190 } else if (tag == "supports-input") {
1191 withinSupportsInput = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001192 } else if (tag == "supports-screens") {
1193 smallScreen = getIntegerAttribute(tree,
1194 SMALL_SCREEN_ATTR, NULL, 1);
1195 normalScreen = getIntegerAttribute(tree,
1196 NORMAL_SCREEN_ATTR, NULL, 1);
1197 largeScreen = getIntegerAttribute(tree,
1198 LARGE_SCREEN_ATTR, NULL, 1);
1199 xlargeScreen = getIntegerAttribute(tree,
1200 XLARGE_SCREEN_ATTR, NULL, 1);
1201 anyDensity = getIntegerAttribute(tree,
1202 ANY_DENSITY_ATTR, NULL, 1);
1203 requiresSmallestWidthDp = getIntegerAttribute(tree,
1204 REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
1205 compatibleWidthLimitDp = getIntegerAttribute(tree,
1206 COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1207 largestWidthLimitDp = getIntegerAttribute(tree,
1208 LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
1209 } else if (tag == "uses-feature") {
1210 String8 name = getAttribute(tree, NAME_ATTR, &error);
1211
1212 if (name != "" && error == "") {
1213 int req = getIntegerAttribute(tree,
1214 REQUIRED_ATTR, NULL, 1);
1215
1216 if (name == "android.hardware.camera") {
1217 specCameraFeature = true;
1218 } else if (name == "android.hardware.camera.autofocus") {
1219 // these have no corresponding permission to check for,
1220 // but should imply the foundational camera permission
1221 reqCameraAutofocusFeature = reqCameraAutofocusFeature || req;
1222 specCameraAutofocusFeature = true;
1223 } else if (req && (name == "android.hardware.camera.flash")) {
1224 // these have no corresponding permission to check for,
1225 // but should imply the foundational camera permission
1226 reqCameraFlashFeature = true;
1227 } else if (name == "android.hardware.location") {
1228 specLocationFeature = true;
1229 } else if (name == "android.hardware.location.network") {
1230 specNetworkLocFeature = true;
1231 reqNetworkLocFeature = reqNetworkLocFeature || req;
1232 } else if (name == "android.hardware.location.gps") {
1233 specGpsFeature = true;
1234 reqGpsFeature = reqGpsFeature || req;
1235 } else if (name == "android.hardware.bluetooth") {
1236 specBluetoothFeature = true;
1237 } else if (name == "android.hardware.touchscreen") {
1238 specTouchscreenFeature = true;
1239 } else if (name == "android.hardware.touchscreen.multitouch") {
1240 specMultitouchFeature = true;
1241 } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
1242 reqDistinctMultitouchFeature = reqDistinctMultitouchFeature || req;
1243 } else if (name == "android.hardware.microphone") {
1244 specMicrophoneFeature = true;
1245 } else if (name == "android.hardware.wifi") {
1246 specWiFiFeature = true;
1247 } else if (name == "android.hardware.telephony") {
1248 specTelephonyFeature = true;
1249 } else if (req && (name == "android.hardware.telephony.gsm" ||
1250 name == "android.hardware.telephony.cdma")) {
1251 // these have no corresponding permission to check for,
1252 // but should imply the foundational telephony permission
1253 reqTelephonySubFeature = true;
1254 } else if (name == "android.hardware.screen.portrait") {
1255 specScreenPortraitFeature = true;
1256 } else if (name == "android.hardware.screen.landscape") {
1257 specScreenLandscapeFeature = true;
1258 }
1259 printf("uses-feature%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001260 req ? "" : "-not-required",
1261 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001262 } else {
1263 int vers = getIntegerAttribute(tree,
1264 GL_ES_VERSION_ATTR, &error);
1265 if (error == "") {
1266 printf("uses-gl-es:'0x%x'\n", vers);
1267 }
1268 }
1269 } else if (tag == "uses-permission") {
1270 String8 name = getAttribute(tree, NAME_ATTR, &error);
1271 if (name != "" && error == "") {
1272 if (name == "android.permission.CAMERA") {
1273 hasCameraPermission = true;
1274 } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
1275 hasGpsPermission = true;
1276 } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
1277 hasMockLocPermission = true;
1278 } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
1279 hasCoarseLocPermission = true;
1280 } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
1281 name == "android.permission.INSTALL_LOCATION_PROVIDER") {
1282 hasGeneralLocPermission = true;
1283 } else if (name == "android.permission.BLUETOOTH" ||
1284 name == "android.permission.BLUETOOTH_ADMIN") {
1285 hasBluetoothPermission = true;
1286 } else if (name == "android.permission.RECORD_AUDIO") {
1287 hasRecordAudioPermission = true;
1288 } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
1289 name == "android.permission.CHANGE_WIFI_STATE" ||
1290 name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
1291 hasWiFiPermission = true;
1292 } else if (name == "android.permission.CALL_PHONE" ||
1293 name == "android.permission.CALL_PRIVILEGED" ||
1294 name == "android.permission.MODIFY_PHONE_STATE" ||
1295 name == "android.permission.PROCESS_OUTGOING_CALLS" ||
1296 name == "android.permission.READ_SMS" ||
1297 name == "android.permission.RECEIVE_SMS" ||
1298 name == "android.permission.RECEIVE_MMS" ||
1299 name == "android.permission.RECEIVE_WAP_PUSH" ||
1300 name == "android.permission.SEND_SMS" ||
1301 name == "android.permission.WRITE_APN_SETTINGS" ||
1302 name == "android.permission.WRITE_SMS") {
1303 hasTelephonyPermission = true;
1304 } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
1305 hasWriteExternalStoragePermission = true;
1306 } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
1307 hasReadExternalStoragePermission = true;
1308 } else if (name == "android.permission.READ_PHONE_STATE") {
1309 hasReadPhoneStatePermission = true;
1310 } else if (name == "android.permission.READ_CONTACTS") {
1311 hasReadContactsPermission = true;
1312 } else if (name == "android.permission.WRITE_CONTACTS") {
1313 hasWriteContactsPermission = true;
1314 } else if (name == "android.permission.READ_CALL_LOG") {
1315 hasReadCallLogPermission = true;
1316 } else if (name == "android.permission.WRITE_CALL_LOG") {
1317 hasWriteCallLogPermission = true;
1318 }
Adam Lesinski58f1f362013-11-12 12:59:08 -08001319
1320 printUsesPermission(name,
1321 getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
1322 getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
1323 } else {
Adam Lesinski282e1812014-01-23 18:17:42 -08001324 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1325 error.string());
1326 goto bail;
1327 }
1328 } else if (tag == "uses-package") {
1329 String8 name = getAttribute(tree, NAME_ATTR, &error);
1330 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001331 printf("uses-package:'%s'\n",
1332 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001333 } else {
1334 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1335 error.string());
1336 goto bail;
1337 }
1338 } else if (tag == "original-package") {
1339 String8 name = getAttribute(tree, NAME_ATTR, &error);
1340 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001341 printf("original-package:'%s'\n",
1342 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001343 } else {
1344 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1345 error.string());
1346 goto bail;
1347 }
1348 } else if (tag == "supports-gl-texture") {
1349 String8 name = getAttribute(tree, NAME_ATTR, &error);
1350 if (name != "" && error == "") {
Maurice Chu2675f762013-10-22 17:33:11 -07001351 printf("supports-gl-texture:'%s'\n",
1352 ResTable::normalizeForOutput(name.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001353 } else {
1354 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1355 error.string());
1356 goto bail;
1357 }
1358 } else if (tag == "compatible-screens") {
Adam Lesinski9cb2c682014-05-15 12:37:54 -07001359 printCompatibleScreens(tree, &error);
1360 if (error != "") {
1361 fprintf(stderr, "ERROR getting compatible screens: %s\n",
1362 error.string());
1363 goto bail;
1364 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001365 depth--;
1366 } else if (tag == "package-verifier") {
1367 String8 name = getAttribute(tree, NAME_ATTR, &error);
1368 if (name != "" && error == "") {
1369 String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
1370 if (publicKey != "" && error == "") {
1371 printf("package-verifier: name='%s' publicKey='%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001372 ResTable::normalizeForOutput(name.string()).string(),
1373 ResTable::normalizeForOutput(publicKey.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001374 }
1375 }
1376 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001377 } else if (depth == 3) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001378 withinActivity = false;
1379 withinReceiver = false;
1380 withinService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001381 withinProvider = false;
Adam Lesinski282e1812014-01-23 18:17:42 -08001382 hasIntentFilter = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001383 hasMetaHostPaymentCategory = false;
1384 hasMetaOffHostPaymentCategory = false;
1385 hasBindDeviceAdminPermission = false;
1386 hasBindInputMethodPermission = false;
1387 hasBindAccessibilityServicePermission = false;
1388 hasBindPrintServicePermission = false;
1389 hasBindNfcServicePermission = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001390 hasRequiredSafAttributes = false;
1391 hasBindNotificationListenerServicePermission = false;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001392 if (withinApplication) {
1393 if(tag == "activity") {
1394 withinActivity = true;
1395 activityName = getAttribute(tree, NAME_ATTR, &error);
Adam Lesinski282e1812014-01-23 18:17:42 -08001396 if (error != "") {
Michael Wrightec4fdec2013-09-06 16:50:52 -07001397 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1398 error.string());
Adam Lesinski282e1812014-01-23 18:17:42 -08001399 goto bail;
1400 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001401
Michael Wrightec4fdec2013-09-06 16:50:52 -07001402 activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
1403 if (error != "") {
1404 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
1405 error.string());
1406 goto bail;
1407 }
1408
1409 activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
1410 if (error != "") {
1411 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
1412 error.string());
1413 goto bail;
1414 }
1415
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001416 activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error);
1417 if (error != "") {
1418 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
1419 error.string());
1420 goto bail;
1421 }
1422
Michael Wrightec4fdec2013-09-06 16:50:52 -07001423 int32_t orien = getResolvedIntegerAttribute(&res, tree,
1424 SCREEN_ORIENTATION_ATTR, &error);
1425 if (error == "") {
1426 if (orien == 0 || orien == 6 || orien == 8) {
1427 // Requests landscape, sensorLandscape, or reverseLandscape.
1428 reqScreenLandscapeFeature = true;
1429 } else if (orien == 1 || orien == 7 || orien == 9) {
1430 // Requests portrait, sensorPortrait, or reversePortrait.
1431 reqScreenPortraitFeature = true;
1432 }
1433 }
1434 } else if (tag == "uses-library") {
1435 String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
1436 if (error != "") {
1437 fprintf(stderr,
1438 "ERROR getting 'android:name' attribute for uses-library"
1439 " %s\n", error.string());
1440 goto bail;
1441 }
1442 int req = getIntegerAttribute(tree,
1443 REQUIRED_ATTR, NULL, 1);
1444 printf("uses-library%s:'%s'\n",
Maurice Chu2675f762013-10-22 17:33:11 -07001445 req ? "" : "-not-required", ResTable::normalizeForOutput(
1446 libraryName.string()).string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001447 } else if (tag == "receiver") {
1448 withinReceiver = true;
1449 receiverName = getAttribute(tree, NAME_ATTR, &error);
1450
1451 if (error != "") {
1452 fprintf(stderr,
1453 "ERROR getting 'android:name' attribute for receiver:"
1454 " %s\n", error.string());
1455 goto bail;
1456 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001457
1458 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1459 if (error == "") {
1460 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
1461 hasBindDeviceAdminPermission = true;
1462 }
1463 } else {
1464 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1465 " receiver '%s': %s\n", receiverName.string(), error.string());
1466 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001467 } else if (tag == "service") {
1468 withinService = true;
1469 serviceName = getAttribute(tree, NAME_ATTR, &error);
1470
1471 if (error != "") {
1472 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1473 "service:%s\n", error.string());
1474 goto bail;
1475 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001476
1477 String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
1478 if (error == "") {
1479 if (permission == "android.permission.BIND_INPUT_METHOD") {
1480 hasBindInputMethodPermission = true;
1481 } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
1482 hasBindAccessibilityServicePermission = true;
1483 } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
1484 hasBindPrintServicePermission = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001485 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
1486 hasBindNfcServicePermission = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001487 } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
1488 hasBindNotificationListenerServicePermission = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001489 }
1490 } else {
1491 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
1492 " service '%s': %s\n", serviceName.string(), error.string());
1493 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001494 } else if (tag == "provider") {
1495 withinProvider = true;
1496
1497 bool exported = getResolvedIntegerAttribute(&res, tree, EXPORTED_ATTR, &error);
1498 if (error != "") {
1499 fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
1500 " %s\n", error.string());
1501 goto bail;
1502 }
1503
1504 bool grantUriPermissions = getResolvedIntegerAttribute(&res, tree,
1505 GRANT_URI_PERMISSIONS_ATTR, &error);
1506 if (error != "") {
1507 fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
1508 " %s\n", error.string());
1509 goto bail;
1510 }
1511
1512 String8 permission = getResolvedAttribute(&res, tree, PERMISSION_ATTR, &error);
1513 if (error != "") {
1514 fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
1515 " %s\n", error.string());
1516 goto bail;
1517 }
1518
1519 hasRequiredSafAttributes |= exported && grantUriPermissions &&
1520 permission == "android.permission.MANAGE_DOCUMENTS";
1521
Michael Wrightec4fdec2013-09-06 16:50:52 -07001522 } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
Adam Lesinskib71adb62014-05-15 14:14:41 -07001523 String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001524 if (error != "") {
1525 fprintf(stderr, "ERROR getting 'android:name' attribute for "
1526 "meta-data:%s\n", error.string());
1527 goto bail;
1528 }
Maurice Chu2675f762013-10-22 17:33:11 -07001529 printf("meta-data: name='%s' ",
1530 ResTable::normalizeForOutput(metaDataName.string()).string());
Maurice Chu76327312013-10-16 18:28:46 -07001531 printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"),
1532 &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001533 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001534 // Try looking for a RESOURCE_ATTR
1535 error = "";
1536 printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR,
1537 String8("resource"), &error);
Michael Wrightec4fdec2013-09-06 16:50:52 -07001538 if (error != "") {
Maurice Chu76327312013-10-16 18:28:46 -07001539 fprintf(stderr, "ERROR getting 'android:value' or "
1540 "'android:resource' attribute for "
1541 "meta-data:%s\n", error.string());
Michael Wrightec4fdec2013-09-06 16:50:52 -07001542 goto bail;
1543 }
Michael Wrightec4fdec2013-09-06 16:50:52 -07001544 }
Maurice Chu76327312013-10-16 18:28:46 -07001545 printf("\n");
Michael Wrightec4fdec2013-09-06 16:50:52 -07001546 } else if (withinSupportsInput && tag == "input-type") {
1547 String8 name = getAttribute(tree, NAME_ATTR, &error);
1548 if (name != "" && error == "") {
1549 supportedInput.add(name);
1550 } else {
1551 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1552 error.string());
1553 goto bail;
1554 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001555 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001556 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001557 } else if (depth == 4) {
1558 if (tag == "intent-filter") {
1559 hasIntentFilter = true;
1560 withinIntentFilter = true;
1561 actMainActivity = false;
1562 actWidgetReceivers = false;
1563 actImeService = false;
1564 actWallpaperService = false;
1565 actAccessibilityService = false;
1566 actPrintService = false;
1567 actDeviceAdminEnabled = false;
1568 actHostApduService = false;
1569 actOffHostApduService = false;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001570 actDocumentsProvider = false;
1571 actNotificationListenerService = false;
1572 actCamera = false;
1573 actCameraSecure = false;
1574 catLauncher = false;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001575 } else if (withinService && tag == "meta-data") {
1576 String8 name = getAttribute(tree, NAME_ATTR, &error);
1577 if (error != "") {
1578 fprintf(stderr, "ERROR getting 'android:name' attribute for"
1579 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1580 goto bail;
1581 }
1582
1583 if (name == "android.nfc.cardemulation.host_apdu_service" ||
1584 name == "android.nfc.cardemulation.off_host_apdu_service") {
1585 bool offHost = true;
1586 if (name == "android.nfc.cardemulation.host_apdu_service") {
1587 offHost = false;
1588 }
1589
1590 String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
1591 if (error != "") {
1592 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
1593 " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
1594 goto bail;
1595 }
1596
1597 Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
1598 offHost, &error);
1599 if (error != "") {
1600 fprintf(stderr, "ERROR getting AID category for service '%s'\n",
1601 serviceName.string());
1602 goto bail;
1603 }
1604
1605 const size_t catLen = categories.size();
1606 for (size_t i = 0; i < catLen; i++) {
1607 bool paymentCategory = (categories[i] == "payment");
1608 if (offHost) {
1609 hasMetaOffHostPaymentCategory |= paymentCategory;
1610 } else {
1611 hasMetaHostPaymentCategory |= paymentCategory;
1612 }
1613 }
1614 }
1615 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001616 } else if ((depth == 5) && withinIntentFilter) {
1617 String8 action;
1618 if (tag == "action") {
1619 action = getAttribute(tree, NAME_ATTR, &error);
1620 if (error != "") {
1621 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
1622 error.string());
1623 goto bail;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001624 }
1625
Adam Lesinskia5018c92013-09-30 16:23:15 -07001626 if (withinActivity) {
1627 if (action == "android.intent.action.MAIN") {
1628 isMainActivity = true;
1629 actMainActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001630 } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
1631 action == "android.media.action.VIDEO_CAMERA") {
1632 actCamera = true;
1633 } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
1634 actCameraSecure = true;
Michael Wrightec4fdec2013-09-06 16:50:52 -07001635 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001636 } else if (withinReceiver) {
1637 if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
1638 actWidgetReceivers = true;
1639 } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
1640 actDeviceAdminEnabled = true;
1641 }
1642 } else if (withinService) {
1643 if (action == "android.view.InputMethod") {
1644 actImeService = true;
1645 } else if (action == "android.service.wallpaper.WallpaperService") {
1646 actWallpaperService = true;
1647 } else if (action == "android.accessibilityservice.AccessibilityService") {
1648 actAccessibilityService = true;
1649 } else if (action == "android.printservice.PrintService") {
1650 actPrintService = true;
Adam Lesinski94fc9122013-09-30 17:16:09 -07001651 } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
1652 actHostApduService = true;
1653 } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
1654 actOffHostApduService = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001655 } else if (action == "android.service.notification.NotificationListenerService") {
1656 actNotificationListenerService = true;
1657 }
1658 } else if (withinProvider) {
1659 if (action == "android.content.action.DOCUMENTS_PROVIDER") {
1660 actDocumentsProvider = true;
Adam Lesinskia5018c92013-09-30 16:23:15 -07001661 }
1662 }
1663 if (action == "android.intent.action.SEARCH") {
1664 isSearchable = true;
1665 }
1666 }
1667
1668 if (tag == "category") {
1669 String8 category = getAttribute(tree, NAME_ATTR, &error);
1670 if (error != "") {
1671 fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
1672 error.string());
1673 goto bail;
1674 }
1675 if (withinActivity) {
1676 if (category == "android.intent.category.LAUNCHER") {
1677 isLauncherActivity = true;
Tim Kilbourn0a5a5d62014-03-07 15:12:50 -08001678 } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
1679 isLeanbackLauncherActivity = true;
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001680 } else if (category == "android.intent.category.HOME") {
1681 catLauncher = true;
Adam Lesinski282e1812014-01-23 18:17:42 -08001682 }
1683 }
1684 }
1685 }
1686 }
1687
1688 // Pre-1.6 implicitly granted permission compatibility logic
1689 if (targetSdk < 4) {
1690 if (!hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001691 printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"));
1692 printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"),
1693 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001694 hasWriteExternalStoragePermission = true;
1695 }
1696 if (!hasReadPhoneStatePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001697 printUsesPermission(String8("android.permission.READ_PHONE_STATE"));
1698 printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"),
1699 String8("targetSdkVersion < 4"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001700 }
1701 }
1702
1703 // If the application has requested WRITE_EXTERNAL_STORAGE, we will
1704 // force them to always take READ_EXTERNAL_STORAGE as well. We always
1705 // do this (regardless of target API version) because we can't have
1706 // an app with write permission but not read permission.
1707 if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001708 printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE"));
1709 printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"),
1710 String8("requested WRITE_EXTERNAL_STORAGE"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001711 }
1712
1713 // Pre-JellyBean call log permission compatibility.
1714 if (targetSdk < 16) {
1715 if (!hasReadCallLogPermission && hasReadContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001716 printUsesPermission(String8("android.permission.READ_CALL_LOG"));
1717 printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"),
1718 String8("targetSdkVersion < 16 and requested READ_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001719 }
1720 if (!hasWriteCallLogPermission && hasWriteContactsPermission) {
Adam Lesinski58f1f362013-11-12 12:59:08 -08001721 printUsesPermission(String8("android.permission.WRITE_CALL_LOG"));
1722 printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"),
1723 String8("targetSdkVersion < 16 and requested WRITE_CONTACTS"));
Adam Lesinski282e1812014-01-23 18:17:42 -08001724 }
1725 }
1726
1727 /* The following blocks handle printing "inferred" uses-features, based
1728 * on whether related features or permissions are used by the app.
1729 * Note that the various spec*Feature variables denote whether the
1730 * relevant tag was *present* in the AndroidManfest, not that it was
1731 * present and set to true.
1732 */
1733 // Camera-related back-compatibility logic
1734 if (!specCameraFeature) {
1735 if (reqCameraFlashFeature) {
1736 // if app requested a sub-feature (autofocus or flash) and didn't
1737 // request the base camera feature, we infer that it meant to
1738 printf("uses-feature:'android.hardware.camera'\n");
1739 printf("uses-implied-feature:'android.hardware.camera'," \
1740 "'requested android.hardware.camera.flash feature'\n");
1741 } else if (reqCameraAutofocusFeature) {
1742 // if app requested a sub-feature (autofocus or flash) and didn't
1743 // request the base camera feature, we infer that it meant to
1744 printf("uses-feature:'android.hardware.camera'\n");
1745 printf("uses-implied-feature:'android.hardware.camera'," \
1746 "'requested android.hardware.camera.autofocus feature'\n");
1747 } else if (hasCameraPermission) {
Maurice Chu2675f762013-10-22 17:33:11 -07001748 // if app wants to use camera but didn't request the feature, we infer
Adam Lesinski282e1812014-01-23 18:17:42 -08001749 // that it meant to, and further that it wants autofocus
1750 // (which was the 1.0 - 1.5 behavior)
1751 printf("uses-feature:'android.hardware.camera'\n");
1752 if (!specCameraAutofocusFeature) {
1753 printf("uses-feature:'android.hardware.camera.autofocus'\n");
1754 printf("uses-implied-feature:'android.hardware.camera.autofocus'," \
1755 "'requested android.permission.CAMERA permission'\n");
1756 }
1757 }
1758 }
1759
1760 // Location-related back-compatibility logic
1761 if (!specLocationFeature &&
1762 (hasMockLocPermission || hasCoarseLocPermission || hasGpsPermission ||
1763 hasGeneralLocPermission || reqNetworkLocFeature || reqGpsFeature)) {
1764 // if app either takes a location-related permission or requests one of the
1765 // sub-features, we infer that it also meant to request the base location feature
1766 printf("uses-feature:'android.hardware.location'\n");
1767 printf("uses-implied-feature:'android.hardware.location'," \
1768 "'requested a location access permission'\n");
1769 }
1770 if (!specGpsFeature && hasGpsPermission) {
1771 // if app takes GPS (FINE location) perm but does not request the GPS
1772 // feature, we infer that it meant to
1773 printf("uses-feature:'android.hardware.location.gps'\n");
1774 printf("uses-implied-feature:'android.hardware.location.gps'," \
1775 "'requested android.permission.ACCESS_FINE_LOCATION permission'\n");
1776 }
1777 if (!specNetworkLocFeature && hasCoarseLocPermission) {
1778 // if app takes Network location (COARSE location) perm but does not request the
1779 // network location feature, we infer that it meant to
1780 printf("uses-feature:'android.hardware.location.network'\n");
1781 printf("uses-implied-feature:'android.hardware.location.network'," \
1782 "'requested android.permission.ACCESS_COARSE_LOCATION permission'\n");
1783 }
1784
1785 // Bluetooth-related compatibility logic
1786 if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
1787 // if app takes a Bluetooth permission but does not request the Bluetooth
1788 // feature, we infer that it meant to
1789 printf("uses-feature:'android.hardware.bluetooth'\n");
1790 printf("uses-implied-feature:'android.hardware.bluetooth'," \
1791 "'requested android.permission.BLUETOOTH or android.permission.BLUETOOTH_ADMIN " \
1792 "permission and targetSdkVersion > 4'\n");
1793 }
1794
1795 // Microphone-related compatibility logic
1796 if (!specMicrophoneFeature && hasRecordAudioPermission) {
1797 // if app takes the record-audio permission but does not request the microphone
1798 // feature, we infer that it meant to
1799 printf("uses-feature:'android.hardware.microphone'\n");
1800 printf("uses-implied-feature:'android.hardware.microphone'," \
1801 "'requested android.permission.RECORD_AUDIO permission'\n");
1802 }
1803
1804 // WiFi-related compatibility logic
1805 if (!specWiFiFeature && hasWiFiPermission) {
1806 // if app takes one of the WiFi permissions but does not request the WiFi
1807 // feature, we infer that it meant to
1808 printf("uses-feature:'android.hardware.wifi'\n");
1809 printf("uses-implied-feature:'android.hardware.wifi'," \
1810 "'requested android.permission.ACCESS_WIFI_STATE, " \
1811 "android.permission.CHANGE_WIFI_STATE, or " \
1812 "android.permission.CHANGE_WIFI_MULTICAST_STATE permission'\n");
1813 }
1814
1815 // Telephony-related compatibility logic
1816 if (!specTelephonyFeature && (hasTelephonyPermission || reqTelephonySubFeature)) {
1817 // if app takes one of the telephony permissions or requests a sub-feature but
1818 // does not request the base telephony feature, we infer that it meant to
1819 printf("uses-feature:'android.hardware.telephony'\n");
1820 printf("uses-implied-feature:'android.hardware.telephony'," \
1821 "'requested a telephony-related permission or feature'\n");
1822 }
1823
1824 // Touchscreen-related back-compatibility logic
1825 if (!specTouchscreenFeature) { // not a typo!
1826 // all apps are presumed to require a touchscreen, unless they explicitly say
1827 // <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
1828 // Note that specTouchscreenFeature is true if the tag is present, regardless
1829 // of whether its value is true or false, so this is safe
1830 printf("uses-feature:'android.hardware.touchscreen'\n");
1831 printf("uses-implied-feature:'android.hardware.touchscreen'," \
1832 "'assumed you require a touch screen unless explicitly made optional'\n");
1833 }
1834 if (!specMultitouchFeature && reqDistinctMultitouchFeature) {
1835 // if app takes one of the telephony permissions or requests a sub-feature but
1836 // does not request the base telephony feature, we infer that it meant to
1837 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
1838 printf("uses-implied-feature:'android.hardware.touchscreen.multitouch'," \
1839 "'requested android.hardware.touchscreen.multitouch.distinct feature'\n");
1840 }
1841
1842 // Landscape/portrait-related compatibility logic
1843 if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
1844 // If the app has specified any activities in its manifest
1845 // that request a specific orientation, then assume that
1846 // orientation is required.
1847 if (reqScreenLandscapeFeature) {
1848 printf("uses-feature:'android.hardware.screen.landscape'\n");
1849 printf("uses-implied-feature:'android.hardware.screen.landscape'," \
1850 "'one or more activities have specified a landscape orientation'\n");
1851 }
1852 if (reqScreenPortraitFeature) {
1853 printf("uses-feature:'android.hardware.screen.portrait'\n");
1854 printf("uses-implied-feature:'android.hardware.screen.portrait'," \
1855 "'one or more activities have specified a portrait orientation'\n");
1856 }
1857 }
1858
Adam Lesinski282e1812014-01-23 18:17:42 -08001859 if (hasWidgetReceivers) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001860 printComponentPresence("app-widget");
Adam Lesinski282e1812014-01-23 18:17:42 -08001861 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001862 if (hasDeviceAdminReceiver) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001863 printComponentPresence("device-admin");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001864 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001865 if (hasImeService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001866 printComponentPresence("ime");
Adam Lesinski282e1812014-01-23 18:17:42 -08001867 }
1868 if (hasWallpaperService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001869 printComponentPresence("wallpaper");
Adam Lesinski282e1812014-01-23 18:17:42 -08001870 }
Adam Lesinskia5018c92013-09-30 16:23:15 -07001871 if (hasAccessibilityService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001872 printComponentPresence("accessibility");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001873 }
1874 if (hasPrintService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001875 printComponentPresence("print-service");
Adam Lesinskia5018c92013-09-30 16:23:15 -07001876 }
Adam Lesinski94fc9122013-09-30 17:16:09 -07001877 if (hasPaymentService) {
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001878 printComponentPresence("payment");
1879 }
1880 if (isSearchable) {
1881 printComponentPresence("search");
1882 }
1883 if (hasDocumentsProvider) {
1884 printComponentPresence("document-provider");
1885 }
1886 if (hasLauncher) {
1887 printComponentPresence("launcher");
1888 }
1889 if (hasNotificationListenerService) {
1890 printComponentPresence("notification-listener");
1891 }
1892 if (hasCameraActivity) {
1893 printComponentPresence("camera");
1894 }
1895 if (hasCameraSecureActivity) {
1896 printComponentPresence("camera-secure");
1897 }
1898
1899 if (hasMainActivity) {
1900 printf("main\n");
Adam Lesinski94fc9122013-09-30 17:16:09 -07001901 }
Adam Lesinski282e1812014-01-23 18:17:42 -08001902 if (hasOtherActivities) {
1903 printf("other-activities\n");
1904 }
Adam Lesinski9d5b08e2014-04-25 11:01:43 -07001905 if (hasOtherReceivers) {
Adam Lesinski282e1812014-01-23 18:17:42 -08001906 printf("other-receivers\n");
1907 }
1908 if (hasOtherServices) {
1909 printf("other-services\n");
1910 }
1911
1912 // For modern apps, if screen size buckets haven't been specified
1913 // but the new width ranges have, then infer the buckets from them.
1914 if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
1915 && requiresSmallestWidthDp > 0) {
1916 int compatWidth = compatibleWidthLimitDp;
1917 if (compatWidth <= 0) {
1918 compatWidth = requiresSmallestWidthDp;
1919 }
1920 if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
1921 smallScreen = -1;
1922 } else {
1923 smallScreen = 0;
1924 }
1925 if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
1926 normalScreen = -1;
1927 } else {
1928 normalScreen = 0;
1929 }
1930 if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
1931 largeScreen = -1;
1932 } else {
1933 largeScreen = 0;
1934 }
1935 if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
1936 xlargeScreen = -1;
1937 } else {
1938 xlargeScreen = 0;
1939 }
1940 }
1941
1942 // Determine default values for any unspecified screen sizes,
1943 // based on the target SDK of the package. As of 4 (donut)
1944 // the screen size support was introduced, so all default to
1945 // enabled.
1946 if (smallScreen > 0) {
1947 smallScreen = targetSdk >= 4 ? -1 : 0;
1948 }
1949 if (normalScreen > 0) {
1950 normalScreen = -1;
1951 }
1952 if (largeScreen > 0) {
1953 largeScreen = targetSdk >= 4 ? -1 : 0;
1954 }
1955 if (xlargeScreen > 0) {
1956 // Introduced in Gingerbread.
1957 xlargeScreen = targetSdk >= 9 ? -1 : 0;
1958 }
1959 if (anyDensity > 0) {
1960 anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
1961 || compatibleWidthLimitDp > 0) ? -1 : 0;
1962 }
1963 printf("supports-screens:");
1964 if (smallScreen != 0) {
1965 printf(" 'small'");
1966 }
1967 if (normalScreen != 0) {
1968 printf(" 'normal'");
1969 }
1970 if (largeScreen != 0) {
1971 printf(" 'large'");
1972 }
1973 if (xlargeScreen != 0) {
1974 printf(" 'xlarge'");
1975 }
1976 printf("\n");
1977 printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
1978 if (requiresSmallestWidthDp > 0) {
1979 printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
1980 }
1981 if (compatibleWidthLimitDp > 0) {
1982 printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
1983 }
1984 if (largestWidthLimitDp > 0) {
1985 printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
1986 }
1987
1988 printf("locales:");
1989 const size_t NL = locales.size();
1990 for (size_t i=0; i<NL; i++) {
1991 const char* localeStr = locales[i].string();
1992 if (localeStr == NULL || strlen(localeStr) == 0) {
1993 localeStr = "--_--";
1994 }
1995 printf(" '%s'", localeStr);
1996 }
1997 printf("\n");
1998
1999 printf("densities:");
2000 const size_t ND = densities.size();
2001 for (size_t i=0; i<ND; i++) {
2002 printf(" '%d'", densities[i]);
2003 }
2004 printf("\n");
2005
2006 AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
2007 if (dir != NULL) {
2008 if (dir->getFileCount() > 0) {
2009 printf("native-code:");
2010 for (size_t i=0; i<dir->getFileCount(); i++) {
Maurice Chu2675f762013-10-22 17:33:11 -07002011 printf(" '%s'", ResTable::normalizeForOutput(
2012 dir->getFileName(i).string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002013 }
2014 printf("\n");
2015 }
2016 delete dir;
2017 }
2018 } else if (strcmp("badger", option) == 0) {
2019 printf("%s", CONSOLE_DATA);
2020 } else if (strcmp("configurations", option) == 0) {
2021 Vector<ResTable_config> configs;
2022 res.getConfigurations(&configs);
2023 const size_t N = configs.size();
2024 for (size_t i=0; i<N; i++) {
2025 printf("%s\n", configs[i].toString().string());
2026 }
2027 } else {
2028 fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
2029 goto bail;
2030 }
2031 }
2032
2033 result = NO_ERROR;
2034
2035bail:
2036 if (asset) {
2037 delete asset;
2038 }
2039 return (result != NO_ERROR);
2040}
2041
2042
2043/*
2044 * Handle the "add" command, which wants to add files to a new or
2045 * pre-existing archive.
2046 */
2047int doAdd(Bundle* bundle)
2048{
2049 ZipFile* zip = NULL;
2050 status_t result = UNKNOWN_ERROR;
2051 const char* zipFileName;
2052
2053 if (bundle->getUpdate()) {
2054 /* avoid confusion */
2055 fprintf(stderr, "ERROR: can't use '-u' with add\n");
2056 goto bail;
2057 }
2058
2059 if (bundle->getFileSpecCount() < 1) {
2060 fprintf(stderr, "ERROR: must specify zip file name\n");
2061 goto bail;
2062 }
2063 zipFileName = bundle->getFileSpecEntry(0);
2064
2065 if (bundle->getFileSpecCount() < 2) {
2066 fprintf(stderr, "NOTE: nothing to do\n");
2067 goto bail;
2068 }
2069
2070 zip = openReadWrite(zipFileName, true);
2071 if (zip == NULL) {
2072 fprintf(stderr, "ERROR: failed opening/creating '%s' as Zip file\n", zipFileName);
2073 goto bail;
2074 }
2075
2076 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2077 const char* fileName = bundle->getFileSpecEntry(i);
2078
2079 if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
2080 printf(" '%s'... (from gzip)\n", fileName);
2081 result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
2082 } else {
2083 if (bundle->getJunkPath()) {
2084 String8 storageName = String8(fileName).getPathLeaf();
Maurice Chu2675f762013-10-22 17:33:11 -07002085 printf(" '%s' as '%s'...\n", fileName,
2086 ResTable::normalizeForOutput(storageName.string()).string());
Adam Lesinski282e1812014-01-23 18:17:42 -08002087 result = zip->add(fileName, storageName.string(),
2088 bundle->getCompressionMethod(), NULL);
2089 } else {
2090 printf(" '%s'...\n", fileName);
2091 result = zip->add(fileName, bundle->getCompressionMethod(), NULL);
2092 }
2093 }
2094 if (result != NO_ERROR) {
2095 fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName);
2096 if (result == NAME_NOT_FOUND) {
2097 fprintf(stderr, ": file not found\n");
2098 } else if (result == ALREADY_EXISTS) {
2099 fprintf(stderr, ": already exists in archive\n");
2100 } else {
2101 fprintf(stderr, "\n");
2102 }
2103 goto bail;
2104 }
2105 }
2106
2107 result = NO_ERROR;
2108
2109bail:
2110 delete zip;
2111 return (result != NO_ERROR);
2112}
2113
2114
2115/*
2116 * Delete files from an existing archive.
2117 */
2118int doRemove(Bundle* bundle)
2119{
2120 ZipFile* zip = NULL;
2121 status_t result = UNKNOWN_ERROR;
2122 const char* zipFileName;
2123
2124 if (bundle->getFileSpecCount() < 1) {
2125 fprintf(stderr, "ERROR: must specify zip file name\n");
2126 goto bail;
2127 }
2128 zipFileName = bundle->getFileSpecEntry(0);
2129
2130 if (bundle->getFileSpecCount() < 2) {
2131 fprintf(stderr, "NOTE: nothing to do\n");
2132 goto bail;
2133 }
2134
2135 zip = openReadWrite(zipFileName, false);
2136 if (zip == NULL) {
2137 fprintf(stderr, "ERROR: failed opening Zip archive '%s'\n",
2138 zipFileName);
2139 goto bail;
2140 }
2141
2142 for (int i = 1; i < bundle->getFileSpecCount(); i++) {
2143 const char* fileName = bundle->getFileSpecEntry(i);
2144 ZipEntry* entry;
2145
2146 entry = zip->getEntryByName(fileName);
2147 if (entry == NULL) {
2148 printf(" '%s' NOT FOUND\n", fileName);
2149 continue;
2150 }
2151
2152 result = zip->remove(entry);
2153
2154 if (result != NO_ERROR) {
2155 fprintf(stderr, "Unable to delete '%s' from '%s'\n",
2156 bundle->getFileSpecEntry(i), zipFileName);
2157 goto bail;
2158 }
2159 }
2160
2161 /* update the archive */
2162 zip->flush();
2163
2164bail:
2165 delete zip;
2166 return (result != NO_ERROR);
2167}
2168
Adam Lesinski3921e872014-05-13 10:56:25 -07002169static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002170 const size_t numDirs = dir->getDirs().size();
2171 for (size_t i = 0; i < numDirs; i++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002172 bool ignore = ignoreConfig;
2173 const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
2174 const char* dirStr = subDir->getLeaf().string();
2175 if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
2176 ignore = true;
2177 }
2178 status_t err = addResourcesToBuilder(subDir, builder, ignore);
Adam Lesinskifab50872014-04-16 14:40:42 -07002179 if (err != NO_ERROR) {
2180 return err;
2181 }
2182 }
2183
2184 const size_t numFiles = dir->getFiles().size();
2185 for (size_t i = 0; i < numFiles; i++) {
2186 sp<AaptGroup> gp = dir->getFiles().valueAt(i);
2187 const size_t numConfigs = gp->getFiles().size();
2188 for (size_t j = 0; j < numConfigs; j++) {
Adam Lesinski3921e872014-05-13 10:56:25 -07002189 status_t err = NO_ERROR;
2190 if (ignoreConfig) {
2191 err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2192 } else {
2193 err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
2194 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002195 if (err != NO_ERROR) {
2196 fprintf(stderr, "Failed to add %s (%s) to builder.\n",
2197 gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
2198 return err;
2199 }
2200 }
2201 }
2202 return NO_ERROR;
2203}
2204
2205static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
2206 if (split->isBase()) {
2207 return original;
2208 }
2209
2210 String8 ext(original.getPathExtension());
2211 if (ext == String8(".apk")) {
2212 return String8::format("%s_%s%s",
2213 original.getBasePath().string(),
2214 split->getDirectorySafeName().string(),
2215 ext.string());
2216 }
2217
2218 return String8::format("%s_%s", original.string(),
2219 split->getDirectorySafeName().string());
2220}
Adam Lesinski282e1812014-01-23 18:17:42 -08002221
2222/*
2223 * Package up an asset directory and associated application files.
2224 */
2225int doPackage(Bundle* bundle)
2226{
2227 const char* outputAPKFile;
2228 int retVal = 1;
2229 status_t err;
2230 sp<AaptAssets> assets;
2231 int N;
2232 FILE* fp;
2233 String8 dependencyFile;
Adam Lesinskifab50872014-04-16 14:40:42 -07002234 sp<ApkBuilder> builder;
Adam Lesinski282e1812014-01-23 18:17:42 -08002235
Anton Krumina2ef5c02014-03-12 14:46:44 -07002236 // -c en_XA or/and ar_XB means do pseudolocalization
Adam Lesinskifab50872014-04-16 14:40:42 -07002237 sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
2238 err = configFilter->parse(bundle->getConfigurations());
Adam Lesinski282e1812014-01-23 18:17:42 -08002239 if (err != NO_ERROR) {
2240 goto bail;
2241 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002242 if (configFilter->containsPseudo()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002243 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
2244 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002245 if (configFilter->containsPseudoBidi()) {
Anton Krumina2ef5c02014-03-12 14:46:44 -07002246 bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
Adam Lesinski282e1812014-01-23 18:17:42 -08002247 }
2248
2249 N = bundle->getFileSpecCount();
2250 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0
Adam Lesinski09384302014-01-22 16:07:42 -08002251 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002252 fprintf(stderr, "ERROR: no input files\n");
2253 goto bail;
2254 }
2255
2256 outputAPKFile = bundle->getOutputAPKFile();
2257
2258 // Make sure the filenames provided exist and are of the appropriate type.
2259 if (outputAPKFile) {
2260 FileType type;
2261 type = getFileType(outputAPKFile);
2262 if (type != kFileTypeNonexistent && type != kFileTypeRegular) {
2263 fprintf(stderr,
2264 "ERROR: output file '%s' exists but is not regular file\n",
2265 outputAPKFile);
2266 goto bail;
2267 }
2268 }
2269
2270 // Load the assets.
2271 assets = new AaptAssets();
2272
2273 // Set up the resource gathering in assets if we're going to generate
2274 // dependency files. Every time we encounter a resource while slurping
2275 // the tree, we'll add it to these stores so we have full resource paths
2276 // to write to a dependency file.
2277 if (bundle->getGenDependencies()) {
2278 sp<FilePathStore> resPathStore = new FilePathStore;
2279 assets->setFullResPaths(resPathStore);
2280 sp<FilePathStore> assetPathStore = new FilePathStore;
2281 assets->setFullAssetPaths(assetPathStore);
2282 }
2283
2284 err = assets->slurpFromArgs(bundle);
2285 if (err < 0) {
2286 goto bail;
2287 }
2288
2289 if (bundle->getVerbose()) {
2290 assets->print(String8());
2291 }
2292
Adam Lesinskifab50872014-04-16 14:40:42 -07002293 // Create the ApkBuilder, which will collect the compiled files
2294 // to write to the final APK (or sets of APKs if we are building
2295 // a Split APK.
2296 builder = new ApkBuilder(configFilter);
2297
2298 // If we are generating a Split APK, find out which configurations to split on.
2299 if (bundle->getSplitConfigurations().size() > 0) {
2300 const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
2301 const size_t numSplits = splitStrs.size();
2302 for (size_t i = 0; i < numSplits; i++) {
2303 std::set<ConfigDescription> configs;
2304 if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
2305 fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
2306 goto bail;
2307 }
2308
2309 err = builder->createSplitForConfigs(configs);
2310 if (err != NO_ERROR) {
2311 goto bail;
2312 }
2313 }
2314 }
2315
Adam Lesinski282e1812014-01-23 18:17:42 -08002316 // If they asked for any fileAs that need to be compiled, do so.
2317 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002318 err = buildResources(bundle, assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002319 if (err != 0) {
2320 goto bail;
2321 }
2322 }
2323
2324 // At this point we've read everything and processed everything. From here
2325 // on out it's just writing output files.
2326 if (SourcePos::hasErrors()) {
2327 goto bail;
2328 }
2329
2330 // Update symbols with information about which ones are needed as Java symbols.
2331 assets->applyJavaSymbols();
2332 if (SourcePos::hasErrors()) {
2333 goto bail;
2334 }
2335
2336 // If we've been asked to generate a dependency file, do that here
2337 if (bundle->getGenDependencies()) {
2338 // If this is the packaging step, generate the dependency file next to
2339 // the output apk (e.g. bin/resources.ap_.d)
2340 if (outputAPKFile) {
2341 dependencyFile = String8(outputAPKFile);
2342 // Add the .d extension to the dependency file.
2343 dependencyFile.append(".d");
2344 } else {
2345 // Else if this is the R.java dependency generation step,
2346 // generate the dependency file in the R.java package subdirectory
2347 // e.g. gen/com/foo/app/R.java.d
2348 dependencyFile = String8(bundle->getRClassDir());
2349 dependencyFile.appendPath("R.java.d");
2350 }
2351 // Make sure we have a clean dependency file to start with
2352 fp = fopen(dependencyFile, "w");
2353 fclose(fp);
2354 }
2355
2356 // Write out R.java constants
2357 if (!assets->havePrivateSymbols()) {
2358 if (bundle->getCustomPackage() == NULL) {
2359 // Write the R.java file into the appropriate class directory
2360 // e.g. gen/com/foo/app/R.java
2361 err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
2362 } else {
2363 const String8 customPkg(bundle->getCustomPackage());
2364 err = writeResourceSymbols(bundle, assets, customPkg, true);
2365 }
2366 if (err < 0) {
2367 goto bail;
2368 }
2369 // If we have library files, we're going to write our R.java file into
2370 // the appropriate class directory for those libraries as well.
2371 // e.g. gen/com/foo/app/lib/R.java
2372 if (bundle->getExtraPackages() != NULL) {
2373 // Split on colon
2374 String8 libs(bundle->getExtraPackages());
2375 char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
2376 while (packageString != NULL) {
2377 // Write the R.java file out with the correct package name
2378 err = writeResourceSymbols(bundle, assets, String8(packageString), true);
2379 if (err < 0) {
2380 goto bail;
2381 }
2382 packageString = strtok(NULL, ":");
2383 }
2384 libs.unlockBuffer();
2385 }
2386 } else {
2387 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false);
2388 if (err < 0) {
2389 goto bail;
2390 }
2391 err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true);
2392 if (err < 0) {
2393 goto bail;
2394 }
2395 }
2396
2397 // Write out the ProGuard file
2398 err = writeProguardFile(bundle, assets);
2399 if (err < 0) {
2400 goto bail;
2401 }
2402
2403 // Write the apk
2404 if (outputAPKFile) {
Adam Lesinskifab50872014-04-16 14:40:42 -07002405 // Gather all resources and add them to the APK Builder. The builder will then
2406 // figure out which Split they belong in.
2407 err = addResourcesToBuilder(assets, builder);
Adam Lesinski282e1812014-01-23 18:17:42 -08002408 if (err != NO_ERROR) {
Adam Lesinski282e1812014-01-23 18:17:42 -08002409 goto bail;
2410 }
Adam Lesinskifab50872014-04-16 14:40:42 -07002411
2412 const Vector<sp<ApkSplit> >& splits = builder->getSplits();
2413 const size_t numSplits = splits.size();
2414 for (size_t i = 0; i < numSplits; i++) {
2415 const sp<ApkSplit>& split = splits[i];
2416 String8 outputPath = buildApkName(String8(outputAPKFile), split);
2417 err = writeAPK(bundle, outputPath, split);
2418 if (err != NO_ERROR) {
2419 fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
2420 goto bail;
2421 }
2422 }
Adam Lesinski282e1812014-01-23 18:17:42 -08002423 }
2424
2425 // If we've been asked to generate a dependency file, we need to finish up here.
2426 // the writeResourceSymbols and writeAPK functions have already written the target
2427 // half of the dependency file, now we need to write the prerequisites. (files that
2428 // the R.java file or .ap_ file depend on)
2429 if (bundle->getGenDependencies()) {
2430 // Now that writeResourceSymbols or writeAPK has taken care of writing
2431 // the targets to our dependency file, we'll write the prereqs
2432 fp = fopen(dependencyFile, "a+");
2433 fprintf(fp, " : ");
2434 bool includeRaw = (outputAPKFile != NULL);
2435 err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
2436 // Also manually add the AndroidManifeset since it's not under res/ or assets/
2437 // and therefore was not added to our pathstores during slurping
2438 fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
2439 fclose(fp);
2440 }
2441
2442 retVal = 0;
2443bail:
2444 if (SourcePos::hasErrors()) {
2445 SourcePos::printErrors(stderr);
2446 }
2447 return retVal;
2448}
2449
2450/*
2451 * Do PNG Crunching
2452 * PRECONDITIONS
2453 * -S flag points to a source directory containing drawable* folders
2454 * -C flag points to destination directory. The folder structure in the
2455 * source directory will be mirrored to the destination (cache) directory
2456 *
2457 * POSTCONDITIONS
2458 * Destination directory will be updated to match the PNG files in
Maurice Chu2675f762013-10-22 17:33:11 -07002459 * the source directory.
Adam Lesinski282e1812014-01-23 18:17:42 -08002460 */
2461int doCrunch(Bundle* bundle)
2462{
2463 fprintf(stdout, "Crunching PNG Files in ");
2464 fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
2465 fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
2466
2467 updatePreProcessedCache(bundle);
2468
2469 return NO_ERROR;
2470}
2471
2472/*
2473 * Do PNG Crunching on a single flag
2474 * -i points to a single png file
2475 * -o points to a single png output file
2476 */
2477int doSingleCrunch(Bundle* bundle)
2478{
2479 fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
2480 fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
2481
2482 String8 input(bundle->getSingleCrunchInputFile());
2483 String8 output(bundle->getSingleCrunchOutputFile());
2484
2485 if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
2486 // we can't return the status_t as it gets truncate to the lower 8 bits.
2487 return 42;
2488 }
2489
2490 return NO_ERROR;
2491}
2492
2493char CONSOLE_DATA[2925] = {
2494 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2495 32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2496 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2497 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2498 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 63,
2499 86, 35, 40, 46, 46, 95, 95, 95, 95, 97, 97, 44, 32, 46, 124, 42, 33, 83,
2500 62, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2501 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2502 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 46, 58, 59, 61, 59, 61, 81,
2503 81, 81, 81, 66, 96, 61, 61, 58, 46, 46, 46, 58, 32, 32, 32, 32, 32, 32,
2504 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2505 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2506 32, 32, 32, 46, 61, 59, 59, 59, 58, 106, 81, 81, 81, 81, 102, 59, 61, 59,
2507 59, 61, 61, 61, 58, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2508 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2509 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59,
2510 59, 58, 109, 81, 81, 81, 81, 61, 59, 59, 59, 59, 59, 58, 59, 59, 46, 32,
2511 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2512 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2513 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 60, 81, 81, 81, 81, 87,
2514 58, 59, 59, 59, 59, 59, 59, 61, 119, 44, 32, 32, 32, 32, 32, 32, 32, 32,
2515 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2516 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2517 47, 61, 59, 59, 58, 100, 81, 81, 81, 81, 35, 58, 59, 59, 59, 59, 59, 58,
2518 121, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2519 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2520 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 109, 58, 59, 59, 61, 81, 81,
2521 81, 81, 81, 109, 58, 59, 59, 59, 59, 61, 109, 81, 81, 76, 46, 32, 32, 32,
2522 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2523 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2524 32, 32, 32, 41, 87, 59, 61, 59, 41, 81, 81, 81, 81, 81, 81, 59, 61, 59,
2525 59, 58, 109, 81, 81, 87, 39, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2526 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2527 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 81, 91, 59,
2528 59, 61, 81, 81, 81, 81, 81, 87, 43, 59, 58, 59, 60, 81, 81, 81, 76, 32,
2529 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2530 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2531 32, 32, 32, 32, 32, 32, 32, 32, 52, 91, 58, 45, 59, 87, 81, 81, 81, 81,
2532 70, 58, 58, 58, 59, 106, 81, 81, 81, 91, 32, 32, 32, 32, 32, 32, 32, 32,
2533 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2534 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2535 32, 93, 40, 32, 46, 59, 100, 81, 81, 81, 81, 40, 58, 46, 46, 58, 100, 81,
2536 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2537 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2538 32, 46, 46, 46, 32, 46, 46, 46, 32, 46, 32, 46, 45, 91, 59, 61, 58, 109,
2539 81, 81, 81, 87, 46, 58, 61, 59, 60, 81, 81, 80, 32, 32, 32, 32, 32, 32,
2540 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2541 32, 32, 32, 32, 32, 32, 32, 46, 46, 61, 59, 61, 61, 61, 59, 61, 61, 59,
2542 59, 59, 58, 58, 46, 46, 41, 58, 59, 58, 81, 81, 81, 81, 69, 58, 59, 59,
2543 60, 81, 81, 68, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2544 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 58, 59,
2545 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 46,
2546 61, 59, 93, 81, 81, 81, 81, 107, 58, 59, 58, 109, 87, 68, 96, 32, 32, 32,
2547 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2548 32, 32, 10, 32, 32, 32, 46, 60, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2549 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 115, 109, 68, 41, 36, 81,
2550 109, 46, 61, 61, 81, 69, 96, 46, 58, 58, 46, 58, 46, 46, 32, 32, 32, 32,
2551 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 46, 32, 95, 81,
2552 67, 61, 61, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2553 59, 59, 59, 59, 58, 68, 39, 61, 105, 61, 63, 81, 119, 58, 106, 80, 32, 58,
2554 61, 59, 59, 61, 59, 61, 59, 61, 46, 95, 32, 32, 32, 32, 32, 32, 32, 32,
2555 32, 32, 32, 32, 32, 32, 10, 32, 32, 36, 81, 109, 105, 59, 61, 59, 59, 59,
2556 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 46, 58, 37,
2557 73, 108, 108, 62, 52, 81, 109, 34, 32, 61, 59, 59, 59, 59, 59, 59, 59, 59,
2558 59, 61, 59, 61, 61, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2559 32, 46, 45, 57, 101, 43, 43, 61, 61, 59, 59, 59, 59, 59, 59, 61, 59, 59,
2560 59, 59, 59, 59, 59, 59, 59, 58, 97, 46, 61, 108, 62, 126, 58, 106, 80, 96,
2561 46, 61, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61,
2562 97, 103, 97, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 45, 46, 32,
2563 46, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 58, 59, 59, 59, 59, 61,
2564 119, 81, 97, 124, 105, 124, 124, 39, 126, 95, 119, 58, 61, 58, 59, 59, 59,
2565 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 119, 81, 81, 99, 32, 32,
2566 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2567 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 58, 106, 81, 81, 81, 109, 119,
2568 119, 119, 109, 109, 81, 81, 122, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59,
2569 59, 59, 59, 59, 59, 58, 115, 81, 87, 81, 102, 32, 32, 32, 32, 32, 32, 10,
2570 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2571 32, 32, 61, 58, 59, 61, 81, 81, 81, 81, 81, 81, 87, 87, 81, 81, 81, 81,
2572 81, 58, 59, 59, 59, 59, 59, 59, 59, 59, 58, 45, 45, 45, 59, 59, 59, 41,
2573 87, 66, 33, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2574 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59, 93, 81,
2575 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58,
2576 45, 32, 46, 32, 32, 32, 32, 32, 46, 32, 126, 96, 32, 32, 32, 32, 32, 32,
2577 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2578 32, 32, 32, 32, 32, 32, 58, 61, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2579 81, 81, 81, 81, 81, 40, 58, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32,
2580 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2581 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58,
2582 59, 59, 58, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 40, 58,
2583 59, 59, 59, 46, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2584 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2585 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59, 60, 81, 81, 81, 81,
2586 81, 81, 81, 81, 81, 81, 81, 81, 81, 59, 61, 59, 59, 61, 32, 32, 32, 32,
2587 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2588 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2589 32, 32, 32, 58, 59, 59, 93, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2590 81, 81, 40, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2591 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32,
2592 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 106,
2593 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 76, 58, 59, 59, 59,
2594 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2595 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2596 32, 32, 32, 32, 32, 32, 32, 61, 58, 58, 81, 81, 81, 81, 81, 81, 81, 81,
2597 81, 81, 81, 81, 81, 87, 58, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32,
2598 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32,
2599 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2600 58, 59, 61, 41, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 87, 59,
2601 61, 58, 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2602 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2603 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 58, 61, 81, 81, 81,
2604 81, 81, 81, 81, 81, 81, 81, 81, 81, 107, 58, 59, 59, 59, 59, 58, 32, 32,
2605 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2606 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2607 32, 32, 32, 32, 58, 59, 59, 58, 51, 81, 81, 81, 81, 81, 81, 81, 81, 81,
2608 81, 102, 94, 59, 59, 59, 59, 59, 61, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2609 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32,
2610 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 58, 61, 59,
2611 59, 59, 43, 63, 36, 81, 81, 81, 87, 64, 86, 102, 58, 59, 59, 59, 59, 59,
2612 59, 59, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2613 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2614 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 59, 59, 43, 33,
2615 58, 126, 126, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32,
2616 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32,
2617 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46,
2618 61, 59, 59, 59, 58, 45, 58, 61, 59, 58, 58, 58, 61, 59, 59, 59, 59, 59,
2619 59, 59, 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32,
2620 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32,
2621 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 61, 59, 59, 59, 59, 59, 58, 95,
2622 32, 45, 61, 59, 61, 59, 59, 59, 59, 59, 59, 59, 45, 58, 59, 59, 59, 59,
2623 61, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2624 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2625 32, 32, 58, 61, 59, 59, 59, 59, 59, 61, 59, 61, 46, 46, 32, 45, 45, 45,
2626 59, 58, 45, 45, 46, 58, 59, 59, 59, 59, 59, 59, 61, 46, 32, 32, 32, 32,
2627 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32,
2628 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 58, 59, 59, 59, 59,
2629 59, 59, 59, 59, 59, 61, 59, 46, 32, 32, 46, 32, 46, 32, 58, 61, 59, 59,
2630 59, 59, 59, 59, 59, 59, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2631 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2632 32, 32, 32, 32, 32, 32, 32, 45, 59, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2633 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 58, 32,
2634 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10,
2635 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2636 46, 61, 59, 59, 59, 59, 59, 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 61,
2637 46, 61, 59, 59, 59, 59, 59, 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2638 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32,
2639 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59,
2640 59, 59, 32, 46, 32, 32, 32, 32, 32, 32, 32, 46, 61, 58, 59, 59, 59, 59,
2641 59, 58, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2642 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2643 32, 32, 32, 32, 58, 59, 59, 59, 59, 59, 59, 59, 59, 46, 46, 32, 32, 32,
2644 32, 32, 32, 32, 61, 59, 59, 59, 59, 59, 59, 59, 45, 32, 32, 32, 32, 32,
2645 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32,
2646 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 32, 45, 61,
2647 59, 59, 59, 59, 59, 58, 32, 46, 32, 32, 32, 32, 32, 32, 32, 58, 59, 59,
2648 59, 59, 59, 58, 45, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2649 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2650 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 45, 32, 46, 32,
2651 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 61, 59, 58, 45, 45, 32, 32, 32,
2652 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2653 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2654 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2655 32, 32, 46, 32, 32, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2656 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10
2657 };